├── 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 | https://www.packtpub.com/ 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 | --------------------------------------------------------------------------------