├── LICENSE
├── README.md
├── chapter1
├── _NOTES.md
└── example_compat.py
├── chapter12
├── _NOTES.md
├── primes.py
├── pytest_testing
│ ├── primes.py
│ └── test_primes.py
├── test_with_fake
│ ├── mailer.py
│ └── test_mailer.py
├── test_with_mock
│ ├── mailer.py
│ └── test_mailer.py
├── test_with_mock_patch
│ ├── mailer.py
│ └── test_mailer.py
└── unittest_testing
│ ├── primes.py
│ └── test_primes.py
├── chapter13
├── _NOTES.md
├── cprofile_decorator.py
├── cprofile_profiling.py
├── cyclic_references.py
└── graphing_backreferences.py
├── chapter14
├── _NOTES.md
├── memoize_decorator.py
└── memoize_lru_cache.py
├── chapter15
├── _NOTES.md
├── async_aiohttp.py
├── async_cooperative_wait.py
├── async_futures.py
├── async_print.py
├── asyncrates.py
├── multiprocessing_basics.py
├── multiprocessing_dummy.py
├── multiprocessing_forks.py
├── multiprocessing_pipes.py
├── multiprocessing_process_pool.py
├── multiprocessing_sharedctypes.py
├── synchronous.py
├── threads_exceptions_and_throttling.py
├── threads_one_per_item.py
├── threads_thread_pool.py
└── threads_two_way_queues.py
├── chapter16
├── _NOTES.md
├── subject_based_events.py
├── tkinter_gui.py
├── topic_based_events.py
└── web_application.py
├── chapter17
├── _NOTES.md
├── creational_borg.py
├── creational_singleton.py
├── interfaces_abc.py
├── interfaces_annotations.py
├── interfaces_zope.py
├── observer.py
└── structural_adapter.py
├── chapter2
├── Dockerfile
├── Dockerfile.alpine
├── Vagrantfile
├── _NOTES.md
├── docker-compose.networks-a.yml
├── docker-compose.networks-b.yml
├── docker-compose.services.yml
├── docker-compose.yml
└── pythonstartup
├── chapter3
├── _NOTES.md
├── context_manager_as_class.py
├── context_manager_as_function.py
├── decorators_repeat.py
├── decorators_repeat_with_metadata.py
├── enums_auto.py
├── enums_flags.py
├── enums_ints.py
├── enums_statuses.py
├── iterators_countdown.py
├── iterators_countdown_with_state.py
├── yield_chaining_generators.py
├── yield_fibonacci.py
└── yield_psychologist.py
├── chapter4
├── _NOTES.md
├── descriptors_init_on_access.py
├── descriptors_lazy_class_attribute.py
├── descriptors_revealing_access.py
├── distinctdict.py
├── folder.py
├── lists.py
├── pizza.py
├── properties_decorator.py
├── properties_explicit.py
├── vector.py
└── vector_as_dataclass.py
├── chapter5
├── _NOTES.md
├── hyllo.hy
├── instance_counting.py
├── metaclasses.py
├── nonzero.py
└── py_hyllo.py
├── chapter6
├── _NOTES.md
├── custom_container.py
├── name_mangling.py
├── options.py
├── private_attributes.py
└── private_variables.py
├── chapter7
├── _NOTES.md
├── example_with_readme_conversion
│ ├── README.md
│ ├── example_with_readme_conversion
│ │ └── __init__.py
│ └── setup.py
├── example_with_version
│ ├── example_with_version
│ │ └── __init__.py
│ └── setup.py
├── explicit_namespace_package
│ ├── acme
│ │ ├── __init__.py
│ │ └── templating
│ │ │ └── __init__.py
│ └── setup.py
└── implicit_namespace_package
│ ├── acme
│ └── templating
│ │ └── __init__.py
│ └── setup.py
├── chapter8
├── _NOTES.md
└── webxample-package
│ ├── MANIFEST.in
│ ├── README.md
│ ├── circus.ini
│ ├── fabfile.py
│ ├── fabutils.py
│ ├── setup.py
│ └── webxample
│ ├── __init__.py
│ ├── conf
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
│ ├── locale
│ ├── de
│ │ └── LC_MESSAGES
│ │ │ └── django.po
│ ├── en
│ │ └── LC_MESSAGES
│ │ │ └── django.po
│ └── pl
│ │ └── LC_MESSAGES
│ │ └── django.po
│ ├── manage.py
│ └── myapp
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ └── __init__.py
│ ├── models.py
│ ├── static
│ ├── js
│ │ └── myapp.js
│ └── sass
│ │ └── myapp.scss
│ ├── templates
│ ├── index.html
│ └── some_view.html
│ ├── tests.py
│ └── views.py
└── chapter9
├── _NOTES.md
├── cffi_qsort.py
├── ctypes_libc_printf.py
├── ctypes_qsort.py
├── fibonacci_c
├── fibonacci.c
├── fibonacci.egg-info
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ ├── dependency_links.txt
│ └── top_level.txt
└── setup.py
├── fibonacci_c_error_handling
├── fibonacci.c
└── setup.py
├── fibonacci_c_releasing_gil
├── fibonacci.c
└── setup.py
├── fibonacci_cython
├── fibonacci.pyx
└── setup.py
├── fibonacci_cython_nogil
├── fibonacci.pyx
└── setup.py
├── fibonacci_cythonize
├── fibonacci.py
└── setup.py
└── fibonacci_cythonize_optionally
├── fibonacci.py
└── setup.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Packt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Expert Python Programming - Third Edition
2 |
3 |
4 |
5 | This is the code repository for [Expert Python Programming - Third Edition](https://www.packtpub.com/application-development/expert-python-programming-third-edition?utm_source=github&utm_medium=repository&utm_campaign=9781789808896), published by Packt.
6 |
7 | **Become a master in Python by learning coding best practices and advanced programming concepts in Python 3.7**
8 |
9 | ## What is this book about?
10 | Python is a dynamic programming language that's used in a wide range of domains thanks to its simple yet powerful nature. Although writing Python code is easy, making it readable, reusable, and easy to maintain is challenging. Complete with best practices, useful tools, and standards implemented by professional Python developers, the third edition of Expert Python Programming will help you overcome this challenge.
11 |
12 | This book covers the following exciting features:
13 | * Explore modern ways of setting up repeatable and consistent development environments
14 | * Package Python code effectively for community and production use
15 | * Learn modern syntax elements of Python programming such as f-strings, enums, and lambda functions
16 | * Demystify metaprogramming in Python with metaclasses
17 | * Write concurrent code in Python
18 | * Extend Python with code written in different languages
19 | * Integrate Python with code written in different languages
20 |
21 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1789808898) today!
22 |
23 |
25 |
26 | ## Instructions and Navigations
27 | All of the code is organized into folders. For example, Chapter02.
28 |
29 | The code will look like the following:
30 | ```
31 | print("hello world")
32 | print "goodbye python2"
33 | ```
34 |
35 | **Following is what you need for this book:**
36 | This book will appeal to you if you’re a programmer looking to take your Python knowledge to the next level by writing efficient code and learning the latest features of version 3.7 and above.
37 |
38 | The required softwares are specified chapter wise in the book.
39 |
40 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://www.packtpub.com/sites/default/files/downloads/9781789808896_ColorImages.pdf).
41 |
42 | ## Errata
43 |
44 | * PDF page no: 306 Chapter 9
45 |
46 | Sample code like this:
47 |
48 | ```
49 | with nogil:
50 | result = fibonacci_cc(n)
51 |
52 | return fibonacci_cc(n)
53 | ```
54 |
55 | `return fibonacci_cc(n)` should be `return result`.
56 |
57 | ### Related products
58 | * Learn Python Programming - Second Edition [[Packt]](https://www.packtpub.com/application-development/learn-python-programming-second-edition?utm_source=github&utm_medium=repository&utm_campaign=9781788996662) [[Amazon]](https://www.amazon.com/dp/1788996666)
59 |
60 | * Mastering Concurrency in Python [[Packt]](https://www.packtpub.com/application-development/mastering-concurrency-python?utm_source=github&utm_medium=repository&utm_campaign=9781789343052) [[Amazon]](https://www.amazon.com/dp/B07GYLYNCR)
61 |
62 | ## Get to Know the Author
63 | **Michal Jaworski**
64 | has 10 years' of professional experience in Python. He has been in various
65 | roles at different companies, from an ordinary full-stack developer, through software architect, to VP of engineering in a fast-paced start-up company. He is currently a senior backend engineer at Showpad. He is highly experienced in designing high-performance distributed services. He is also an active contributor to many open source Python projects.
66 |
67 | **Tarek Ziade**
68 | is a Python developer located in the countryside near Dijon, France. He works at Mozilla in the services team. He founded a French Python user group called Afpy, and has written several books about Python in French and English. When he is not hacking on his computer or hanging out with his family, he's spending time between his two passions, running and playing the trumpet.
69 |
70 | ## Other books by the authors
71 | [Powershell Core 6.2 Cookbook](https://www.packtpub.com/networking-and-servers/powershell-core-62-cookbook?utm_source=github&utm_medium=repository&utm_campaign=9781789803303)
72 |
73 | [Python Microservices Development](https://www.packtpub.com/web-development/python-microservices-development?utm_source=github&utm_medium=repository&utm_campaign=9781785881114)
74 |
75 | ### Suggestions and Feedback
76 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions.
77 |
--------------------------------------------------------------------------------
/chapter1/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * "The popular tools and techniques used for maintaining cross-version compatibility"
4 | * `example_compat.py`
5 |
--------------------------------------------------------------------------------
/chapter1/example_compat.py:
--------------------------------------------------------------------------------
1 | """This module provides compatibility layer for
2 | selected things that have changed across Python
3 | versions.
4 | """
5 | import sys
6 |
7 | if sys.version_info < (3, 0, 0):
8 | import urlparse # noqa
9 |
10 |
11 | def is_string(s):
12 | """Return True if given value is considered string"""
13 | return isinstance(s, basestring)
14 |
15 | else:
16 | # note: urlparse was moved to urllib.parse in Python 3
17 | from urllib import parse as urlparse # noqa
18 |
19 |
20 | def is_string(s):
21 | """Return True if given value is considered string"""
22 | return isinstance(s, str)
--------------------------------------------------------------------------------
/chapter12/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Python standard test tools (unittest)
4 | * `unittest_testing/test_primes.py`
5 |
6 | * unittest alternatives (pytest)
7 | * `pytest_testing/test_primes.py`
8 |
9 | 3. Building a fake
10 | 3.1 ./test_with_fake/test_mailer.py
11 |
12 | 4. Using mocks
13 | 4.1 ./test_with_mock/test_mailer.py
14 | 4.2 ./test_with_mock_patch/test_mailer.py
15 |
--------------------------------------------------------------------------------
/chapter12/primes.py:
--------------------------------------------------------------------------------
1 | def is_prime(number):
2 | if number < 0 or number in (0, 1):
3 | return False
4 |
5 | for element in range(2, number):
6 | if number % element == 0:
7 | return False
8 |
9 | return True
10 |
--------------------------------------------------------------------------------
/chapter12/pytest_testing/primes.py:
--------------------------------------------------------------------------------
1 | def is_prime(number):
2 | if number < 0 or number in (0, 1):
3 | return False
4 |
5 | for element in range(2, number):
6 | if number % element == 0:
7 | return False
8 |
9 | return True
10 |
--------------------------------------------------------------------------------
/chapter12/pytest_testing/test_primes.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from primes import is_prime
4 |
5 |
6 | @pytest.fixture()
7 | def prime_numbers():
8 | return [3, 5, 7]
9 |
10 |
11 | @pytest.fixture()
12 | def non_prime_numbers():
13 | return [8, 0, 1]
14 |
15 |
16 | @pytest.fixture()
17 | def negative_numbers():
18 | return [-1, -3, -6]
19 |
20 |
21 | def test_is_prime_true(prime_numbers):
22 | for number in prime_numbers:
23 | assert is_prime(number)
24 |
25 |
26 | def test_is_prime_false(non_prime_numbers, negative_numbers):
27 | for number in non_prime_numbers:
28 | assert not is_prime(number)
29 |
30 | for number in negative_numbers:
31 | assert not is_prime(number)
32 |
--------------------------------------------------------------------------------
/chapter12/test_with_fake/mailer.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | import email.message
3 |
4 |
5 | def send(
6 | sender, to,
7 | subject='None',
8 | body='None',
9 | server='localhost'
10 | ):
11 | """sends a message."""
12 | message = email.message.Message()
13 | message['To'] = to
14 | message['From'] = sender
15 | message['Subject'] = subject
16 | message.set_payload(body)
17 |
18 | server = smtplib.SMTP(server)
19 | try:
20 | return server.sendmail(sender, to, message.as_string())
21 | finally:
22 | server.quit()
23 |
--------------------------------------------------------------------------------
/chapter12/test_with_fake/test_mailer.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | import pytest
3 | from mailer import send
4 |
5 |
6 | class FakeSMTP(object):
7 | def __init__(self, *args, **kw):
8 | # arguments are not important in our example
9 | pass
10 |
11 | def quit(self):
12 | pass
13 |
14 | def sendmail(self, *args, **kw):
15 | return {}
16 |
17 |
18 | @pytest.yield_fixture()
19 | def patch_smtplib():
20 | # setup step: monkey patch smtplib
21 | old_smtp = smtplib.SMTP
22 | smtplib.SMTP = FakeSMTP
23 |
24 | yield
25 |
26 | # teardown step: bring back smtplib to
27 | # its former state
28 | smtplib.SMTP = old_smtp
29 |
30 |
31 | def test_send(patch_smtplib):
32 | res = send(
33 | 'john.doe@example.com',
34 | 'john.doe@example.com',
35 | 'topic',
36 | 'body'
37 | )
38 | assert res == {}
39 |
--------------------------------------------------------------------------------
/chapter12/test_with_mock/mailer.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | import email.message
3 |
4 |
5 | def send(
6 | sender, to,
7 | subject='None',
8 | body='None',
9 | server='localhost'
10 | ):
11 | """sends a message."""
12 | message = email.message.Message()
13 | message['To'] = to
14 | message['From'] = sender
15 | message['Subject'] = subject
16 | message.set_payload(body)
17 |
18 | server = smtplib.SMTP(server)
19 | try:
20 | return server.sendmail(sender, to, message.as_string())
21 | finally:
22 | server.quit()
23 |
--------------------------------------------------------------------------------
/chapter12/test_with_mock/test_mailer.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | from unittest.mock import MagicMock
3 | from mailer import send
4 |
5 |
6 | def test_send(monkeypatch):
7 | smtp_mock = MagicMock()
8 | smtp_mock.sendmail.return_value = {}
9 |
10 | monkeypatch.setattr(smtplib, 'SMTP', MagicMock(return_value=smtp_mock))
11 |
12 | res = send(
13 | 'john.doe@example.com',
14 | 'john.doe@example.com',
15 | 'topic',
16 | 'body'
17 | )
18 | assert res == {}
19 |
--------------------------------------------------------------------------------
/chapter12/test_with_mock_patch/mailer.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | import email.message
3 |
4 |
5 | def send(
6 | sender, to,
7 | subject='None',
8 | body='None',
9 | server='localhost'
10 | ):
11 | """sends a message."""
12 | message = email.message.Message()
13 | message['To'] = to
14 | message['From'] = sender
15 | message['Subject'] = subject
16 | message.set_payload(body)
17 |
18 | server = smtplib.SMTP(server)
19 | try:
20 | return server.sendmail(sender, to, message.as_string())
21 | finally:
22 | server.quit()
23 |
--------------------------------------------------------------------------------
/chapter12/test_with_mock_patch/test_mailer.py:
--------------------------------------------------------------------------------
1 | from unittest.mock import patch
2 | from mailer import send
3 |
4 |
5 | def test_send():
6 | with patch('smtplib.SMTP') as mock:
7 | instance = mock.return_value
8 | instance.sendmail.return_value = {}
9 | res = send(
10 | 'john.doe@example.com',
11 | 'john.doe@example.com',
12 | 'topic',
13 | 'body'
14 | )
15 | assert res == {}
16 |
--------------------------------------------------------------------------------
/chapter12/unittest_testing/primes.py:
--------------------------------------------------------------------------------
1 | def is_prime(number):
2 | if number < 0 or number in (0, 1):
3 | return False
4 |
5 | for element in range(2, number):
6 | if number % element == 0:
7 | return False
8 |
9 | return True
10 |
--------------------------------------------------------------------------------
/chapter12/unittest_testing/test_primes.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from primes import is_prime
4 |
5 |
6 | class PrimesTests(unittest.TestCase):
7 | def test_is_prime(self):
8 | self.assertTrue(is_prime(5))
9 | self.assertTrue(is_prime(7))
10 |
11 | self.assertFalse(is_prime(8))
12 | self.assertFalse(is_prime(0))
13 | self.assertFalse(is_prime(1))
14 |
15 | self.assertFalse(is_prime(-1))
16 | self.assertFalse(is_prime(-3))
17 | self.assertFalse(is_prime(-6))
18 |
19 |
20 | if __name__ == "__main__":
21 | unittest.main()
22 |
--------------------------------------------------------------------------------
/chapter13/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Macro-profiling
4 | * `cprofile_profiling.py`
5 | * `cprofile_decorator.py`
6 |
7 | * Profiling memory
8 | * `graphing_backreferences.py`
9 | * `cyclic_references.py`
10 |
--------------------------------------------------------------------------------
/chapter13/cprofile_decorator.py:
--------------------------------------------------------------------------------
1 | import time
2 | import tempfile
3 | import cProfile
4 | import pstats
5 |
6 |
7 | def profile(column='time', list=3):
8 | def parametrized_decorator(function):
9 | def decorated(*args, **kw):
10 | s = tempfile.mktemp()
11 |
12 | profiler = cProfile.Profile()
13 | profiler.runcall(function, *args, **kw)
14 | profiler.dump_stats(s)
15 |
16 | p = pstats.Stats(s)
17 | print("=" * 5, f"{function.__name__}() profile", "=" * 5)
18 | p.sort_stats(column).print_stats(list)
19 | return decorated
20 |
21 | return parametrized_decorator
22 |
23 |
24 | def medium():
25 | time.sleep(0.01)
26 |
27 |
28 | @profile('time')
29 | def heavy():
30 | for i in range(100):
31 | medium()
32 | medium()
33 | time.sleep(2)
34 |
35 |
36 | @profile('time')
37 | def main():
38 | for i in range(2):
39 | heavy()
40 |
41 |
42 | if __name__ == '__main__':
43 | main()
44 |
45 |
46 | """
47 | $ python3 cprofile_decorator.py
48 | ===== heavy() profile =====
49 | Wed Apr 10 03:08:18 2019 /var/folders/jy/wy13kx0s7sb1dx2rfsqdvzdw0000gq/T/tmpwh6lb0y1
50 |
51 | 603 function calls in 4.462 seconds
52 |
53 | Ordered by: internal time
54 |
55 | ncalls tottime percall cumtime percall filename:lineno(function)
56 | 301 4.456 0.015 4.456 0.015 {built-in method time.sleep}
57 | 200 0.002 0.000 2.327 0.012 cprofile_decorator.py:25(medium)
58 | 1 0.002 0.002 4.462 4.462 cprofile_decorator.py:33(heavy)
59 | 100 0.001 0.000 0.129 0.001 cprofile_decorator.py:29(light)
60 | 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
61 |
62 |
63 | ===== heavy() profile =====
64 | Wed Apr 10 03:08:23 2019 /var/folders/jy/wy13kx0s7sb1dx2rfsqdvzdw0000gq/T/tmpsp6h0mb5
65 |
66 | 603 function calls in 4.430 seconds
67 |
68 | Ordered by: internal time
69 |
70 | ncalls tottime percall cumtime percall filename:lineno(function)
71 | 301 4.425 0.015 4.425 0.015 {built-in method time.sleep}
72 | 1 0.002 0.002 4.430 4.430 cprofile_decorator.py:33(heavy)
73 | 200 0.002 0.000 2.295 0.011 cprofile_decorator.py:25(medium)
74 | 100 0.001 0.000 0.129 0.001 cprofile_decorator.py:29(light)
75 | 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
76 |
77 |
78 | ===== main() profile =====
79 | Wed Apr 10 03:08:23 2019 /var/folders/jy/wy13kx0s7sb1dx2rfsqdvzdw0000gq/T/tmpenh1xtz7
80 |
81 | 64 function calls in 8.896 seconds
82 |
83 | Ordered by: internal time
84 | List reduced from 27 to 5 due to restriction <5>
85 |
86 | ncalls tottime percall cumtime percall filename:lineno(function)
87 | 1 8.896 8.896 8.896 8.896 {method 'enable' of '_lsprof.Profiler' objects}
88 | 1 0.000 0.000 0.000 0.000 {built-in method posix.lstat}
89 | 8 0.000 0.000 0.000 0.000 /usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/random.py:224(_randbelow)
90 | 1 0.000 0.000 8.896 8.896 cprofile_decorator.py:42(main)
91 | 8 0.000 0.000 0.000 0.000 /usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7/lib/python3.7/random.py:256(choice)
92 |
93 | """
94 |
--------------------------------------------------------------------------------
/chapter13/cprofile_profiling.py:
--------------------------------------------------------------------------------
1 | """
2 | "Macro-profiling" section example of invoking cProfile
3 | Python profiles from Python script
4 |
5 | """
6 | import time
7 | import cProfile
8 |
9 |
10 | def medium():
11 | time.sleep(0.01)
12 |
13 |
14 | def light():
15 | time.sleep(0.001)
16 |
17 |
18 | def heavy():
19 | for i in range(100):
20 | light()
21 | medium()
22 | medium()
23 | time.sleep(2)
24 |
25 |
26 | def main():
27 | for i in range(2):
28 | heavy()
29 |
30 |
31 | if __name__ == '__main__':
32 | profiler = cProfile.Profile()
33 | profiler.runcall(main)
34 | profiler.print_stats()
35 |
--------------------------------------------------------------------------------
/chapter13/cyclic_references.py:
--------------------------------------------------------------------------------
1 | """
2 | "Profiling memory" section example of how CPython
3 | deals with cyclic references in different versions
4 | of interpreter.
5 |
6 | """
7 | import gc
8 | import platform
9 |
10 | import objgraph
11 |
12 |
13 | class WithDel(list):
14 | """ list subclass with custom __del__ implementation """
15 | def __del__(self):
16 | pass
17 |
18 |
19 | def main():
20 | x = WithDel()
21 | y = []
22 | z = []
23 |
24 | x.append(y)
25 | y.append(z)
26 | z.append(x)
27 |
28 | del x, y, z
29 |
30 | print("unreachable prior collection: %s" % gc.collect())
31 | print("unreachable after collection: %s" % len(gc.garbage))
32 | print("WithDel objects count: %s" % objgraph.count('WithDel'))
33 |
34 |
35 | if __name__ == "__main__":
36 | print("Python version: %s" % platform.python_version())
37 | print()
38 | main()
39 |
--------------------------------------------------------------------------------
/chapter13/graphing_backreferences.py:
--------------------------------------------------------------------------------
1 | """
2 | "Profiling memory" section example of creating reference
3 | and backreference graphs of memory objects with objgraph.
4 |
5 | """
6 | from collections import Counter
7 | import objgraph
8 |
9 |
10 | def graph_references(*objects):
11 | objgraph.show_refs(
12 | objects,
13 | filename='show_refs.png',
14 | refcounts=True,
15 | # additional filtering for the sake of brevity
16 | too_many=5,
17 | filter=lambda x: not isinstance(x, dict),
18 | )
19 | objgraph.show_backrefs(
20 | objects,
21 | filename='show_backrefs.png',
22 | refcounts=True
23 | )
24 |
25 |
26 | if __name__ == "__main__":
27 | quote = """
28 | People who think they know everything are a
29 | great annoyance to those of us who do.
30 | """
31 | words = quote.lower().strip().split()
32 | counts = Counter(words)
33 | graph_references(words, quote, counts)
34 |
--------------------------------------------------------------------------------
/chapter14/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Deterministic caching
4 | * `memoize_decorator.py`
5 | * `memoize_lru_cache.py`
6 |
--------------------------------------------------------------------------------
/chapter14/memoize_decorator.py:
--------------------------------------------------------------------------------
1 | """
2 | "Deterministic caching" section example of caching
3 | approach with simple `memoize()` decorator function.
4 |
5 | """
6 |
7 |
8 | def memoize(function):
9 | """ Memoize the call to single-argument function
10 | """
11 | call_cache = {}
12 |
13 | def memoized(argument):
14 | try:
15 | return call_cache[argument]
16 | except KeyError:
17 | return call_cache.setdefault(argument, function(argument))
18 |
19 | return memoized
20 |
21 |
22 | @memoize
23 | def fibonacci(n):
24 | """ Return nth Fibonacci sequence number computed recursively
25 | """
26 | if n < 2:
27 | return 1
28 | else:
29 | return fibonacci(n - 1) + fibonacci(n - 2)
30 |
31 |
32 | if __name__ == "__main__":
33 | print([fibonacci(x) for x in range(30)])
34 |
--------------------------------------------------------------------------------
/chapter14/memoize_lru_cache.py:
--------------------------------------------------------------------------------
1 | """
2 | "Deterministic caching" section example of caching
3 | approach with simple `functools.lrs_cache` decorator.
4 |
5 | """
6 | from functools import lru_cache
7 |
8 |
9 | @lru_cache(None)
10 | def fibonacci(n):
11 | """ Return nth Fibonacci sequence number computed recursively
12 | """
13 | if n < 2:
14 | return 1
15 | else:
16 | return fibonacci(n - 1) + fibonacci(n - 2)
17 |
18 |
19 | @lru_cache(None)
20 | def two_arg(a, b):
21 | return a, b
22 |
23 | if __name__ == "__main__":
24 | print([fibonacci(x) for x in range(30)])
25 | print([two_arg(x, x+1) for x in range(30)])
26 |
--------------------------------------------------------------------------------
/chapter15/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Multithreading
4 | * `synchronous.py`
5 | * `threads_one_per_item.py`
6 | * `threads_thread_pool.py`
7 | * `threads_two_way_queues.py`
8 | * `threads_exceptions_and_throttling.py`
9 |
10 | * Multiprocessing
11 | * `multiprocessing_forks.py`
12 | * `multiprocessing_basics.py`
13 | * `multiprocessing_pipes.py`
14 | * `multiprocessing_process_pool.py`
15 |
16 | * Asynchronous programming
17 | * `async_print.py`
18 | * `async_cooperative_wait.py`
19 | * `async_aiohttp.py`
20 | * `async_futures.py`
21 |
--------------------------------------------------------------------------------
/chapter15/async_aiohttp.py:
--------------------------------------------------------------------------------
1 | """
2 | "Asynchronous programming" section example showing how
3 | to use aiohttp to perform asynchronous HTTP calls
4 |
5 | """
6 | import asyncio
7 | import time
8 |
9 | import aiohttp
10 |
11 | from asyncrates import get_rates
12 |
13 | SYMBOLS = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
14 | BASES = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
15 |
16 |
17 | async def fetch_rates(session, place):
18 | return await get_rates(session, place)
19 |
20 |
21 | async def present_result(result):
22 | base, rates = (await result)
23 |
24 | rates_line = ", ".join(
25 | [f"{rates[symbol]:7.03} {symbol}" for symbol in SYMBOLS]
26 | )
27 | print(f"1 {base} = {rates_line}")
28 |
29 |
30 | async def main():
31 | async with aiohttp.ClientSession() as session:
32 | await asyncio.wait([
33 | present_result(fetch_rates(session, base))
34 | for base in BASES
35 | ])
36 |
37 |
38 | if __name__ == "__main__":
39 | started = time.time()
40 | loop = asyncio.get_event_loop()
41 | loop.run_until_complete(main())
42 | elapsed = time.time() - started
43 |
44 | print()
45 | print("time elapsed: {:.2f}s".format(elapsed))
46 |
--------------------------------------------------------------------------------
/chapter15/async_cooperative_wait.py:
--------------------------------------------------------------------------------
1 | """
2 | "Asynchronous programming" section example showing how
3 | two coroutines cooperate by releasing control to event
4 | loop on blocking calls.
5 |
6 | """
7 | import random
8 | import asyncio
9 |
10 |
11 | async def waiter(name):
12 | for _ in range(4):
13 | time_to_sleep = random.randint(1, 3) / 4
14 | await asyncio.sleep(time_to_sleep)
15 | print(
16 | "{} waited {} seconds"
17 | "".format(name, time_to_sleep)
18 | )
19 |
20 |
21 | async def main():
22 | await asyncio.wait([waiter("foo"), waiter("bar")])
23 |
24 |
25 | if __name__ == "__main__":
26 | loop = asyncio.get_event_loop()
27 | loop.run_until_complete(main())
28 | loop.close()
29 |
--------------------------------------------------------------------------------
/chapter15/async_futures.py:
--------------------------------------------------------------------------------
1 | """
2 | "Asynchronous programming" section example showing
3 | how to employ `futures` and threading/multiprocessing
4 | to use non-async libraries in asyncio-based applications
5 |
6 | """
7 | import asyncio
8 | import time
9 |
10 | import requests
11 |
12 | SYMBOLS = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
13 | BASES = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
14 |
15 | THREAD_POOL_SIZE = 4
16 |
17 |
18 | async def fetch_rates(base):
19 | loop = asyncio.get_event_loop()
20 | response = await loop.run_in_executor(
21 | None, requests.get,
22 | f"https://api.exchangeratesapi.io/latest?base={base}"
23 | )
24 | response.raise_for_status()
25 | rates = response.json()["rates"]
26 | # note: same currency exchanges to itself 1:1
27 | rates[base] = 1.
28 | return base, rates
29 |
30 |
31 | async def present_result(result):
32 | base, rates = (await result)
33 |
34 | rates_line = ", ".join(
35 | [f"{rates[symbol]:7.03} {symbol}" for symbol in SYMBOLS]
36 | )
37 | print(f"1 {base} = {rates_line}")
38 |
39 |
40 | async def main():
41 | await asyncio.wait([
42 | present_result(fetch_rates(base))
43 | for base in BASES
44 | ])
45 |
46 |
47 | if __name__ == "__main__":
48 | started = time.time()
49 | loop = asyncio.get_event_loop()
50 | loop.run_until_complete(main())
51 | elapsed = time.time() - started
52 |
53 | print()
54 | print("time elapsed: {:.2f}s".format(elapsed))
55 |
--------------------------------------------------------------------------------
/chapter15/async_print.py:
--------------------------------------------------------------------------------
1 | """
2 | "Asynchronous programming" section example showing simple
3 | asynchronous printing of numbers sequence.
4 |
5 | """
6 | import asyncio
7 |
8 |
9 | async def print_number(number):
10 | print(number)
11 |
12 |
13 | if __name__ == "__main__":
14 | loop = asyncio.get_event_loop()
15 |
16 | loop.run_until_complete(
17 | asyncio.wait([
18 | print_number(number)
19 | for number in range(10)
20 | ])
21 | )
22 |
--------------------------------------------------------------------------------
/chapter15/asyncrates.py:
--------------------------------------------------------------------------------
1 | import aiohttp
2 |
3 |
4 | async def get_rates(session: aiohttp.ClientSession, base: str):
5 | async with session.get(
6 | f"https://api.exchangeratesapi.io/latest?base={base}"
7 | ) as response:
8 | rates = (await response.json())['rates']
9 | rates[base] = 1.
10 |
11 | return base, rates
12 |
--------------------------------------------------------------------------------
/chapter15/multiprocessing_basics.py:
--------------------------------------------------------------------------------
1 | """
2 | "Multiprocessing" section example showing how
3 | to create new processes with `multiprocessing` module
4 |
5 | """
6 | from multiprocessing import Process
7 | import os
8 |
9 |
10 | def work(identifier):
11 | print(
12 | 'hey, i am a process {}, pid: {}'
13 | ''.format(identifier, os.getpid())
14 | )
15 |
16 |
17 | def main():
18 | processes = [
19 | Process(target=work, args=(number,))
20 | for number in range(5)
21 | ]
22 | for process in processes:
23 | process.start()
24 |
25 | while processes:
26 | processes.pop().join()
27 |
28 |
29 | if __name__ == "__main__":
30 | main()
31 |
--------------------------------------------------------------------------------
/chapter15/multiprocessing_dummy.py:
--------------------------------------------------------------------------------
1 | """
2 | "Multiprocessing" section example showing how
3 | to `multiprocessing.dummy` can be used as an
4 | abstraction layer over threads.
5 |
6 | """
7 | from multiprocessing import Pool as ProcessPool
8 | from multiprocessing.dummy import Pool as ThreadPool
9 |
10 | from gmaps import Geocoding
11 |
12 | api = Geocoding()
13 |
14 |
15 | PLACES = (
16 | 'Reykjavik', 'Vien', 'Zadar', 'Venice',
17 | 'Wrocław', 'Bolognia', 'Berlin', 'Słubice',
18 | 'New York', 'Dehli',
19 | )
20 |
21 | POOL_SIZE = 4
22 |
23 |
24 | def fetch_place(place):
25 | return api.geocode(place)[0]
26 |
27 |
28 | def present_result(geocoded):
29 | print("{:>25s}, {:6.2f}, {:6.2f}".format(
30 | geocoded['formatted_address'],
31 | geocoded['geometry']['location']['lat'],
32 | geocoded['geometry']['location']['lng'],
33 | ))
34 |
35 |
36 | def main(use_threads=False):
37 | if use_threads:
38 | pool_cls = ThreadPool
39 | else:
40 | pool_cls = ProcessPool
41 |
42 | with pool_cls(POOL_SIZE) as pool:
43 | results = pool.map(fetch_place, PLACES)
44 |
45 | for result in results:
46 | present_result(result)
47 |
48 |
49 | if __name__ == "__main__":
50 | main()
51 |
--------------------------------------------------------------------------------
/chapter15/multiprocessing_forks.py:
--------------------------------------------------------------------------------
1 | """
2 | "Multiprocessing" section example showing how
3 | to spawn new process using os.fork on POSIX
4 | systems.
5 |
6 | """
7 | import os
8 |
9 | pid_list = []
10 |
11 |
12 | def main():
13 | pid_list.append(os.getpid())
14 | child_pid = os.fork()
15 |
16 | if child_pid == 0:
17 | pid_list.append(os.getpid())
18 | print()
19 | print("CHLD: hey, I am the child process")
20 | print("CHLD: all the pids i know %s" % pid_list)
21 |
22 | else:
23 | pid_list.append(os.getpid())
24 | print()
25 | print("PRNT: hey, I am the parent")
26 | print("PRNT: the child is pid %d" % child_pid)
27 | print("PRNT: all the pids i know %s" % pid_list)
28 |
29 |
30 | if __name__ == "__main__":
31 | main()
32 |
--------------------------------------------------------------------------------
/chapter15/multiprocessing_pipes.py:
--------------------------------------------------------------------------------
1 | """
2 | "Multiprocessing" section example showing how
3 | to use pipes from `multiprocessing` module
4 | as communication channel.
5 |
6 | """
7 | from multiprocessing import Process, Pipe
8 |
9 |
10 | class CustomClass:
11 | pass
12 |
13 |
14 | def work(connection):
15 | while True:
16 | instance = connection.recv()
17 |
18 | if instance:
19 | print(
20 | "CHLD: recv: {}".format(instance)
21 | )
22 |
23 | else:
24 | return
25 |
26 |
27 | def main():
28 | parent_conn, child_conn = Pipe()
29 |
30 | child = Process(target=work, args=(child_conn,))
31 |
32 | for item in (
33 | 42,
34 | 'some string',
35 | {'one': 1},
36 | CustomClass(),
37 | None,
38 | ):
39 | print(
40 | "PRNT: send: {}".format(item)
41 | )
42 | parent_conn.send(item)
43 |
44 | child.start()
45 | child.join()
46 |
47 |
48 | if __name__ == "__main__":
49 | main()
50 |
--------------------------------------------------------------------------------
/chapter15/multiprocessing_process_pool.py:
--------------------------------------------------------------------------------
1 | import time
2 | from multiprocessing import Pool
3 |
4 | import requests
5 |
6 |
7 | SYMBOLS = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
8 | BASES = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
9 |
10 | POOL_SIZE = 4
11 |
12 |
13 | def fetch_rates(base):
14 | response = requests.get(
15 | f"https://api.exchangeratesapi.io/latest?base={base}"
16 | )
17 |
18 | response.raise_for_status()
19 | rates = response.json()["rates"]
20 | # note: same currency exchanges to itself 1:1
21 | rates[base] = 1.
22 | return base, rates
23 |
24 |
25 | def present_result(base, rates):
26 | rates_line = ", ".join(
27 | [f"{rates[symbol]:7.03} {symbol}" for symbol in SYMBOLS]
28 | )
29 | print(f"1 {base} = {rates_line}")
30 |
31 |
32 | def main():
33 | with Pool(POOL_SIZE) as pool:
34 | results = pool.map(fetch_rates, BASES)
35 |
36 | for result in results:
37 | present_result(*result)
38 |
39 |
40 | if __name__ == "__main__":
41 | started = time.time()
42 | main()
43 | elapsed = time.time() - started
44 |
45 | print()
46 | print("time elapsed: {:.2f}s".format(elapsed))
47 |
48 |
--------------------------------------------------------------------------------
/chapter15/multiprocessing_sharedctypes.py:
--------------------------------------------------------------------------------
1 | """
2 | "Multiprocessing" section example showing how
3 | to use sharedctypes submodule to share data
4 | between multiple processes.
5 |
6 | """
7 | from multiprocessing import Process, Value, Array
8 |
9 |
10 | def f(n, a):
11 | n.value = 3.1415927
12 | for i in range(len(a)):
13 | a[i] = -a[i]
14 |
15 | if __name__ == '__main__':
16 | num = Value('d', 0.0)
17 | arr = Array('i', range(10))
18 |
19 | p = Process(target=f, args=(num, arr))
20 | p.start()
21 | p.join()
22 |
23 | print(num.value)
24 | print(arr[:])
25 |
--------------------------------------------------------------------------------
/chapter15/synchronous.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | import requests
4 |
5 | SYMBOLS = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
6 | BASES = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
7 |
8 |
9 | def fetch_rates(base):
10 | response = requests.get(
11 | f"https://api.exchangeratesapi.io/latest?base={base}"
12 | )
13 |
14 | response.raise_for_status()
15 | rates = response.json()["rates"]
16 | # note: same currency exchanges to itself 1:1
17 | rates[base] = 1.
18 |
19 | rates_line = ", ".join(
20 | [f"{rates[symbol]:7.03} {symbol}" for symbol in SYMBOLS]
21 | )
22 | print(f"1 {base} = {rates_line}")
23 |
24 |
25 | def main():
26 | for base in BASES:
27 | fetch_rates(base)
28 |
29 |
30 | if __name__ == "__main__":
31 | started = time.time()
32 | main()
33 | elapsed = time.time() - started
34 |
35 | print()
36 | print("time elapsed: {:.2f}s".format(elapsed))
37 |
--------------------------------------------------------------------------------
/chapter15/threads_exceptions_and_throttling.py:
--------------------------------------------------------------------------------
1 | """
2 | "An example of a threaded application" section example
3 | showing how throttling / rate limiting can be implemented
4 | in multithreaded application
5 |
6 | """
7 | import random
8 | import time
9 | from queue import Queue, Empty
10 | from threading import Thread, Lock
11 |
12 | import requests
13 |
14 |
15 | SYMBOLS = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
16 | BASES = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
17 |
18 | THREAD_POOL_SIZE = 4
19 |
20 |
21 | class Throttle:
22 | def __init__(self, rate):
23 | self._consume_lock = Lock()
24 | self.rate = rate
25 | self.tokens = 0
26 | self.last = 0
27 |
28 | def consume(self, amount=1):
29 | with self._consume_lock:
30 | now = time.time()
31 |
32 | # time measument is initialized on first
33 | # token request to avoid initial bursts
34 | if self.last == 0:
35 | self.last = now
36 |
37 | elapsed = now - self.last
38 |
39 | # make sure that quant of passed time is big
40 | # enough to add new tokens
41 | if int(elapsed * self.rate):
42 | self.tokens += int(elapsed * self.rate)
43 | self.last = now
44 |
45 | # never over-fill the bucket
46 | self.tokens = (
47 | self.rate
48 | if self.tokens > self.rate
49 | else self.tokens
50 | )
51 |
52 | # finally dispatch tokens if available
53 | if self.tokens >= amount:
54 | self.tokens -= amount
55 | else:
56 | amount = 0
57 |
58 | return amount
59 |
60 |
61 | def fetch_rates(base):
62 | response = requests.get(
63 | f"https://api.exchangeratesapi.io/latest?base={base}"
64 | )
65 |
66 | if random.randint(0, 5) < 1:
67 | # simulate error by overriding status code
68 | response.status_code = 429
69 |
70 | response.raise_for_status()
71 | rates = response.json()["rates"]
72 | # note: same currency exchanges to itself 1:1
73 | rates[base] = 1.
74 | return base, rates
75 |
76 |
77 | def present_result(base, rates):
78 | rates_line = ", ".join(
79 | [f"{rates[symbol]:7.03} {symbol}" for symbol in SYMBOLS]
80 | )
81 | print(f"1 {base} = {rates_line}")
82 |
83 |
84 | def worker(work_queue, results_queue, throttle):
85 | while not work_queue.empty():
86 | try:
87 | item = work_queue.get(block=False)
88 | except Empty:
89 | break
90 | else:
91 |
92 | while not throttle.consume():
93 | pass
94 |
95 | try:
96 | result = fetch_rates(item)
97 | except Exception as err:
98 | results_queue.put(err)
99 | else:
100 | results_queue.put(result)
101 | finally:
102 | work_queue.task_done()
103 |
104 |
105 | def main():
106 | work_queue = Queue()
107 | results_queue = Queue()
108 | throttle = Throttle(10)
109 |
110 | for base in BASES:
111 | work_queue.put(base)
112 |
113 | threads = [
114 | Thread(target=worker, args=(work_queue, results_queue, throttle))
115 | for _ in range(THREAD_POOL_SIZE)
116 | ]
117 |
118 | for thread in threads:
119 | thread.start()
120 |
121 | work_queue.join()
122 |
123 | while threads:
124 | threads.pop().join()
125 |
126 | while not results_queue.empty():
127 | result = results_queue.get()
128 | if isinstance(result, Exception):
129 | raise result
130 |
131 | present_result(*result)
132 |
133 |
134 | if __name__ == "__main__":
135 | started = time.time()
136 | main()
137 | elapsed = time.time() - started
138 |
139 | print()
140 | print("time elapsed: {:.2f}s".format(elapsed))
141 |
--------------------------------------------------------------------------------
/chapter15/threads_one_per_item.py:
--------------------------------------------------------------------------------
1 | """
2 | "An example of a threaded application" section example
3 | showing how to use `threading` module in simplest
4 | one-thread-per-item fashion.
5 |
6 | """
7 | import time
8 | from threading import Thread
9 |
10 | import requests
11 |
12 | SYMBOLS = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
13 | BASES = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
14 |
15 |
16 | def fetch_rates(base):
17 | response = requests.get(
18 | f"https://api.exchangeratesapi.io/latest?base={base}"
19 | )
20 |
21 | response.raise_for_status()
22 | rates = response.json()["rates"]
23 | # note: same currency exchanges to itself 1:1
24 | rates[base] = 1.
25 |
26 | rates_line = ", ".join(
27 | [f"{rates[symbol]:7.03} {symbol}" for symbol in SYMBOLS]
28 | )
29 | print(f"1 {base} = {rates_line}")
30 |
31 |
32 | def main():
33 | threads = []
34 | for base in BASES:
35 | thread = Thread(target=fetch_rates, args=[base])
36 | thread.start()
37 | threads.append(thread)
38 |
39 | while threads:
40 | threads.pop().join()
41 |
42 |
43 | if __name__ == "__main__":
44 | started = time.time()
45 | main()
46 | elapsed = time.time() - started
47 |
48 | print()
49 | print("time elapsed: {:.2f}s".format(elapsed))
50 |
--------------------------------------------------------------------------------
/chapter15/threads_thread_pool.py:
--------------------------------------------------------------------------------
1 | """
2 | "An example of a threaded application" section example
3 | showing how to implement simple thread pool.
4 |
5 | """
6 | import time
7 | from queue import Queue, Empty
8 | from threading import Thread
9 |
10 | import requests
11 |
12 | THREAD_POOL_SIZE = 4
13 |
14 |
15 | SYMBOLS = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
16 | BASES = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
17 |
18 |
19 | def fetch_rates(base):
20 | response = requests.get(
21 | f"https://api.exchangeratesapi.io/latest?base={base}"
22 | )
23 |
24 | response.raise_for_status()
25 | rates = response.json()["rates"]
26 | # note: same currency exchanges to itself 1:1
27 | rates[base] = 1.
28 |
29 | rates_line = ", ".join(
30 | [f"{rates[symbol]:7.03} {symbol}" for symbol in SYMBOLS]
31 | )
32 | print(f"1 {base} = {rates_line}")
33 |
34 |
35 | def worker(work_queue):
36 | while not work_queue.empty():
37 | try:
38 | item = work_queue.get(block=False)
39 | except Empty:
40 | break
41 | else:
42 | fetch_rates(item)
43 | work_queue.task_done()
44 |
45 |
46 | def main():
47 | work_queue = Queue()
48 |
49 | for base in BASES:
50 | work_queue.put(base)
51 |
52 | threads = [
53 | Thread(target=worker, args=(work_queue,))
54 | for _ in range(THREAD_POOL_SIZE)
55 | ]
56 |
57 | for thread in threads:
58 | thread.start()
59 |
60 | work_queue.join()
61 |
62 | while threads:
63 | threads.pop().join()
64 |
65 |
66 | if __name__ == "__main__":
67 | started = time.time()
68 | main()
69 | elapsed = time.time() - started
70 |
71 | print()
72 | print("time elapsed: {:.2f}s".format(elapsed))
73 |
74 |
--------------------------------------------------------------------------------
/chapter15/threads_two_way_queues.py:
--------------------------------------------------------------------------------
1 | """
2 | "An example of a threaded application" section example
3 | showing how to use two-way queues as inter-thread
4 | communication method.
5 |
6 | """
7 | import time
8 | from queue import Queue, Empty
9 | from threading import Thread
10 |
11 | import requests
12 |
13 |
14 | SYMBOLS = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
15 | BASES = ('USD', 'EUR', 'PLN', 'NOK', 'CZK')
16 |
17 | THREAD_POOL_SIZE = 4
18 |
19 |
20 | def fetch_rates(base):
21 | response = requests.get(
22 | f"https://api.exchangeratesapi.io/latest?base={base}"
23 | )
24 |
25 | response.raise_for_status()
26 | rates = response.json()["rates"]
27 | # note: same currency exchanges to itself 1:1
28 | rates[base] = 1.
29 | return base, rates
30 |
31 |
32 | def present_result(base, rates):
33 | rates_line = ", ".join(
34 | [f"{rates[symbol]:7.03} {symbol}" for symbol in SYMBOLS]
35 | )
36 | print(f"1 {base} = {rates_line}")
37 |
38 |
39 | def worker(work_queue, results_queue):
40 | while not work_queue.empty():
41 | try:
42 | item = work_queue.get(block=False)
43 | except Empty:
44 | break
45 | else:
46 | results_queue.put(
47 | fetch_rates(item)
48 | )
49 | work_queue.task_done()
50 |
51 |
52 | def main():
53 | work_queue = Queue()
54 | results_queue = Queue()
55 |
56 | for base in BASES:
57 | work_queue.put(base)
58 |
59 | threads = [
60 | Thread(target=worker, args=(work_queue, results_queue))
61 | for _ in range(THREAD_POOL_SIZE)
62 | ]
63 |
64 | for thread in threads:
65 | thread.start()
66 |
67 | work_queue.join()
68 |
69 | while threads:
70 | threads.pop().join()
71 |
72 | while not results_queue.empty():
73 | present_result(*results_queue.get())
74 |
75 |
76 | if __name__ == "__main__":
77 | started = time.time()
78 | main()
79 | elapsed = time.time() - started
80 |
81 | print()
82 | print("time elapsed: {:.2f}s".format(elapsed))
83 |
--------------------------------------------------------------------------------
/chapter16/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Event-driven programming in GUIs
4 | * `tkinter_gui.py`
5 |
6 | * Event-driven communication
7 | * `web_application.py`
8 |
9 | * Subject-based style
10 | * `subject_based_style.py`
11 |
12 | * Topic-based style
13 | * `topic_signaling_style.p`
14 |
--------------------------------------------------------------------------------
/chapter16/subject_based_events.py:
--------------------------------------------------------------------------------
1 | import itertools
2 |
3 |
4 | class Subject:
5 | _new_id = itertools.count(1)
6 |
7 | def __init__(self):
8 | self._id = next(self._new_id)
9 | self._observers = []
10 |
11 | def register(self, observer):
12 | self._notify_observers(f"register({observer})")
13 | self._observers.append(observer)
14 |
15 | def _notify_observers(self, event):
16 | for observer in self._observers:
17 | observer.notify(self, event)
18 |
19 | def __str__(self):
20 | return f"<{self.__class__.__name__}: {self._id}>"
21 |
22 |
23 | class Observer:
24 | _new_id = itertools.count(1)
25 |
26 | def __init__(self):
27 | self._id = next(self._new_id)
28 |
29 | def notify(self, subject, event):
30 | print(f"{self}: received event '{event}' from {subject}")
31 |
32 | def __str__(self):
33 | return f"<{self.__class__.__name__}: {self._id}>"
34 |
35 |
--------------------------------------------------------------------------------
/chapter16/tkinter_gui.py:
--------------------------------------------------------------------------------
1 | import this
2 | from tkinter import *
3 | from tkinter import messagebox
4 |
5 | rot13 = str.maketrans(
6 | "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz",
7 | "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm"
8 | )
9 |
10 |
11 | def main_window(root):
12 | frame = Frame(root, width=100, height=100)
13 | zen_button = Button(root, text="Python Zen", command=show_zen)
14 | zen_button.pack()
15 |
16 |
17 | def show_zen():
18 | messagebox.showinfo(
19 | "Zen of Python",
20 | this.s.translate(rot13)
21 | )
22 |
23 |
24 | if __name__ == "__main__":
25 | root = Tk()
26 | main_window(root)
27 | root.mainloop()
--------------------------------------------------------------------------------
/chapter16/topic_based_events.py:
--------------------------------------------------------------------------------
1 | import itertools
2 |
3 | from blinker import signal
4 |
5 |
6 | class SelfWatch:
7 | _new_id = itertools.count(1)
8 |
9 | def __init__(self):
10 | self._id = next(self._new_id)
11 | init_signal = signal("SelfWatch.init")
12 | init_signal.send(self)
13 | init_signal.connect(self.receiver)
14 |
15 | def receiver(self, sender):
16 | print(f"{self}: received event from {sender}")
17 |
18 | def __str__(self):
19 | return f"<{self.__class__.__name__}: {self._id}>"
--------------------------------------------------------------------------------
/chapter16/web_application.py:
--------------------------------------------------------------------------------
1 | import this
2 |
3 | from flask import Flask
4 |
5 | app = Flask(__name__)
6 |
7 | rot13 = str.maketrans(
8 | "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz",
9 | "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm"
10 | )
11 |
12 |
13 | def simple_html(body):
14 | return f"""
15 |
16 |
17 |
18 |
19 | Book Example
20 |
21 |
22 | {body}
23 |
24 |
25 | """
26 |
27 |
28 | @app.route('/')
29 | def hello():
30 | return simple_html("Python Zen")
31 |
32 |
33 | @app.route('/zen')
34 | def zen():
35 | return simple_html(
36 | "
".join(this.s.translate(rot13).split("\n"))
37 | )
38 |
39 | if __name__ == '__main__':
40 | app.run()
--------------------------------------------------------------------------------
/chapter17/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Creational patterns
4 | * `creational_singleton.py`
5 | * `creational_borg.py`
6 |
7 | * Adapter
8 | * `structural_adapter.py`
9 |
10 | * Interfaces
11 | * `interfaces_zope.py`
12 | * `interfaces_abc.py`
13 | * `interfaces_annotations.py`
14 |
15 | * Observer
16 | * `observer.py`
17 |
--------------------------------------------------------------------------------
/chapter17/creational_borg.py:
--------------------------------------------------------------------------------
1 | """
2 | "Creational patterns" section example showing different
3 | ways to implement Borg in Python.
4 |
5 | """
6 | class Borg(object):
7 | _state = {}
8 | def __new__(cls, *args, **kwargs):
9 | ob = super().__new__(cls, *args, **kwargs)
10 | ob.__dict__ = cls._state
11 | return ob
12 |
--------------------------------------------------------------------------------
/chapter17/creational_singleton.py:
--------------------------------------------------------------------------------
1 | """
2 | "Creational patterns" section example showing different
3 | ways to implement Singleton in Python.
4 |
5 | """
6 | class Singleton(object):
7 | _instance = None
8 |
9 | def __new__(cls, *args, **kwargs):
10 | if cls._instance is None:
11 | cls._instance = super().__new__(cls, *args, **kwargs)
12 |
13 | return cls._instance
14 |
15 |
16 | class MetaSingleton(type):
17 | _instances = {}
18 |
19 | def __call__(cls, *args, **kwargs):
20 | if cls not in cls._instances:
21 | cls._instances[cls] = super().__call__(*args, **kwargs)
22 | return cls._instances[cls]
23 |
--------------------------------------------------------------------------------
/chapter17/interfaces_abc.py:
--------------------------------------------------------------------------------
1 | """
2 | "Interfaces" section example showing how implicit interfaces
3 | can be created and verified using Abstract Base Classes.
4 |
5 | """
6 | from abc import (
7 | ABCMeta,
8 | abstractmethod,
9 | abstractproperty
10 | )
11 |
12 |
13 | class IRectangle(metaclass=ABCMeta):
14 |
15 | @abstractproperty
16 | def width(self):
17 | return
18 |
19 | @abstractproperty
20 | def height(self):
21 | return
22 |
23 | @abstractmethod
24 | def area(self):
25 | """ Return rectangle area
26 | """
27 |
28 | @abstractmethod
29 | def perimeter(self):
30 | """ Return rectangle perimeter
31 | """
32 |
33 | @classmethod
34 | def __subclasshook__(cls, C):
35 | if cls is IRectangle:
36 | if all([
37 | any("area" in B.__dict__ for B in C.__mro__),
38 | any("perimeter" in B.__dict__ for B in C.__mro__),
39 | any("width" in B.__dict__ for B in C.__mro__),
40 | any("height" in B.__dict__ for B in C.__mro__),
41 | ]):
42 | return True
43 | return NotImplemented
44 |
45 |
46 | class Rectangle(IRectangle):
47 | def __init__(self, width, height):
48 | self._width = width
49 | self._height = height
50 |
51 | @property
52 | def width(self):
53 | return self._width
54 |
55 | @property
56 | def height(self):
57 | return self._height
58 |
59 | def area(self):
60 | return self.width * self.height
61 |
62 | def perimeter(self):
63 | return self.width * 2 + self.height * 2
64 |
65 |
66 | class ImplicitRectangle:
67 | def __init__(self, width, height):
68 | self._width = width
69 | self._height = height
70 |
71 | @property
72 | def width(self):
73 | return self._width
74 |
75 | @property
76 | def height(self):
77 | return self._height
78 |
79 | def area(self):
80 | return self.width * self.height
81 |
82 | def perimeter(self):
83 | return self.width * 2 + self.height * 2
84 |
85 |
86 | if __name__ == "__main__":
87 | rectangle = Rectangle(10, 10)
88 |
89 | print("Instance type:", type(rectangle))
90 | print("Instance MRO: ", rectangle.__class__.mro())
91 | print("isinstance(rectangle, IRectangle) =",
92 | isinstance(rectangle, IRectangle))
93 | print()
94 |
95 | rectangle = ImplicitRectangle(10, 10)
96 | print("Instance type:", type(rectangle))
97 | print("Instance MRO: ", rectangle.__class__.mro())
98 | print("isinstance(rectangle, IRectangle) =",
99 | isinstance(rectangle, IRectangle))
100 | print()
101 |
--------------------------------------------------------------------------------
/chapter17/interfaces_annotations.py:
--------------------------------------------------------------------------------
1 | """
2 | "Interfaces" section example showing how function annotations
3 | can be used to verify/check interfaces defines with Abstract
4 | Base Classes.
5 |
6 | """
7 | from abc import (
8 | ABCMeta,
9 | abstractmethod,
10 | abstractproperty
11 | )
12 |
13 |
14 | class IRectangle(metaclass=ABCMeta):
15 |
16 | @abstractproperty
17 | def width(self):
18 | return
19 |
20 | @abstractproperty
21 | def height(self):
22 | return
23 |
24 | @abstractmethod
25 | def area(self):
26 | """ Return rectangle area
27 | """
28 |
29 | @abstractmethod
30 | def perimeter(self):
31 | """ Return rectangle perimeter
32 | """
33 |
34 | @classmethod
35 | def __subclasshook__(cls, C):
36 | if cls is IRectangle:
37 | if all([
38 | any("area" in B.__dict__ for B in C.__mro__),
39 | any("perimeter" in B.__dict__ for B in C.__mro__),
40 | any("width" in B.__dict__ for B in C.__mro__),
41 | any("height" in B.__dict__ for B in C.__mro__),
42 | ]):
43 | return True
44 | return NotImplemented
45 |
46 |
47 | class Rectangle(IRectangle):
48 | def __init__(self, width, height):
49 | self._width = width
50 | self._height = height
51 |
52 | @property
53 | def width(self):
54 | return self._width
55 |
56 | @property
57 | def height(self):
58 | return self._height
59 |
60 | def area(self):
61 | return self.width * self.height
62 |
63 | def perimeter(self):
64 | return self.width * 2 + self.height * 2
65 |
66 |
67 | class ImplicitRectangle:
68 | def __init__(self, width, height):
69 | self._width = width
70 | self._height = height
71 |
72 | @property
73 | def width(self):
74 | return self._width
75 |
76 | @property
77 | def height(self):
78 | return self._height
79 |
80 | def area(self):
81 | return self.width * self.height
82 |
83 | def perimeter(self):
84 | return self.width * 2 + self.height * 2
85 |
86 | from functools import wraps
87 | import inspect
88 |
89 |
90 | def ensure_interface(function):
91 | signature = inspect.signature(function)
92 | parameters = signature.parameters
93 |
94 | @wraps(function)
95 | def wrapped(*args, **kwargs):
96 | bound = signature.bind(*args, **kwargs)
97 | for name, value in bound.arguments.items():
98 | annotation = parameters[name].annotation
99 |
100 | if not isinstance(annotation, ABCMeta):
101 | continue
102 |
103 | if not isinstance(value, annotation):
104 | raise TypeError(
105 | "{} does not implement {} interface"
106 | "".format(value, annotation)
107 |
108 | )
109 |
110 | function(*args, **kwargs)
111 |
112 | return wrapped
113 |
114 |
115 | @ensure_interface
116 | def draw_rectangle(rectangle: IRectangle):
117 | print(
118 | "{} x {} rectangle drawing"
119 | "".format(rectangle.width, rectangle.height)
120 | )
121 |
122 |
123 | def attempt_draw(instance):
124 | print("attempting draw of", instance)
125 | try:
126 | draw_rectangle(instance)
127 | except TypeError as error:
128 | print("draw failed because", error)
129 | else:
130 | print("draw succeeded")
131 |
132 |
133 | if __name__ == "__main__":
134 | for maybe_rectangle in (
135 | "foo",
136 | ImplicitRectangle(10, 12),
137 | Rectangle(10,5),
138 | (10, 23)
139 | ):
140 | attempt_draw(maybe_rectangle)
141 | print()
142 |
--------------------------------------------------------------------------------
/chapter17/interfaces_zope.py:
--------------------------------------------------------------------------------
1 | """
2 | "Interfaces" section example showing how interfaces
3 | can be implemented using `zope.interface` package.
4 |
5 | """
6 | from zope.interface import Interface, Attribute, implementer
7 |
8 |
9 | class IRectangle(Interface):
10 | width = Attribute("The width of rectangle")
11 | height = Attribute("The height of rectangle")
12 |
13 | def area():
14 | """ Return area of rectangle
15 | """
16 |
17 | def perimeter():
18 | """ Return perimeter of rectangle
19 | """
20 |
21 |
22 | @implementer(IRectangle)
23 | class Square:
24 | """ Concrete implementation of square with rectangle interface
25 | """
26 |
27 | def __init__(self, size):
28 | self.size = size
29 |
30 | @property
31 | def width(self):
32 | return self.size
33 |
34 | @property
35 | def height(self):
36 | return self.size
37 |
38 | def area(self):
39 | return self.size ** 2
40 |
41 | def perimeter(self):
42 | return 4 * self.size
43 |
44 |
45 | @implementer(IRectangle)
46 | class Rectangle:
47 | """ Concrete implementation of rectangle
48 | """
49 | def __init__(self, width, height):
50 | self.width = width
51 | self.height = height
52 |
53 | def area(self):
54 | return self.width * self.height
55 |
56 | def perimeter(self):
57 | return self.width * 2 + self.height * 2
58 |
--------------------------------------------------------------------------------
/chapter17/observer.py:
--------------------------------------------------------------------------------
1 | """
2 | "Observer" section example showing simple event subscription
3 | with observer pattern.
4 |
5 | """
6 | class Event:
7 | _observers = []
8 |
9 | def __init__(self, subject):
10 | self.subject = subject
11 |
12 | @classmethod
13 | def register(cls, observer):
14 | if observer not in cls._observers:
15 | cls._observers.append(observer)
16 |
17 | @classmethod
18 | def unregister(cls, observer):
19 | if observer in cls._observers:
20 | cls._observers.remove(observer)
21 |
22 | @classmethod
23 | def notify(cls, subject):
24 | event = cls(subject)
25 | for observer in cls._observers:
26 | observer(event)
27 |
28 |
29 | class WriteEvent(Event):
30 | def __repr__(self):
31 | return 'WriteEvent'
32 |
33 |
34 | def log(event):
35 | print(
36 | '{!r} was fired with subject "{}"'
37 | ''.format(event, event.subject)
38 | )
39 |
40 |
41 | class AnotherObserver(object):
42 | def __call__(self, event):
43 | print(
44 | "{!r} trigerred {}'s action"
45 | "".format(event, self.__class__.__name__)
46 | )
47 |
48 |
49 | WriteEvent.register(log)
50 | WriteEvent.register(AnotherObserver())
51 |
52 |
53 | if __name__ == "__main__":
54 | Event.notify("telephone rang")
55 |
--------------------------------------------------------------------------------
/chapter17/structural_adapter.py:
--------------------------------------------------------------------------------
1 | """
2 | "Adapters" section example showing how adapters can
3 | be implemented.
4 |
5 | """
6 | from os.path import split, splitext
7 |
8 |
9 | class DublinCoreAdapter(object):
10 | def __init__(self, filename):
11 | self._filename = filename
12 |
13 | @property
14 | def title(self):
15 | return splitext(split(self._filename)[-1])[0]
16 |
17 | @property
18 | def languages(self):
19 | return 'en',
20 |
21 | def __getitem__(self, item):
22 | return getattr(self, item, 'Unknown')
23 |
24 |
25 | class DublinCoreInfo(object):
26 | def summary(self, dc_dict):
27 | print('Title: %s' % dc_dict['title'])
28 | print('Creator: %s' % dc_dict['creator'])
29 | print('Languages: %s' % ', '.join(dc_dict['languages']))
30 |
31 |
32 | if __name__ == "__main__":
33 | file_name = 'example.txt'
34 |
35 | print(
36 | "Summary of '{}' adapted with DublinCoreAdapter"
37 | "".format(file_name)
38 | )
39 | DublinCoreInfo().summary(DublinCoreAdapter(file_name))
40 |
--------------------------------------------------------------------------------
/chapter2/Dockerfile:
--------------------------------------------------------------------------------
1 | # Let's define base image.
2 | # "python" is official Python image.
3 | # The "slim" versions are sensible starting
4 | # points for other lightweight Python-based images
5 | FROM python:3.7-slim
6 |
7 | # In order to keep image clean let's switch
8 | # to selected working directory. "/app/" is
9 | # commonly used for that purpose.
10 | WORKDIR /app/
11 |
12 | # These are our static files copied from
13 | # project source tree to the current working
14 | # directory.
15 | COPY static/ static/
16 |
17 | # We would run "python -m http.server" locally
18 | # so lets make it an entry point.
19 | ENTRYPOINT ["python3.7", "-m", "http.server"]
20 |
21 | # We want to serve files from static/ directory
22 | # on port 80 by default so set this as default arguments
23 | # of the built-in Python HTTP server
24 | CMD ["--directory", "static/", "80"]
25 |
--------------------------------------------------------------------------------
/chapter2/Dockerfile.alpine:
--------------------------------------------------------------------------------
1 | # Here we use bare alpine to illustrate
2 | # package management as it lacks Python
3 | # by default. For Python projects in general
4 | # the 'python:3.7-alpine' is probably better
5 | # choice.
6 | FROM alpine:3.7
7 |
8 | # Add python3 package as alpine image lacks it by default
9 | RUN apk add python3
10 |
11 | # Run multiple commands in single RUN instruction
12 | # so space can be reclaimed after the 'apk del py3-pip'
13 | # command because image layer is committed only after
14 | # whole whole instruction.
15 | RUN apk add py3-pip && \
16 | pip3 install django && \
17 | apk del py3-pip
18 |
19 | # (...)
--------------------------------------------------------------------------------
/chapter2/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | Vagrant.configure("2") do |config|
5 | # Every Vagrant development environment requires a box.
6 | # You can search for boxes at https://vagrantcloud.com/search.
7 | # Here we use Bionic version Ubuntu system for x64 architecture.
8 | config.vm.box = "ubuntu/bionic64"
9 |
10 | # Create a forwarded port mapping which allows access to a specific port
11 | # within the machine from a port on the host machine and only allow access
12 | # via 127.0.0.1 to disable public access
13 |
14 | config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
15 | config.vm.provider "virtualbox" do |vb|
16 | # Display the VirtualBox GUI when booting the machine
17 | vb.gui = false
18 | # Customize the amount of memory on the VM:
19 | vb.memory = "1024"
20 | end
21 | # Enable provisioning with a shell script.
22 | config.vm.provision "shell", inline: <<-SHELL
23 | apt-get update
24 | apt-get install python3.7 -y
25 | SHELL
26 | end
--------------------------------------------------------------------------------
/chapter2/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Virtual development environments using Vagrant
4 | * `Vagrantfile`
5 |
6 | * Writing your first Dockerfile
7 | * `Dockerfile`
8 |
9 | * Setting up complex environments
10 | * `docker-compose.yml`
11 |
12 | * Reducing size of containers
13 | * `Dockerfile.alpine`
14 |
15 | * Addressing services inside of Compose environment
16 | * `docker-compose.services.yml`
17 |
18 | * Communication between multiple Compose environments
19 | * `docker-compose.networks-a.yml`
20 | * `docker-compose.networks-b.yml`
--------------------------------------------------------------------------------
/chapter2/docker-compose.networks-a.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | networks:
4 | default:
5 | external:
6 | name: my-interservice-network
7 |
8 | services:
9 | webserver:
10 | build: .
11 | ports:
12 | - "80:80"
13 | tty: true
14 |
15 | environment:
16 | - DATABASE_HOSTNAME=database
17 | - DATABASE_PORT=5432
18 |
19 | database:
20 | image: postgres
21 | restart: always
--------------------------------------------------------------------------------
/chapter2/docker-compose.networks-b.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | networks:
4 | default:
5 | external:
6 | name: my-interservice-network
7 |
8 | services:
9 | other-service:
10 | build: .
11 | ports:
12 | - "80:80"
13 | tty: true
14 |
15 | environment:
16 | - DATABASE_HOSTNAME=database
17 | - DATABASE_PORT=5432
18 | - WEBSERVER_ADDRESS=http://webserver:80
--------------------------------------------------------------------------------
/chapter2/docker-compose.services.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | webserver:
5 | build: .
6 | ports:
7 | - "80:80"
8 | tty: true
9 | environment:
10 | - DATABASE_HOSTNAME=database
11 | - DATABASE_PORT=5432
12 |
13 | database:
14 | image: postgres
15 | restart: always
--------------------------------------------------------------------------------
/chapter2/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | webserver:
5 | # this tell Compose to build image from
6 | # local (.) directory
7 | build: .
8 |
9 | # this is equivalent to "-p" option of
10 | # the "docker build" command
11 | ports:
12 | - "80:80"
13 |
14 | # this is equivalent to "-t" option of
15 | # the "docker build" command
16 | tty: true
--------------------------------------------------------------------------------
/chapter2/pythonstartup:
--------------------------------------------------------------------------------
1 | # python startup file
2 | import readline
3 |
4 | import rlcompleter
5 | import atexit
6 | import os
7 |
8 | # tab completion
9 | readline.parse_and_bind('tab: complete')
10 |
11 | # history file
12 | histfile = os.path.join(os.environ['HOME'], '.pythonhistory')
13 |
14 | try:
15 | readline.read_history_file(histfile)
16 |
17 | except IOError:
18 | pass
19 |
20 | atexit.register(readline.write_history_file, histfile)
21 | del os, histfile, readline, rlcompleter
22 |
--------------------------------------------------------------------------------
/chapter3/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Symbolic enumeration with enum module
4 | * `enums_ints.py`
5 | * `enums_auto.py`
6 | * `enums_statuses.py`
7 | * `enums_flags.py`
8 |
9 | * Iterators
10 | * `iterators_countdown.py`
11 | * `iterators_countdown_with_state.py`
12 |
13 | * Generators and yield statement
14 | * `yield_fibonacci.py`
15 | * `yield_chaining_generators.py`
16 | * `yield_psychologist.py`
17 |
18 | * Decorators
19 | * `decorators_repeat.py`
20 | * `decorators_repeat_with_metadata.py`
21 |
22 | * Context managers - the with statement
23 | * `context_manager_as_class.py`
24 | * `context_manager_as_function.py`
--------------------------------------------------------------------------------
/chapter3/context_manager_as_class.py:
--------------------------------------------------------------------------------
1 | class ContextIllustration:
2 | def __enter__(self):
3 | print('entering context')
4 |
5 | def __exit__(self, exc_type, exc_value, traceback):
6 | print('leaving context')
7 | if exc_type is None:
8 | print('with no error')
9 | else:
10 | print(f'with an error ({exc_value})')
11 |
12 |
13 | if __name__ == "__main__":
14 | print("Process of running code within context manager WITHOUT error")
15 |
16 | with ContextIllustration():
17 | print(">> inside context")
18 | print()
19 |
20 | print("Process of running code within context manager WITH error")
21 | try:
22 | with ContextIllustration():
23 | print(">> inside context")
24 | raise RuntimeError("Exception inside context manager")
25 | except RuntimeError:
26 | pass
--------------------------------------------------------------------------------
/chapter3/context_manager_as_function.py:
--------------------------------------------------------------------------------
1 | from contextlib import contextmanager
2 |
3 |
4 | @contextmanager
5 | def context_illustration():
6 | print('entering context')
7 | try:
8 | yield
9 | except Exception as e:
10 | print('leaving context')
11 | print(f'with an error ({e})')
12 | # exception needs to be reraised
13 | raise
14 | else:
15 | print('leaving context')
16 | print('with no error')
17 |
18 |
19 | if __name__ == "__main__":
20 | print("Process of running code within context manager WITHOUT error")
21 |
22 | with context_illustration():
23 | print(">> inside context")
24 | print()
25 |
26 | print("Process of running code within context manager WITH error")
27 | try:
28 | with context_illustration():
29 | print(">> inside context")
30 | raise RuntimeError("Exception inside context manager")
31 | except RuntimeError:
32 | pass
--------------------------------------------------------------------------------
/chapter3/decorators_repeat.py:
--------------------------------------------------------------------------------
1 | def repeat(number=3):
2 | """Cause decorated function to be repeated a number of times.
3 |
4 | Last value of original function call is returned as a result.
5 |
6 | :param number: number of repetitions, 3 if not specified
7 | """
8 | def actual_decorator(function):
9 | def wrapper(*args, **kwargs):
10 | result = None
11 | for _ in range(number):
12 | result = function(*args, **kwargs)
13 | return result
14 |
15 | return wrapper
16 |
17 | return actual_decorator
18 |
19 |
20 | @repeat(5)
21 | def print_hello_world():
22 | """Simplest "hello world" implementation."""
23 | print("Hello, world!")
24 |
25 |
26 | if __name__ == "__main__":
27 | print(
28 | "Result of calling function print_hello_world() "
29 | "defined with repeat(5) decorator (without preserving "
30 | "metadata)"
31 | )
32 | print_hello_world()
33 | print()
34 |
35 | print("Name of decorated function:", print_hello_world.__name__)
36 | print("Docstring of that function:", print_hello_world.__doc__)
--------------------------------------------------------------------------------
/chapter3/decorators_repeat_with_metadata.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 |
3 |
4 | def repeat(number=3):
5 | """Cause decorated function to be repeated a number of times.
6 |
7 | Last value of original function call is returned as a result.
8 |
9 | :param number: number of repetitions, 3 if not specified
10 | """
11 | def actual_decorator(function):
12 | @wraps(function)
13 | def wrapper(*args, **kwargs):
14 | result = None
15 | for _ in range(number):
16 | result = function(*args, **kwargs)
17 | return result
18 |
19 | return wrapper
20 |
21 | return actual_decorator
22 |
23 |
24 | @repeat(5)
25 | def print_hello_world():
26 | """Simplest "hello world" implementation."""
27 | print("Hello, world!")
28 |
29 |
30 | if __name__ == "__main__":
31 | print(
32 | "Result of calling function print_hello_world() "
33 | "defined with repeat(5) decorator (without preserving "
34 | "metadata)"
35 | )
36 | print_hello_world()
37 | print()
38 |
39 | print("Name of decorated function:", print_hello_world.__name__)
40 | print("Docstring of that function:", print_hello_world.__doc__)
--------------------------------------------------------------------------------
/chapter3/enums_auto.py:
--------------------------------------------------------------------------------
1 | from enum import Enum, auto
2 |
3 |
4 | class Weekday(Enum):
5 | MONDAY = auto()
6 | TUESDAY = auto()
7 | WEDNESDAY = auto()
8 | THURSDAY = auto()
9 | FRIDAY = auto()
10 | SATURDAY = auto()
11 | SUNDAY = auto()
12 |
--------------------------------------------------------------------------------
/chapter3/enums_flags.py:
--------------------------------------------------------------------------------
1 | from enum import Flag, auto
2 |
3 |
4 | class Side(Flag):
5 | GUACAMOLE = auto()
6 | TORTILLA = auto()
7 | FRIES = auto()
8 | BEER = auto()
9 | POTATO_SALAD = auto()
10 |
--------------------------------------------------------------------------------
/chapter3/enums_ints.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class Weekday(Enum):
5 | MONDAY = 0
6 | TUESDAY = 1
7 | WEDNESDAY = 2
8 | THURSDAY = 3
9 | FRIDAY = 4
10 | SATURDAY = 5
11 | SUNDAY = 6
--------------------------------------------------------------------------------
/chapter3/enums_statuses.py:
--------------------------------------------------------------------------------
1 | from enum import Enum, auto
2 |
3 |
4 | class OrderStatus(Enum):
5 | PENDING = auto()
6 | PROCESSING = auto()
7 | PROCESSED = auto()
8 |
9 |
10 | class Order:
11 | def __init__(self):
12 | self.status = OrderStatus.PENDING
13 |
14 | def process(self):
15 | if self.status == OrderStatus.PROCESSED:
16 | raise RuntimeError(
17 | "Can't process order that has "
18 | "been already processed"
19 | )
20 |
21 | self.status = OrderStatus.PROCESSING
22 | ...
23 | self.status = OrderStatus.PROCESSED
24 |
--------------------------------------------------------------------------------
/chapter3/iterators_countdown.py:
--------------------------------------------------------------------------------
1 | from time import sleep
2 |
3 |
4 | class CountDown:
5 | def __init__(self, step):
6 | self.step = step
7 |
8 | def __next__(self):
9 | """Return the next element."""
10 | if self.step <= 0:
11 | raise StopIteration
12 | self.step -= 1
13 | return self.step
14 |
15 | def __iter__(self):
16 | """Return the iterator itself."""
17 | return self
18 |
19 |
20 | if __name__ == "__main__":
21 | print("Counting down:")
22 |
23 | for element in CountDown(10):
24 | print('*', element)
25 | sleep(0.2)
26 |
--------------------------------------------------------------------------------
/chapter3/iterators_countdown_with_state.py:
--------------------------------------------------------------------------------
1 | class CounterState:
2 | def __init__(self, step):
3 | self.step = step
4 |
5 | def __next__(self):
6 | """Move the counter step towards 0 by 1."""
7 | if self.step <= 0:
8 | raise StopIteration
9 | self.step -= 1
10 | return self.step
11 |
12 |
13 | class CountDown:
14 | def __init__(self, steps):
15 | self.steps = steps
16 |
17 | def __iter__(self):
18 | """Return iterable state"""
19 | return CounterState(self.steps)
20 |
21 |
22 | if __name__ == "__main__":
23 | print("Counting down:")
24 |
25 | for element in CountDown(10):
26 | print('*', element)
27 | sleep(0.2)
28 |
--------------------------------------------------------------------------------
/chapter3/yield_chaining_generators.py:
--------------------------------------------------------------------------------
1 | def capitalize(values):
2 | for value in values:
3 | yield value.upper()
4 |
5 |
6 | def hyphenate(values):
7 | for value in values:
8 | yield f"-{value}-"
9 |
10 |
11 | def leetspeak(values):
12 | for value in values:
13 | if value in {'t', 'T'}:
14 | yield '7'
15 | elif value in {'e', 'E'}:
16 | yield '3'
17 | else:
18 | yield value
19 |
20 |
21 | def join(values):
22 | return "".join(values)
23 |
24 |
25 | if __name__ == "__main__":
26 | text = "This is processed text"
27 |
28 | print("join(leetspeak(text)) result:")
29 | print(join(leetspeak(text)))
30 | print("join(hyphenate(text.split())) result:")
31 | print(join(hyphenate(text.split())))
32 |
--------------------------------------------------------------------------------
/chapter3/yield_fibonacci.py:
--------------------------------------------------------------------------------
1 | def fibonacci():
2 | a, b = 0, 1
3 |
4 | while True:
5 | yield b
6 | a, b = b, a + b
7 |
8 |
9 | if __name__:
10 | fib = fibonacci()
11 |
12 | print("Return type of fibonacci function:", type(fib))
13 | print(
14 | "First 10 fibonacci numbers (using next):",
15 | [next(fib) for _ in range(10)]
16 | )
17 | print(
18 | "Next 10 fibonacci numbers (continued):",
19 | [next(fib) for _ in range(10)]
20 | )
--------------------------------------------------------------------------------
/chapter3/yield_psychologist.py:
--------------------------------------------------------------------------------
1 | def psychologist():
2 | print('Please tell me your problems')
3 | while True:
4 | answer = (yield)
5 |
6 | if answer is not None:
7 | if answer.endswith('?'):
8 | print("Don't ask yourself too much questions")
9 |
10 | elif 'good' in answer:
11 | print("Ahh that's good, go on")
12 |
13 | elif 'bad' in answer:
14 | print("Don't be so negative")
15 |
16 | elif answer in ('q', 'quit'):
17 | print("Goodbye")
18 | yield
19 | return
20 |
21 | else:
22 | print("Please continue")
23 |
24 |
25 | if __name__ == "__main__":
26 | print("Starting psychologist session, type 'q' or 'quit' to end session")
27 |
28 | freud = psychologist()
29 |
30 | for phrase in freud:
31 | problem = input("> ")
32 | freud.send(problem)
--------------------------------------------------------------------------------
/chapter4/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Reducing boilerplate with data classes
4 | * `vector.py`
5 | * `vector_as_dataclass.py`
6 |
7 | * Subclassing built-in types:
8 | * `distinctdict.py`
9 | * `folder.py`
10 |
11 | * MRO and accessing methods from superclasses
12 | * `pizza.py`
13 |
14 | * Lists and tuples:
15 | * `lists.py`
16 |
17 | * Descriptors
18 | * `descriptors_revealing_access.py`
19 | * `descriptors_init_on_access.py`
20 | * `descriptors_lazy_class_attribute.py`
21 |
22 | * Properties
23 | * `properties_explicit.py`
24 | * `properties_decorator.py`
--------------------------------------------------------------------------------
/chapter4/descriptors_init_on_access.py:
--------------------------------------------------------------------------------
1 | class InitOnAccess:
2 | def __init__(self, klass, *args, **kwargs):
3 | self.klass = klass
4 | self.args = args
5 | self.kwargs = kwargs
6 | self._initialized = None
7 |
8 | def __get__(self, instance, owner):
9 | if self._initialized is None:
10 | print('initialized!')
11 | self._initialized = self.klass(*self.args, **self.kwargs)
12 | else:
13 | print('cached!')
14 | return self._initialized
15 |
16 |
17 | class MyClass:
18 | lazily_initialized = InitOnAccess(list, "argument")
19 |
20 |
21 | if __name__ == "__main__":
22 | instance = MyClass()
23 |
24 | print("First access to instance.lazily_initialized")
25 | print(">> instance.lazily_initialized =", instance.lazily_initialized, '\n')
26 |
27 | print("Next access to instance.lazily_initialized")
28 | print(">> instance.lazily_initialized =", instance.lazily_initialized, '\n')
--------------------------------------------------------------------------------
/chapter4/descriptors_lazy_class_attribute.py:
--------------------------------------------------------------------------------
1 | class lazy_class_attribute(object):
2 | def __init__(self, function):
3 | self.fget = function
4 |
5 | def __get__(self, obj, cls):
6 | value = self.fget(obj or cls)
7 | # note: storing in class object not its instance
8 | # no matter if its a class-level or
9 | # instance-level access
10 | setattr(cls, self.fget.__name__, value)
11 | return value
12 |
13 |
14 | class MyComplexClass:
15 | @lazy_class_attribute
16 | def evaluated_only_once(self):
17 | print("Evaluation of a method!")
18 | return sum(x ** 2 for x in range(200))
19 |
20 |
21 | if __name__ == "__main__":
22 | instance = MyComplexClass()
23 |
24 | print("First access to attribute at instance level")
25 | print("instance.evaluated_only_once =",
26 | instance.evaluated_only_once,
27 | '\n')
28 |
29 | print("Next access to attribute at instance level")
30 | print("instance.evaluated_only_once =",
31 | instance.evaluated_only_once,
32 | '\n')
33 |
34 | print("Access to attribute at class level")
35 | print("MyComplexClass.evaluated_only_once =",
36 | MyComplexClass.evaluated_only_once,
37 | '\n')
38 |
39 | print("Access to attribute from completely new instance")
40 | print("MyComplexClass().evaluated_only_once =",
41 | MyComplexClass().evaluated_only_once,
42 | '\n')
43 |
--------------------------------------------------------------------------------
/chapter4/descriptors_revealing_access.py:
--------------------------------------------------------------------------------
1 | class RevealAccess(object):
2 | """A data descriptor that sets and returns values
3 | normally and prints a message logging their access.
4 | """
5 |
6 | def __init__(self, initval=None, name='var'):
7 | self.val = initval
8 | self.name = name
9 |
10 | def __get__(self, obj, objtype):
11 | print('Retrieving', self.name)
12 | return self.val
13 |
14 | def __set__(self, obj, val):
15 | print('Updating', self.name)
16 | self.val = val
17 |
18 |
19 | class MyClass(object):
20 | x = RevealAccess(10, 'var "x"')
21 | y = 5
22 |
23 |
24 | if __name__ == "__main__":
25 | my_instance = MyClass()
26 |
27 | # set x attribute (will issue print)
28 | my_instance.x = 4
29 | # access x attribute (will issue print)
30 | assert my_instance.x == 4
31 |
32 | # set y attribute (will pass silently)
33 | my_instance.y = 2
34 | # access x attribute (will pass silently)
35 | assert my_instance.y == 2
--------------------------------------------------------------------------------
/chapter4/distinctdict.py:
--------------------------------------------------------------------------------
1 | from collections import UserDict
2 |
3 |
4 | class DistinctError(ValueError):
5 | """Raised when duplicate value is added to a distinctdict."""
6 |
7 |
8 | class distinctdict(UserDict):
9 | """Dictionary that does not accept duplicate values."""
10 |
11 | def __setitem__(self, key, value):
12 | if value in self.values():
13 | if (
14 | (key in self and self[key] != value) or
15 | key not in self
16 | ):
17 | raise DistinctError(
18 | "This value already exists for different key"
19 | )
20 |
21 | super().__setitem__(key, value)
22 |
23 |
24 | if __name__ == "__main__":
25 | names_to_numbers = {
26 | "one": 1,
27 | "two": 2,
28 | "uno": 1,
29 | }
30 |
31 | ddict = distinctdict()
32 | for key, value in names_to_numbers.items():
33 | try:
34 | ddict[key] = value
35 | except DistinctError:
36 | pass
37 |
38 | print("ordinary dictionary:", names_to_numbers)
39 | print("distinctdict dictionary:", ddict)
--------------------------------------------------------------------------------
/chapter4/folder.py:
--------------------------------------------------------------------------------
1 | from collections import UserList
2 |
3 |
4 | class Folder(UserList):
5 | def __init__(self, name):
6 | self.name = name
7 |
8 | def dir(self, nesting=0):
9 | offset = " " * nesting
10 | print('%s%s/' % (offset, self.name))
11 |
12 | for element in self:
13 | if hasattr(element, 'dir'):
14 | element.dir(nesting + 1)
15 | else:
16 | print("%s %s" % (offset, element))
17 |
--------------------------------------------------------------------------------
/chapter4/lists.py:
--------------------------------------------------------------------------------
1 | """
2 | "List and tuples" section examples of list comprehension usage
3 | """
4 |
5 |
6 | def evens_using_for_loop(count):
7 | """ Calculate evens using for loop """
8 | evens = []
9 | for i in range(count):
10 | if i % 2 == 0:
11 | evens.append(i)
12 | return evens
13 |
14 |
15 | def evens_using_list_comprehension(count):
16 | """ Calculate evens using list comprehension """
17 | return [i for i in range(count) if i % 2 == 0]
18 |
19 |
20 | def enumerate_elements(elements):
21 | for index, element in enumerate(elements):
22 | print(index, element)
23 |
24 |
25 | if __name__ == "__main__":
26 | print(
27 | "0-10 evens calculated in for loop:",
28 | evens_using_for_loop(11)
29 | )
30 | print()
31 |
32 | print(
33 | "0-10 evens calculated in list comprehension:",
34 | evens_using_list_comprehension(11)
35 | )
36 | print()
37 |
38 | print("0-10 evens enumerated:")
39 | enumerate_elements(evens_using_list_comprehension(11))
40 | print()
--------------------------------------------------------------------------------
/chapter4/pizza.py:
--------------------------------------------------------------------------------
1 | class Pizza:
2 | def __init__(self, toppings):
3 | self.toppings = toppings
4 |
5 | def __repr__(self):
6 | return "Pizza with " + " and ".join(self.toppings)
7 |
8 | @classmethod
9 | def recommend(cls):
10 | """Recommend some pizza with arbitrary toppings,"""
11 | return cls(['spam', 'ham', 'eggs'])
12 |
13 |
14 | class VikingPizza(Pizza):
15 | @classmethod
16 | def recommend(cls):
17 | """Use same recommendation as super but add extra spam"""
18 | recommended = super(VikingPizza).recommend()
19 | recommended.toppings += ['spam'] * 5
20 | return recommended
21 |
22 |
23 | if __name__ == "__main__":
24 | print("Ordinary pizza recomendation:", Pizza.recommend())
25 | print("Viking pizza recomendation:", VikingPizza.recommend())
--------------------------------------------------------------------------------
/chapter4/properties_decorator.py:
--------------------------------------------------------------------------------
1 | class Rectangle:
2 | def __init__(self, x1, y1, x2, y2):
3 | self.x1, self.y1 = x1, y1
4 | self.x2, self.y2 = x2, y2
5 |
6 | @property
7 | def width(self):
8 | """rectangle width measured from left"""
9 | return self.x2 - self.x1
10 |
11 | @width.setter
12 | def width(self, value):
13 | self.x2 = self.x1 + value
14 |
15 | @property
16 | def height(self):
17 | """rectangle height measured from top"""
18 | return self.y2 - self.y1
19 |
20 | @height.setter
21 | def height(self, value):
22 | self.y2 = self.y1 + value
23 |
24 |
25 | if __name__ == "__main__":
26 | rectangle = Rectangle(0, 0, 10, 10)
27 | print(
28 | f"At start we have {rectangle} with "
29 | f"size of {rectangle.width} x {rectangle.height}"
30 | )
31 |
32 | rectangle.width = 2
33 | rectangle.height = 8
34 | print(
35 | f"After resizing we have {rectangle} with "
36 | f"size of {rectangle.width} x {rectangle.height}"
37 | )
38 |
--------------------------------------------------------------------------------
/chapter4/properties_explicit.py:
--------------------------------------------------------------------------------
1 | class Rectangle:
2 | def __init__(self, x1, y1, x2, y2):
3 | self.x1, self.y1 = x1, y1
4 | self.x2, self.y2 = x2, y2
5 |
6 | def _width_get(self):
7 | return self.x2 - self.x1
8 |
9 | def _width_set(self, value):
10 | self.x2 = self.x1 + value
11 |
12 | def _height_get(self):
13 | return self.y2 - self.y1
14 |
15 | def _height_set(self, value):
16 | self.y2 = self.y1 + value
17 |
18 | width = property(
19 | _width_get, _width_set,
20 | doc="rectangle width measured from left"
21 | )
22 | height = property(
23 | _height_get, _height_set,
24 | doc="rectangle height measured from top"
25 | )
26 |
27 | def __repr__(self):
28 | return "{}({}, {}, {}, {})".format(
29 | self.__class__.__name__,
30 | self.x1, self.y1, self.x2, self.y2
31 | )
32 |
33 |
34 | if __name__ == "__main__":
35 | rectangle = Rectangle(0, 0, 10, 10)
36 | print(
37 | f"At start we have {rectangle} with "
38 | f"size of {rectangle.width} x {rectangle.height}"
39 | )
40 |
41 | rectangle.width = 2
42 | rectangle.height = 8
43 | print(
44 | f"After resizing we have {rectangle} with "
45 | f"size of {rectangle.width} x {rectangle.height}"
46 | )
--------------------------------------------------------------------------------
/chapter4/vector.py:
--------------------------------------------------------------------------------
1 | class Vector:
2 | def __init__(self, x, y):
3 | self.x = x
4 | self.y = y
5 |
6 | def __add__(self, other):
7 | """Add two vectors using + operator"""
8 | return Vector(
9 | self.x + other.x,
10 | self.y + other.y,
11 | )
12 |
13 | def __sub__(self, other):
14 | """Subtract two vectors using - operator"""
15 | return Vector(
16 | self.x - other.x,
17 | self.y - other.y,
18 | )
19 |
20 | def __repr__(self):
21 | """Return textual representation of vector"""
22 | return f""
23 |
24 | def __eq__(self, other):
25 | """Compare two vectors for equality"""
26 | return self.x == other.x and self.y == other.y
--------------------------------------------------------------------------------
/chapter4/vector_as_dataclass.py:
--------------------------------------------------------------------------------
1 | from dataclasses import dataclass
2 |
3 |
4 | @dataclass
5 | class Vector:
6 | x: int
7 | y: int
8 |
9 | def __add__(self, other):
10 | """Add two vectors using + operator"""
11 | return Vector(
12 | self.x + other.x,
13 | self.y + other.y,
14 | )
15 |
16 | def __sub__(self, other):
17 | """Subtract two vectors using - operator"""
18 | return Vector(
19 | self.x - other.x,
20 | self.y - other.y,
21 | )
22 |
23 |
24 | @dataclass(frozen=True)
25 | class FrozenVector:
26 | x: int
27 | y: int
--------------------------------------------------------------------------------
/chapter5/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Using the __new__() method to override instance creation process
4 | * `instance_counting.py`
5 | * `nonzero.py`
6 |
7 | * Metaclasses
8 | * `metaclasses.py`
9 |
10 | * Hy
11 | * `hyllo.hy`
12 | * `py_hyllo.py`
--------------------------------------------------------------------------------
/chapter5/hyllo.hy:
--------------------------------------------------------------------------------
1 | ;; "Hy" section example of defining function using Hy - Python
2 | ;; based dialect of lisp
3 | (defn hello [] (print "hello world!"))
--------------------------------------------------------------------------------
/chapter5/instance_counting.py:
--------------------------------------------------------------------------------
1 | from random import randint
2 |
3 |
4 | class InstanceCountingClass:
5 | instances_created = 0
6 |
7 | def __new__(cls, *args, **kwargs):
8 | print('__new__() called with:', cls, args, kwargs)
9 | instance = super().__new__(cls)
10 | instance.number = cls.instances_created
11 | cls.instances_created += 1
12 |
13 | return instance
14 |
15 | def __init__(self, attribute):
16 | print('__init__() called with:', self, attribute)
17 | self.attribute = attribute
18 |
19 |
20 | if __name__ == "__main__":
21 | print(
22 | "InstanceCountingClass.instances_created =",
23 | InstanceCountingClass.instances_created
24 | )
25 |
26 | desired_count = randint(2, 10)
27 | print(
28 | f"Creating {desired_count} instances of InstanceCountingClass..."
29 | )
30 |
31 | for number in range(desired_count):
32 | InstanceCountingClass(number)
33 |
34 | print(
35 | "InstanceCountingClass.instances_created =",
36 | InstanceCountingClass.instances_created
37 | )
--------------------------------------------------------------------------------
/chapter5/metaclasses.py:
--------------------------------------------------------------------------------
1 | print(" >>> defining RevealingMeta(type)")
2 |
3 |
4 | class RevealingMeta(type):
5 | def __new__(mcs, name, bases, namespace, **kwargs):
6 | print(mcs, "__new__ called")
7 | return super().__new__(mcs, name, bases, namespace)
8 |
9 | @classmethod
10 | def __prepare__(mcs, name, bases, **kwargs):
11 | print(mcs, "__prepare__ called")
12 | return super().__prepare__(name, bases, **kwargs)
13 |
14 | def __init__(cls, name, bases, namespace, **kwargs):
15 | print(cls, "__init__ called")
16 | super().__init__(name, bases, namespace)
17 |
18 | def __call__(cls, *args, **kwargs):
19 | print(cls, "__call__ called")
20 | return super().__call__(*args, **kwargs)
21 |
22 |
23 | print(" >>> defining RevealingClass(metaclass=RevealingMeta)")
24 |
25 |
26 | class RevealingClass(metaclass=RevealingMeta):
27 | def __new__(cls):
28 | print(cls, "__new__ called")
29 | return super().__new__(cls)
30 |
31 | def __init__(self):
32 | print(self, "__init__ called")
33 | super().__init__()
34 |
35 |
36 | if __name__ == "__main__":
37 | print(" >>> Creating RevealingClass()")
38 | instance = RevealingClass()
39 |
--------------------------------------------------------------------------------
/chapter5/nonzero.py:
--------------------------------------------------------------------------------
1 | class NonZero(int):
2 | def __new__(cls, value):
3 | return super().__new__(cls, value) if value != 0 else None
4 |
5 | def __init__(self, skipped_value):
6 | # implementation of __init__ could be skipped in this case
7 | # but it is left to present how it may be not called
8 | print("__init__() called")
9 | super().__init__()
10 |
11 |
12 | if __name__ == "__main__":
13 | print("NonZero(-12) =", NonZero(-12))
14 | print("NonZero(-3.123) =", NonZero(-3.123))
15 | print("NonZero(0) =", NonZero(0))
16 |
--------------------------------------------------------------------------------
/chapter5/py_hyllo.py:
--------------------------------------------------------------------------------
1 | import hy # this import statement enables Hy language import hooks
2 |
3 | import hyllo
4 |
5 |
6 | if __name__ == "__main__":
7 | hyllo.hello()
--------------------------------------------------------------------------------
/chapter6/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Constants
4 | * `options.py`
5 |
6 | * Properties:
7 | * `custom_container.py`
8 |
9 | * Public and private variables
10 | * `private_variables.py`
11 | * `private_attributes.py`
12 |
--------------------------------------------------------------------------------
/chapter6/custom_container.py:
--------------------------------------------------------------------------------
1 | from random import randint
2 |
3 |
4 | class Container:
5 | _contents = []
6 |
7 | def append(self, item):
8 | self._contents.append(item)
9 |
10 | @property
11 | def unique_items(self):
12 | return set(self._contents)
13 |
14 | @property
15 | def ordered_items(self):
16 | return list(self._contents)
17 |
18 |
19 | if __name__ == "__main__":
20 | container = Container()
21 |
22 | for _ in range(20):
23 | value = randint(0, 10)
24 | print(f"adding {value}")
25 | container.append(value)
26 |
27 | print(f"Ordered items: {container.ordered_items}")
28 | print(f"Unique items: {container.unique_items}")
29 |
--------------------------------------------------------------------------------
/chapter6/name_mangling.py:
--------------------------------------------------------------------------------
1 | class Base(object):
2 | def __secret(self):
3 | print("don't tell")
4 |
5 | def public(self):
6 | self.__secret()
7 |
8 |
9 | class Derived(Base):
10 | def __secret(self):
11 | print("never ever")
12 |
13 |
14 | if __name__ == "__main__":
15 |
16 | print("Base class members:", dir(Base))
17 | print("Derived class members:", dir(Derived))
18 |
19 | print("Base.public() result:")
20 | Base().public()
21 |
22 | print("Derived.public() result:")
23 | Derived().public()
24 |
--------------------------------------------------------------------------------
/chapter6/options.py:
--------------------------------------------------------------------------------
1 | OPTIONS = {}
2 |
3 |
4 | def register_option(name):
5 | return OPTIONS.setdefault(name, 1 << len(OPTIONS))
6 |
7 |
8 | def has_option(options, name):
9 | return bool(options & name)
10 |
11 |
12 | # now defining options
13 | BLUE = register_option('BLUE')
14 | RED = register_option('RED')
15 | WHITE = register_option('WHITE')
16 |
--------------------------------------------------------------------------------
/chapter6/private_attributes.py:
--------------------------------------------------------------------------------
1 | class Citizen(object):
2 | def __init__(self, first_name, last_name):
3 | self._first_name = first_name
4 | self._last_name = last_name
5 |
6 | @property
7 | def full_name(self):
8 | return f"{self._first_name} {self._last_name}"
9 |
10 |
11 | class UnforgivingElephant(object):
12 | def __init__(self, name):
13 | self.name = name
14 | self._people_to_stomp_on = []
15 |
16 | def get_slapped_by(self, name):
17 | self._people_to_stomp_on.append(name)
18 | print('Ouch!')
19 |
20 | def revenge(self):
21 | print('10 years later...')
22 | for person in self._people_to_stomp_on:
23 | print('%s stomps on %s' % (self.name, person))
24 |
--------------------------------------------------------------------------------
/chapter6/private_variables.py:
--------------------------------------------------------------------------------
1 | _observers = []
2 |
3 |
4 | def add_observer(observer):
5 | _observers.append(observer)
6 |
7 |
8 | def get_observers():
9 | """Makes sure _observers cannot be modified."""
10 | return tuple(_observers)
11 |
--------------------------------------------------------------------------------
/chapter7/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Common patterns
4 | * `example_with_version/setup.py`
5 | * `example_with_readme_conversion/setup.py`
6 |
7 | * Namespace packages
8 | * `implicit_namespace_package/setup.py`
9 | * `explicit_namespace_package/setup.py`
10 |
--------------------------------------------------------------------------------
/chapter7/example_with_readme_conversion/README.md:
--------------------------------------------------------------------------------
1 | # example README
2 |
3 | This is example of `README` file with package long description
4 | using Markdown text markup. This will be translated to reStructuredText
5 | on upload to PyPI.
6 |
--------------------------------------------------------------------------------
/chapter7/example_with_readme_conversion/example_with_readme_conversion/__init__.py:
--------------------------------------------------------------------------------
1 | # version as tuple for simple comparisons
2 | VERSION = (0, 0, 1)
3 | # string created from tuple to avoid inconsistency
4 | __version__ = ".".join([str(x) for x in VERSION])
5 |
--------------------------------------------------------------------------------
/chapter7/example_with_readme_conversion/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 | import os
3 |
4 | try:
5 | from pypandoc import convert
6 |
7 | def read_md(file_path):
8 | return convert(file_path, to='rst', format='asciidoc')
9 |
10 | except ImportError:
11 | convert = None
12 | print(
13 | "warning: pypandoc module not found, "
14 | "could not convert Asciidoc to RST"
15 | )
16 |
17 | def read_md(file_path):
18 | with open(file_path, 'r') as f:
19 | return f.read()
20 |
21 | README = os.path.join(os.path.dirname(__file__), 'README')
22 |
23 | setup(
24 | name='some-package',
25 | long_description=read_md(README),
26 | long_description_content_type='text/x-rst',
27 | packages=find_packages()
28 | )
29 |
--------------------------------------------------------------------------------
/chapter7/example_with_version/example_with_version/__init__.py:
--------------------------------------------------------------------------------
1 | # version as tuple for simple comparisons
2 | VERSION = (0, 0, 1)
3 | # string created from tuple to avoid inconsistency
4 | __version__ = ".".join([str(x) for x in VERSION])
5 |
--------------------------------------------------------------------------------
/chapter7/example_with_version/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 | import os
3 |
4 |
5 | def get_version(version_tuple):
6 | # additional handling of a,b,rc tags, this can
7 | # be simpler depending on your versioning scheme
8 | if not isinstance(version_tuple[-1], int):
9 | return '.'.join(
10 | map(str, version_tuple[:-1])
11 | ) + version_tuple[-1]
12 | return '.'.join(map(str, version_tuple))
13 |
14 | # path to the packages __init__ module in project
15 | # source tree
16 | init = os.path.join(
17 | os.path.dirname(__file__), 'src', 'some_package',
18 | '__init__.py'
19 | )
20 |
21 | version_line = list(
22 | filter(lambda l: l.startswith('VERSION'), open(init))
23 | )[0]
24 |
25 | # VERSION is a tuple so we need to eval 'version_line'.
26 | # We could simply import it from the package but we
27 | # cannot be sure that this package is importable before
28 | # installation is done.
29 | PKG_VERSION = get_version(eval(version_line.split('=')[-1]))
30 |
31 | setup(
32 | name='example_with_version',
33 | version=PKG_VERSION,
34 | long_description="""
35 | Example of automatic inclusion of package version from
36 | version specifier in package sources
37 | """,
38 | packages=find_packages()
39 | )
40 |
--------------------------------------------------------------------------------
/chapter7/explicit_namespace_package/acme/__init__.py:
--------------------------------------------------------------------------------
1 | __import__('pkg_resources').declare_namespace(__name__)
2 |
--------------------------------------------------------------------------------
/chapter7/explicit_namespace_package/acme/templating/__init__.py:
--------------------------------------------------------------------------------
1 | # version as tuple for simple comparisons
2 | VERSION = (0, 0, 1)
3 | # string created from tuple to avoid inconsistency
4 | __version__ = ".".join([str(x) for x in VERSION])
5 |
--------------------------------------------------------------------------------
/chapter7/explicit_namespace_package/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 |
4 | setup(
5 | name='acme.templating',
6 | packages=['acme.templating'],
7 | namespace_packages=['acme'],
8 | )
9 |
--------------------------------------------------------------------------------
/chapter7/implicit_namespace_package/acme/templating/__init__.py:
--------------------------------------------------------------------------------
1 | # version as tuple for simple comparisons
2 | VERSION = (0, 0, 1)
3 | # string created from tuple to avoid inconsistency
4 | __version__ = ".".join([str(x) for x in VERSION])
5 |
--------------------------------------------------------------------------------
/chapter7/implicit_namespace_package/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 |
4 | setup(
5 | name='acme.templating',
6 | packages=['acme.templating'],
7 | )
8 |
--------------------------------------------------------------------------------
/chapter8/_NOTES.md:
--------------------------------------------------------------------------------
1 | All scripts for Chapter 6 form one `webxample` package
2 | where different parts of example illustrate different
3 | topics (chapter sections)
4 |
5 | Map of example scripts to Chapter sections
6 |
7 | * Deployment automation using Fabric
8 | * `webxample-package/fabfile.py`
9 | * `webxample-package/fabutils.py`
10 |
11 | * Deployment using a package
12 | * `webxample-package/setup.py`
13 | * `webxample-package/webxample/*`
14 |
15 | * Using process supervision tools
16 | * `webxample-package/circus.ini`
17 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include webxample/myapp/templates *.html
2 | recursive-include webxample/myapp/static *.js *.css
3 | recursive-inlcude webxample/myapp/static *.css
4 | recursive-include webxample/locale *.po *.mo
5 |
6 | recursive-exclude * __pycache__
7 | recursive-exclude * *.py[co]
8 |
9 | include README.md
10 | include MANIFEST.in
11 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Expert-Python-Programming-Third-Edition/2c964acb2203b165c1990e1b9ebcae4a8f339f2f/chapter8/webxample-package/README.md
--------------------------------------------------------------------------------
/chapter8/webxample-package/circus.ini:
--------------------------------------------------------------------------------
1 | [watcher:webxample]
2 | cmd = /var/projects/webxample/.envs/webxample/bin/gunicorn webxample.conf.wsgi:application
3 | numprocesses = 1
4 |
5 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/fabfile.py:
--------------------------------------------------------------------------------
1 | from fabric import task
2 | from .fabutils import *
3 |
4 |
5 | @task
6 | def uptime(c):
7 | """
8 | Run uptime command on remote host - for testing connection.
9 | """
10 | c.run("uptime")
11 |
12 |
13 | @task
14 | def deploy(c):
15 | """ Deploy application with packaging in mind """
16 | version = get_version(c)
17 |
18 | pip_path = os.path.join(
19 | REMOTE_PROJECT_LOCATION, version, 'bin', 'pip'
20 | )
21 |
22 | if not c.run(f"test -d {REMOTE_PROJECT_LOCATION}", warn=True):
23 | # it may not exist for initial deployment on fresh host
24 | c.run(f"mkdir -p {REMOTE_PROJECT_LOCATION}")
25 |
26 | with c.cd(REMOTE_PROJECT_LOCATION):
27 | # create new virtual environment using venv
28 | c.run(f'python3 -m venv {version}')
29 |
30 | c.run(f"{pip_path} install webxample=={version} --index-url {PYPI_URL}")
31 |
32 |
33 | switch_versions(c, version)
34 | # let's assume that Circus is our process supervision tool
35 | # of choice.
36 | c.run('circusctl restart webxample')
--------------------------------------------------------------------------------
/chapter8/webxample-package/fabutils.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 |
4 | # Let's assume we have private package repository created
5 | # using 'devpi' project
6 | PYPI_URL = 'http://devpi.webxample.example.com'
7 |
8 | # This is arbitrary location for storing installed releases.
9 | # Each release is a separate virtual environment directory
10 | # which is named after project version. There is also a
11 | # symbolic link 'current' that points to recently deployed
12 | # version. This symlink is an actual path that will be used
13 | # for configuring the process supervision tool e.g.:
14 | # .
15 | # ├── 0.0.1
16 | # ├── 0.0.2
17 | # ├── 0.0.3
18 | # ├── 0.1.0
19 | # └── current -> 0.1.0/
20 |
21 | REMOTE_PROJECT_LOCATION = "/var/projects/webxample"
22 |
23 |
24 | def prepare_release(c):
25 | """ Prepare a new release by creating source distribution and
26 | uploading to out private package repository
27 | """
28 | c.local(f'python setup.py build sdist')
29 | c.local(f'twine upload --repository-url {PYPI_URL}')
30 |
31 |
32 | def get_version(c):
33 | """ Get current project version from setuptools """
34 | return c.local('python setup.py --version').stdout.strip()
35 |
36 |
37 | def switch_versions(c, version):
38 | """ Switch versions by replacing symlinks atomically """
39 | new_version_path = os.path.join(REMOTE_PROJECT_LOCATION, version)
40 | temporary = os.path.join(REMOTE_PROJECT_LOCATION, 'next')
41 | desired = os.path.join(REMOTE_PROJECT_LOCATION, 'current')
42 |
43 | # force symlink (-f) since probably there is a one already
44 | c.run(f"ln -fsT {new_version_path} {temporary}")
45 | # mv -T ensures atomicity of this operation
46 | c.run(f"mv -Tf {temporary} {desired}" )
47 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from setuptools import setup
4 | from setuptools import find_packages
5 | from distutils.cmd import Command
6 | from distutils.command.build import build as _build
7 |
8 | try:
9 | from django.core.management.commands.compilemessages \
10 | import Command as CompileCommand
11 | except ImportError:
12 | # note: during installation django may not be available
13 | CompileCommand = None
14 |
15 | # this environment is requires
16 | os.environ.setdefault(
17 | "DJANGO_SETTINGS_MODULE", "webxample.conf.settings"
18 | )
19 |
20 |
21 | class build_messages(Command):
22 | """ Custom command for building gettext messages in Django
23 | """
24 | description = """compile gettext messages"""
25 | user_options = []
26 |
27 | def initialize_options(self):
28 | pass
29 |
30 | def finalize_options(self):
31 |
32 | pass
33 |
34 | def run(self):
35 | if CompileCommand:
36 | CompileCommand().handle(
37 | verbosity=2, locales=[], exclude=[]
38 | )
39 | else:
40 | raise RuntimeError("could not build translations")
41 |
42 |
43 | class build(_build):
44 | """ Overriden build command that adds additional build steps
45 | """
46 | sub_commands = [
47 | ('build_messages', None),
48 | ('build_sass', None),
49 | ] + _build.sub_commands
50 |
51 |
52 | setup(
53 | name='webxample',
54 | setup_requires=[
55 | 'libsass == 0.6.0',
56 | 'django == 1.11.28',
57 | ],
58 | install_requires=[
59 | 'django == 1.11.28',
60 | 'gunicorn == 19.9.0',
61 | 'djangorestframework == 3.9.1',
62 | 'django-allauth == 0.24.1',
63 | ],
64 | packages=find_packages('.'),
65 | sass_manifests={
66 | 'webxample.myapp': ('static/sass', 'static/css')
67 | },
68 | cmdclass={
69 | 'build_messages': build_messages,
70 | 'build': build,
71 | },
72 | entry_points={
73 | 'console_scripts': {
74 | 'webxample = webxample.manage:main',
75 | }
76 | }
77 | )
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/conf/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Expert-Python-Programming-Third-Edition/2c964acb2203b165c1990e1b9ebcae4a8f339f2f/chapter8/webxample-package/webxample/conf/__init__.py
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/conf/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for webxample project.
3 |
4 | Generated by 'django-admin startproject' using Django 1.9.2.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.9/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.9/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = '6j#h#1hd5u3%zu4-a5pzc9^)a$88pn6**_q%nxc))9+89j-rtr'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = []
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 |
41 | 'webxample.myapp',
42 | 'rest_framework',
43 | 'allauth',
44 | ]
45 |
46 | MIDDLEWARE_CLASSES = [
47 | 'django.middleware.security.SecurityMiddleware',
48 | 'django.contrib.sessions.middleware.SessionMiddleware',
49 | 'django.middleware.common.CommonMiddleware',
50 | 'django.middleware.csrf.CsrfViewMiddleware',
51 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
52 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
53 | 'django.contrib.messages.middleware.MessageMiddleware',
54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
55 | ]
56 |
57 | ROOT_URLCONF = 'webxample.conf.urls'
58 |
59 | TEMPLATES = [
60 | {
61 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
62 | 'DIRS': [],
63 | 'APP_DIRS': True,
64 | 'OPTIONS': {
65 | 'context_processors': [
66 | 'django.template.context_processors.debug',
67 | 'django.template.context_processors.request',
68 | 'django.contrib.auth.context_processors.auth',
69 | 'django.contrib.messages.context_processors.messages',
70 | ],
71 | },
72 | },
73 | ]
74 |
75 | WSGI_APPLICATION = 'webxample.conf.wsgi.application'
76 |
77 |
78 | # Database
79 | # https://docs.djangoproject.com/en/1.9/ref/settings/#databases
80 |
81 | DATABASES = {
82 | 'default': {
83 | 'ENGINE': 'django.db.backends.sqlite3',
84 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
85 | }
86 | }
87 |
88 |
89 | # Password validation
90 | # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
91 |
92 | AUTH_PASSWORD_VALIDATORS = [
93 | {
94 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
95 | },
96 | {
97 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
98 | },
99 | {
100 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
101 | },
102 | {
103 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
104 | },
105 | ]
106 |
107 |
108 | # Internationalization
109 | # https://docs.djangoproject.com/en/1.9/topics/i18n/
110 |
111 | LANGUAGE_CODE = 'en-us'
112 |
113 | from django.utils.translation import ugettext_lazy as _
114 |
115 | from os.path import abspath, join, dirname
116 |
117 | PROJECT_ROOT = dirname(dirname(abspath(__file__)))
118 |
119 | LOCALE_PATHS = (
120 | join(PROJECT_ROOT, 'locale'),
121 | )
122 |
123 | LANGUAGES = [
124 | ('de', _('German')),
125 | ('en', _('English')),
126 | ('pl', _('Polish')),
127 | ]
128 |
129 | TIME_ZONE = 'UTC'
130 |
131 | USE_I18N = True
132 |
133 | USE_L10N = True
134 |
135 | USE_TZ = True
136 |
137 |
138 | # Static files (CSS, JavaScript, Images)
139 | # https://docs.djangoproject.com/en/1.9/howto/static-files/
140 |
141 | STATIC_URL = '/static/'
142 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/conf/urls.py:
--------------------------------------------------------------------------------
1 | """webxample URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.9/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.conf.urls import url, include
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
15 | """
16 | from django.conf.urls import url
17 | from django.contrib import admin
18 |
19 | urlpatterns = [
20 | url(r'^admin/', admin.site.urls),
21 | url(r'delay/', 'webxample.myapp.views.delay'),
22 | ]
23 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/conf/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for webxample project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webxample.conf.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/locale/de/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2016-02-13 15:27+0000\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
20 |
21 | #: myapp/models.py:7
22 | msgid "Hello!"
23 | msgstr ""
24 |
25 | #: myapp/models.py:8
26 | msgid "Bye!"
27 | msgstr ""
28 |
29 | #: myapp/models.py:9
30 | msgid "Thank you!"
31 | msgstr ""
32 |
33 | #: webxample/settings.py:124
34 | msgid "German"
35 | msgstr ""
36 |
37 | #: webxample/settings.py:125
38 | msgid "English"
39 | msgstr ""
40 |
41 | #: webxample/settings.py:126
42 | msgid "Polish"
43 | msgstr ""
44 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/locale/en/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2016-02-13 15:27+0000\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #: myapp/models.py:7
21 | msgid "Hello!"
22 | msgstr ""
23 |
24 | #: myapp/models.py:8
25 | msgid "Bye!"
26 | msgstr ""
27 |
28 | #: myapp/models.py:9
29 | msgid "Thank you!"
30 | msgstr ""
31 |
32 | #: webxample/settings.py:124
33 | msgid "German"
34 | msgstr ""
35 |
36 | #: webxample/settings.py:125
37 | msgid "English"
38 | msgstr ""
39 |
40 | #: webxample/settings.py:126
41 | msgid "Polish"
42 | msgstr ""
43 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/locale/pl/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2016-02-13 15:27+0000\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
20 | "|| n%100>=20) ? 1 : 2);\n"
21 |
22 | #: myapp/models.py:7
23 | msgid "Hello!"
24 | msgstr ""
25 |
26 | #: myapp/models.py:8
27 | msgid "Bye!"
28 | msgstr ""
29 |
30 | #: myapp/models.py:9
31 | msgid "Thank you!"
32 | msgstr ""
33 |
34 | #: webxample/settings.py:124
35 | msgid "German"
36 | msgstr ""
37 |
38 | #: webxample/settings.py:125
39 | msgid "English"
40 | msgstr ""
41 |
42 | #: webxample/settings.py:126
43 | msgid "Polish"
44 | msgstr ""
45 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import sys
4 |
5 |
6 | def main():
7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webxample.conf.settings")
8 |
9 | from django.core.management import execute_from_command_line
10 |
11 | execute_from_command_line(sys.argv)
12 |
13 |
14 | if __name__ == "__main__":
15 | main()
16 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Expert-Python-Programming-Third-Edition/2c964acb2203b165c1990e1b9ebcae4a8f339f2f/chapter8/webxample-package/webxample/myapp/__init__.py
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/apps.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 |
3 | from django.apps import AppConfig
4 |
5 |
6 | class MyappConfig(AppConfig):
7 | name = 'myapp'
8 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Expert-Python-Programming-Third-Edition/2c964acb2203b165c1990e1b9ebcae4a8f339f2f/chapter8/webxample-package/webxample/myapp/migrations/__init__.py
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/models.py:
--------------------------------------------------------------------------------
1 | from django.utils.translation import ugettext as _
2 | from django.db import models
3 |
4 | # Create your models here.
5 |
6 | MESSAGES = (
7 | _("Hello!"),
8 | _('Bye!'),
9 | _('Thank you!'),
10 | )
11 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/static/js/myapp.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Expert-Python-Programming-Third-Edition/2c964acb2203b165c1990e1b9ebcae4a8f339f2f/chapter8/webxample-package/webxample/myapp/static/js/myapp.js
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/static/sass/myapp.scss:
--------------------------------------------------------------------------------
1 | #foo {
2 | color: red;
3 | }
4 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/templates/some_view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/chapter8/webxample-package/webxample/myapp/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 |
4 | # Create your views here.
5 | def delay(request):
6 | import time; time.sleep(2)
7 | return "foo"
8 |
--------------------------------------------------------------------------------
/chapter9/_NOTES.md:
--------------------------------------------------------------------------------
1 | Map of example scripts to Chapter sections
2 |
3 | * Pure C extensions
4 | * `fibonacci_c/*`
5 | * `fibonacci_c_error_handling/*`
6 | * `fibonacci_c_releasing_gil/*`
7 |
8 | * Cython as a source to source compiler
9 | * `fibonacci_cythonize/*`
10 | * `fibonacci_cythonize_optionally/*`
11 |
12 | * Cython as a language
13 | * `fibonacci_cython/*`
14 | * `fibonacci_cython_nogil/*`
15 |
16 | * Calling C functions using ctypes
17 | * `ctypes_libc_printf.py`
18 | * `ctypes_qsort.py`
19 |
20 | * CFFI
21 | * `cffi_qsort.py`
22 |
--------------------------------------------------------------------------------
/chapter9/cffi_qsort.py:
--------------------------------------------------------------------------------
1 | from random import shuffle
2 |
3 | from cffi import FFI
4 |
5 | ffi = FFI()
6 |
7 | ffi.cdef("""
8 | void qsort(void *base, size_t nel, size_t width,
9 | int (*compar)(const void *, const void *));
10 | """)
11 | C = ffi.dlopen(None)
12 |
13 |
14 | @ffi.callback("int(void*, void*)")
15 | def cffi_int_compare(a, b):
16 | # Callback signature requires exact matching of types.
17 | # this involves less more maginc than in ctypes
18 | # but also makes you more specific and requires
19 | # explicit casting
20 | int_a = ffi.cast('int*', a)[0]
21 | int_b = ffi.cast('int*', b)[0]
22 | print(" %s cmp %s" % (int_a, int_b))
23 |
24 | # according to qsort specification this should return:
25 | # * less than zero if a < b
26 | # * zero if a == b
27 | # * more than zero if a > b
28 | return int_a - int_b
29 |
30 |
31 | def main():
32 | numbers = list(range(5))
33 | shuffle(numbers)
34 | print("shuffled: ", numbers)
35 |
36 | c_array = ffi.new("int[]", numbers)
37 |
38 | C.qsort(
39 | # pointer to the sorted array
40 | c_array,
41 | # length of the array
42 | len(c_array),
43 | # size of single array element
44 | ffi.sizeof('int'),
45 | # callback (pointer to the C comparison function)
46 | cffi_int_compare,
47 | )
48 | print("sorted: ", list(c_array))
49 |
50 | if __name__ == "__main__":
51 | main()
52 |
--------------------------------------------------------------------------------
/chapter9/ctypes_libc_printf.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 | from ctypes.util import find_library
3 |
4 | libc = ctypes.cdll.LoadLibrary(find_library('c'))
5 |
6 |
7 | if __name__ == "__main__":
8 | libc.printf(b"Hello world!\n")
9 |
--------------------------------------------------------------------------------
/chapter9/ctypes_qsort.py:
--------------------------------------------------------------------------------
1 | from random import shuffle
2 |
3 | import ctypes
4 | from ctypes.util import find_library
5 |
6 | libc = ctypes.cdll.LoadLibrary(find_library('c'))
7 |
8 | CMPFUNC = ctypes.CFUNCTYPE(
9 | # return type
10 | ctypes.c_int,
11 | # first argument type
12 | ctypes.POINTER(ctypes.c_int),
13 | # second argument type
14 | ctypes.POINTER(ctypes.c_int),
15 | )
16 |
17 |
18 | def ctypes_int_compare(a, b):
19 | # arguments are pointers so we access using [0] index
20 | print(" %s cmp %s" % (a[0], b[0]))
21 |
22 | # according to qsort specification this should return:
23 | # * less than zero if a < b
24 | # * zero if a == b
25 | # * more than zero if a > b
26 | return a[0] - b[0]
27 |
28 |
29 | def main():
30 | numbers = list(range(5))
31 | shuffle(numbers)
32 | print("shuffled: ", numbers)
33 |
34 | # create new type representing array with lenght
35 | # same as the lenght of numbers list
36 | NumbersArray = ctypes.c_int * len(numbers)
37 | # create new C array using a new type
38 | c_array = NumbersArray(*numbers)
39 |
40 | libc.qsort(
41 | # pointer to the sorted array
42 | c_array,
43 | # length of the array
44 | len(c_array),
45 | # size of single array element
46 | ctypes.sizeof(ctypes.c_int),
47 | # callback (pointer to the C comparison function)
48 | CMPFUNC(ctypes_int_compare)
49 | )
50 | print("sorted: ", list(c_array))
51 |
52 |
53 | if __name__ == "__main__":
54 | main()
55 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_c/fibonacci.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 |
4 | long long fibonacci(unsigned int n) {
5 | if (n < 2) {
6 | return 1;
7 | } else {
8 | return fibonacci(n-2) + fibonacci(n-1);
9 | }
10 | }
11 |
12 | static PyObject* fibonacci_py(PyObject* self, PyObject* args) {
13 | PyObject *result = NULL;
14 | long n;
15 | if (PyArg_ParseTuple(args, "l", &n)) {
16 | result = Py_BuildValue("L", fibonacci((unsigned int)n));
17 | }
18 | return result;
19 | }
20 |
21 |
22 | static char fibonacci_docs[] =
23 | "fibonacci(n): Return nth Fibonacci sequence number "
24 | "computed recursively\n";
25 |
26 |
27 | static PyMethodDef fibonacci_module_methods[] = {
28 | {"fibonacci", (PyCFunction)fibonacci_py,
29 | METH_VARARGS, fibonacci_docs},
30 | {NULL, NULL, 0, NULL}
31 | };
32 |
33 |
34 | static struct PyModuleDef fibonacci_module_definition = {
35 | PyModuleDef_HEAD_INIT,
36 | "fibonacci",
37 | "Extension module that provides fibonacci sequence function",
38 | -1,
39 | fibonacci_module_methods
40 | };
41 |
42 |
43 | PyMODINIT_FUNC PyInit_fibonacci(void) {
44 | Py_Initialize();
45 | return PyModule_Create(&fibonacci_module_definition);
46 | }
47 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_c/fibonacci.egg-info/PKG-INFO:
--------------------------------------------------------------------------------
1 | Metadata-Version: 1.0
2 | Name: fibonacci
3 | Version: 0.0.0
4 | Summary: UNKNOWN
5 | Home-page: UNKNOWN
6 | Author: UNKNOWN
7 | Author-email: UNKNOWN
8 | License: UNKNOWN
9 | Description: UNKNOWN
10 | Platform: UNKNOWN
11 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_c/fibonacci.egg-info/SOURCES.txt:
--------------------------------------------------------------------------------
1 | fibonacci.c
2 | setup.py
3 | fibonacci.egg-info/PKG-INFO
4 | fibonacci.egg-info/SOURCES.txt
5 | fibonacci.egg-info/dependency_links.txt
6 | fibonacci.egg-info/top_level.txt
--------------------------------------------------------------------------------
/chapter9/fibonacci_c/fibonacci.egg-info/dependency_links.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_c/fibonacci.egg-info/top_level.txt:
--------------------------------------------------------------------------------
1 | fibonacci
2 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_c/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, Extension
2 |
3 |
4 | setup(
5 | name='fibonacci',
6 | ext_modules=[
7 | Extension('fibonacci', ['fibonacci.c']),
8 | ]
9 | )
10 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_c_error_handling/fibonacci.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | long long fibonacci(unsigned int n) {
4 | if (n < 2) {
5 | return 1;
6 | } else {
7 | return fibonacci(n-2) + fibonacci(n-1);
8 | }
9 | }
10 |
11 |
12 | static PyObject* fibonacci_py(PyObject* self, PyObject* args) {
13 | PyObject *result = NULL;
14 | long n;
15 | long long fib;
16 |
17 | if (PyArg_ParseTuple(args, "l", &n)) {
18 | if (n<0) {
19 | PyErr_SetString(PyExc_ValueError,
20 | "n must not be less than 0");
21 | } else {
22 | result = Py_BuildValue("L", fibonacci((unsigned int)n));
23 | }
24 | }
25 | return result;
26 | }
27 |
28 |
29 | static char fibonacci_docs[] =
30 | "fibonacci(n): Return nth Fibonacci sequence number "
31 | "computed recursively\n";
32 |
33 |
34 | static PyMethodDef fibonacci_module_methods[] = {
35 | {"fibonacci", (PyCFunction)fibonacci_py,
36 | METH_VARARGS, fibonacci_docs},
37 | {NULL, NULL, 0, NULL}
38 | };
39 |
40 |
41 | static struct PyModuleDef fibonacci_module_definition = {
42 | PyModuleDef_HEAD_INIT,
43 | "fibonacci",
44 | "Extension module that provides fibonacci sequence function",
45 | -1,
46 | fibonacci_module_methods
47 | };
48 |
49 |
50 | PyMODINIT_FUNC PyInit_fibonacci(void) {
51 | Py_Initialize();
52 | return PyModule_Create(&fibonacci_module_definition);
53 | }
54 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_c_error_handling/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, Extension
2 |
3 |
4 | setup(
5 | name='fibonacci',
6 | ext_modules=[
7 | Extension('fibonacci', ['fibonacci.c']),
8 | ]
9 | )
10 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_c_releasing_gil/fibonacci.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | long long fibonacci(unsigned int n) {
4 | if (n < 2) {
5 | return 1;
6 | } else {
7 | return fibonacci(n-2) + fibonacci(n-1);
8 | }
9 | }
10 |
11 |
12 | static PyObject* fibonacci_py(PyObject* self, PyObject* args) {
13 | PyObject *result = NULL;
14 | long n;
15 | long long fib;
16 |
17 | if (PyArg_ParseTuple(args, "l", &n)) {
18 | if (n<0) {
19 | PyErr_SetString(PyExc_ValueError,
20 | "n must not be less than 0");
21 | } else {
22 | Py_BEGIN_ALLOW_THREADS;
23 | fib = fibonacci(n);
24 | Py_END_ALLOW_THREADS;
25 |
26 | result = Py_BuildValue("L", fib);
27 | }
28 | }
29 | return result;
30 | }
31 |
32 |
33 | static char fibonacci_docs[] =
34 | "fibonacci(n): Return nth Fibonacci sequence number "
35 | "computed recursively\n";
36 |
37 |
38 | static PyMethodDef fibonacci_module_methods[] = {
39 | {"fibonacci", (PyCFunction)fibonacci_py,
40 | METH_VARARGS, fibonacci_docs},
41 | {NULL, NULL, 0, NULL}
42 | };
43 |
44 |
45 | static struct PyModuleDef fibonacci_module_definition = {
46 | PyModuleDef_HEAD_INIT,
47 | "fibonacci",
48 | "Extension module that provides fibonacci sequence function",
49 | -1,
50 | fibonacci_module_methods
51 | };
52 |
53 |
54 | PyMODINIT_FUNC PyInit_fibonacci(void) {
55 | Py_Initialize();
56 | return PyModule_Create(&fibonacci_module_definition);
57 | }
58 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_c_releasing_gil/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, Extension
2 |
3 |
4 | setup(
5 | name='fibonacci',
6 | ext_modules=[
7 | Extension('fibonacci', ['fibonacci.c']),
8 | ]
9 | )
10 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_cython/fibonacci.pyx:
--------------------------------------------------------------------------------
1 | """Cython module that provides fibonacci sequence function"""
2 |
3 | cdef long long fibonacci_cc(unsigned int n):
4 | if n < 2:
5 | return n
6 | else:
7 | return fibonacci_cc(n - 1) + fibonacci_cc(n - 2)
8 |
9 |
10 | def fibonacci(unsigned int n):
11 | """ Return nth Fibonacci sequence number computed recursively
12 | """
13 | return fibonacci_cc(n)
14 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_cython/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | from Cython.Build import cythonize
3 |
4 |
5 | setup(
6 | name='fibonacci',
7 | ext_modules=cythonize(['fibonacci.pyx'])
8 | )
9 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_cython_nogil/fibonacci.pyx:
--------------------------------------------------------------------------------
1 | """Cython module that provides fibonacci sequence function"""
2 |
3 | cdef long long fibonacci_cc(unsigned int n) nogil:
4 | if n < 2:
5 | return n
6 | else:
7 | return fibonacci_cc(n - 1) + fibonacci_cc(n - 2)
8 |
9 |
10 | def fibonacci(unsigned int n):
11 | """ Return nth Fibonacci sequence number computed recursively
12 | """
13 | return fibonacci_cc(n)
14 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_cython_nogil/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | from Cython.Build import cythonize
3 |
4 |
5 | setup(
6 | name='fibonacci',
7 | ext_modules=cythonize(['fibonacci.pyx'])
8 | )
9 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_cythonize/fibonacci.py:
--------------------------------------------------------------------------------
1 | """Python module that provides fibonacci sequence function"""
2 |
3 |
4 | def fibonacci(n):
5 | """ Return nth Fibonacci sequence number computed recursively
6 | """
7 | if n < 2:
8 | return 1
9 | else:
10 | return fibonacci(n - 1) + fibonacci(n - 2)
11 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_cythonize/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | from Cython.Build import cythonize
3 |
4 |
5 | setup(
6 | name='fibonacci',
7 | ext_modules=cythonize(['fibonacci.py'])
8 | )
9 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_cythonize_optionally/fibonacci.py:
--------------------------------------------------------------------------------
1 | """Python module that provides fibonacci sequence function"""
2 |
3 |
4 | def fibonacci(n):
5 | """ Return nth Fibonacci sequence number computed recursively
6 | """
7 | if n < 2:
8 | return 1
9 | else:
10 | return fibonacci(n - 1) + fibonacci(n - 2)
11 |
--------------------------------------------------------------------------------
/chapter9/fibonacci_cythonize_optionally/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from distutils.core import setup
4 | from distutils.extension import Extension
5 |
6 | try:
7 | # cython source to source compilation available
8 | # only when Cython is available
9 | import Cython
10 | # and specific environment variable says
11 | # explicitely that Cython should be used
12 | # to generate C sources
13 | USE_CYTHON = bool(os.environ.get("USE_CYTHON"))
14 |
15 | except ImportError:
16 | USE_CYTHON = False
17 |
18 | ext = '.pyx' if USE_CYTHON else '.c'
19 |
20 | extensions = [Extension("fibonacci", ["fibonacci"+ext])]
21 |
22 | if USE_CYTHON:
23 | from Cython.Build import cythonize
24 | extensions = cythonize(extensions)
25 |
26 |
27 | setup(
28 | name='fibonacci',
29 | ext_modules=extensions,
30 | extras_require={
31 | # Cython will be set in that specific version
32 | # as a requirement if package will be intalled
33 | # with '[with-cython]' extra feature
34 | 'cython': ['cython==0.23.4']
35 | }
36 | )
37 |
--------------------------------------------------------------------------------