diff --git a/03-dict-set/index.py b/03-dict-set/index.py index 3eac1fb..f8641d2 100644 --- a/03-dict-set/index.py +++ b/03-dict-set/index.py @@ -8,7 +8,7 @@ import sys import re -WORD_RE = re.compile('\w+') +WORD_RE = re.compile(r'\w+') index = {} with open(sys.argv[1], encoding='utf-8') as fp: diff --git a/03-dict-set/index0.py b/03-dict-set/index0.py index e1fa28f..b41af0e 100644 --- a/03-dict-set/index0.py +++ b/03-dict-set/index0.py @@ -8,7 +8,7 @@ import sys import re -WORD_RE = re.compile('\w+') +WORD_RE = re.compile(r'\w+') index = {} with open(sys.argv[1], encoding='utf-8') as fp: diff --git a/03-dict-set/index_default.py b/03-dict-set/index_default.py index 521b2d5..8d3ae58 100644 --- a/03-dict-set/index_default.py +++ b/03-dict-set/index_default.py @@ -9,7 +9,7 @@ import re import collections -WORD_RE = re.compile('\w+') +WORD_RE = re.compile(r'\w+') index = collections.defaultdict(list) # <1> with open(sys.argv[1], encoding='utf-8') as fp: diff --git a/11-iface-abc/lotto.py b/11-iface-abc/lotto.py index 2295b71..da8c2de 100644 --- a/11-iface-abc/lotto.py +++ b/11-iface-abc/lotto.py @@ -17,7 +17,7 @@ def pick(self): try: position = random.randrange(len(self._balls)) # <2> except ValueError: - raise LookupError('pick from empty BingoCage') + raise LookupError('pick from empty LotteryBlower') return self._balls.pop(position) # <3> def loaded(self): # <4> diff --git a/14-it-generator/sentence.py b/14-it-generator/sentence.py index fb866c4..447a192 100644 --- a/14-it-generator/sentence.py +++ b/14-it-generator/sentence.py @@ -5,7 +5,7 @@ import re import reprlib -RE_WORD = re.compile('\w+') +RE_WORD = re.compile(r'\w+') class Sentence: @@ -17,7 +17,7 @@ def __init__(self, text): def __getitem__(self, index): return self.words[index] # <2> - def __len__(self, index): # <3> + def __len__(self): # <3> return len(self.words) def __repr__(self): diff --git a/14-it-generator/sentence_gen.py b/14-it-generator/sentence_gen.py index a17c48f..32a8225 100644 --- a/14-it-generator/sentence_gen.py +++ b/14-it-generator/sentence_gen.py @@ -5,7 +5,7 @@ import re import reprlib -RE_WORD = re.compile('\w+') +RE_WORD = re.compile(r'\w+') class Sentence: diff --git a/14-it-generator/sentence_gen2.py b/14-it-generator/sentence_gen2.py index 8b0f355..b308100 100644 --- a/14-it-generator/sentence_gen2.py +++ b/14-it-generator/sentence_gen2.py @@ -5,7 +5,7 @@ import re import reprlib -RE_WORD = re.compile('\w+') +RE_WORD = re.compile(r'\w+') class Sentence: diff --git a/14-it-generator/sentence_genexp.py b/14-it-generator/sentence_genexp.py index 2919c29..52228de 100644 --- a/14-it-generator/sentence_genexp.py +++ b/14-it-generator/sentence_genexp.py @@ -6,7 +6,7 @@ import re import reprlib -RE_WORD = re.compile('\w+') +RE_WORD = re.compile(r'\w+') class Sentence: diff --git a/14-it-generator/sentence_iter.py b/14-it-generator/sentence_iter.py index 938d5b4..11b8179 100644 --- a/14-it-generator/sentence_iter.py +++ b/14-it-generator/sentence_iter.py @@ -9,7 +9,7 @@ import re import reprlib -RE_WORD = re.compile('\w+') +RE_WORD = re.compile(r'\w+') class Sentence: diff --git a/14-it-generator/sentence_iter2.py b/14-it-generator/sentence_iter2.py index 8597b32..2663f3f 100644 --- a/14-it-generator/sentence_iter2.py +++ b/14-it-generator/sentence_iter2.py @@ -8,7 +8,7 @@ import re import reprlib -RE_WORD = re.compile('\w+') +RE_WORD = re.compile(r'\w+') class Sentence: diff --git a/18-asyncio-py3.7/README.rst b/18-asyncio-py3.7/README.rst index 0f4f1b8..aad52a3 100644 --- a/18-asyncio-py3.7/README.rst +++ b/18-asyncio-py3.7/README.rst @@ -4,8 +4,8 @@ From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015) http://shop.oreilly.com/product/0636920032519.do ################################################################## -NOTE: this "18b" directory contains the examples of chapter 18 -rewritten using the new async/await syntax available in Python 3.5 -ONLY, instead of the "yield-from" syntax which works since Python -3.3 (and will still work with Python 3.5). +NOTE: this directory contains the examples of chapter 18 +rewritten using the new async/await syntax available from Python +3.5+, instead of the "yield-from" syntax of Python 3.3 and 3.4. +The code was tested with Python 3.7 ################################################################## diff --git a/18-asyncio-py3.7/charfinder/charfinder.py b/18-asyncio-py3.7/charfinder/charfinder.py index 7e06792..64e4949 100755 --- a/18-asyncio-py3.7/charfinder/charfinder.py +++ b/18-asyncio-py3.7/charfinder/charfinder.py @@ -64,9 +64,9 @@ import functools from collections import namedtuple -RE_WORD = re.compile('\w+') +RE_WORD = re.compile(r'\w+') RE_UNICODE_NAME = re.compile('^[A-Z0-9 -]+$') -RE_CODEPOINT = re.compile('U\+([0-9A-F]{4,6})') +RE_CODEPOINT = re.compile(r'U\+([0-9A-F]{4,6})') INDEX_NAME = 'charfinder_index.pickle' MINIMUM_SAVE_LEN = 10000 diff --git a/18-asyncio-py3.7/charfinder/tcp_charfinder.py b/18-asyncio-py3.7/charfinder/tcp_charfinder.py index 86e27c9..4980b92 100755 --- a/18-asyncio-py3.7/charfinder/tcp_charfinder.py +++ b/18-asyncio-py3.7/charfinder/tcp_charfinder.py @@ -38,26 +38,17 @@ async def handle_queries(reader, writer): # <3> # END TCP_CHARFINDER_TOP # BEGIN TCP_CHARFINDER_MAIN -def main(address='127.0.0.1', port=2323): # <1> +async def main(address='127.0.0.1', port=2323): # <1> port = int(port) - loop = asyncio.get_event_loop() - server_coro = asyncio.start_server(handle_queries, address, port, - loop=loop) # <2> - server = loop.run_until_complete(server_coro) # <3> + server = await asyncio.start_server(handle_queries, address, port) # <2> - host = server.sockets[0].getsockname() # <4> - print('Serving on {}. Hit CTRL-C to stop.'.format(host)) # <5> - try: - loop.run_forever() # <6> - except KeyboardInterrupt: # CTRL+C pressed - pass + host = server.sockets[0].getsockname() # <3> + print('Serving on {}. Hit CTRL-C to stop.'.format(host)) # <4> - print('Server shutting down.') - server.close() # <7> - loop.run_until_complete(server.wait_closed()) # <8> - loop.close() # <9> + async with server: + await server.serve_forever() if __name__ == '__main__': - main(*sys.argv[1:]) # <10> + asyncio.run(main(*sys.argv[1:])) # <5> # END TCP_CHARFINDER_MAIN diff --git a/18-asyncio-py3.7/spinner_asyncio.py b/18-asyncio-py3.7/spinner_asyncio.py index adbd611..369a8f0 100755 --- a/18-asyncio-py3.7/spinner_asyncio.py +++ b/18-asyncio-py3.7/spinner_asyncio.py @@ -9,21 +9,17 @@ # BEGIN SPINNER_ASYNCIO import asyncio import itertools -import sys async def spin(msg): # <1> - write, flush = sys.stdout.write, sys.stdout.flush for char in itertools.cycle('|/-\\'): status = char + ' ' + msg - write(status) - flush() - write('\x08' * len(status)) + print(status, flush=True, end='\r') try: await asyncio.sleep(.1) # <2> except asyncio.CancelledError: # <3> break - write(' ' * len(status) + '\x08' * len(status)) + print(' ' * len(status), end='\r') async def slow_function(): # <4> diff --git a/18-asyncio-py3.7/spinner_thread.py b/18-asyncio-py3.7/spinner_thread.py index dffcca6..bffc921 100755 --- a/18-asyncio-py3.7/spinner_thread.py +++ b/18-asyncio-py3.7/spinner_thread.py @@ -10,20 +10,15 @@ import threading import itertools import time -import sys -def spin(msg, done): # <2> - write, flush = sys.stdout.write, sys.stdout.flush +def spin(msg, done): # <1> for char in itertools.cycle('|/-\\'): # <3> status = char + ' ' + msg - write(status) - flush() - write('\x08' * len(status)) # <4> + print(status, flush=True, end='\r') if done.wait(.1): # <5> break - write(' ' * len(status) + '\x08' * len(status)) # <6> - + print(' ' * len(status), end='\r') def slow_function(): # <7> # pretend waiting a long time for I/O diff --git a/18-asyncio/charfinder/charfinder.py b/18-asyncio/charfinder/charfinder.py index 7e06792..c061f90 100755 --- a/18-asyncio/charfinder/charfinder.py +++ b/18-asyncio/charfinder/charfinder.py @@ -64,7 +64,7 @@ import functools from collections import namedtuple -RE_WORD = re.compile('\w+') +RE_WORD = re.compile(r'\w+') RE_UNICODE_NAME = re.compile('^[A-Z0-9 -]+$') RE_CODEPOINT = re.compile('U\+([0-9A-F]{4,6})') @@ -163,7 +163,7 @@ def find_chars(self, query, start=0, stop=None): result_sets = [] for word in tokenize(query): chars = self.index.get(word) - if chars is None: # shorcut: no such word + if chars is None: # shortcut: no such word result_sets = [] break result_sets.append(chars) diff --git a/19-dyn-attr-prop/oscon/test_schedule1.py b/19-dyn-attr-prop/oscon/test_schedule1.py index dbaacc9..ba5dfd1 100644 --- a/19-dyn-attr-prop/oscon/test_schedule1.py +++ b/19-dyn-attr-prop/oscon/test_schedule1.py @@ -1,15 +1,20 @@ +import os import shelve + import pytest import schedule1 as schedule +DB_NAME = 'data/test_db' + -@pytest.yield_fixture +@pytest.fixture(scope='module') def db(): - with shelve.open(schedule.DB_NAME) as the_db: + with shelve.open(DB_NAME) as the_db: if schedule.CONFERENCE not in the_db: schedule.load_db(the_db) yield the_db + os.remove(DB_NAME) def test_record_class(): diff --git a/19-dyn-attr-prop/oscon/test_schedule2.py b/19-dyn-attr-prop/oscon/test_schedule2.py index de09d32..ab1c79c 100644 --- a/19-dyn-attr-prop/oscon/test_schedule2.py +++ b/19-dyn-attr-prop/oscon/test_schedule2.py @@ -1,15 +1,18 @@ +import os import shelve + import pytest import schedule2 as schedule -@pytest.yield_fixture +@pytest.fixture(scope='module') def db(): - with shelve.open(schedule.DB_NAME) as the_db: + with shelve.open(DB_NAME) as the_db: if schedule.CONFERENCE not in the_db: schedule.load_db(the_db) yield the_db + os.remove(DB_NAME) def test_record_attr_access(): diff --git a/README.rst b/README.rst index 99a0ede..39b95c4 100644 --- a/README.rst +++ b/README.rst @@ -1,9 +1,9 @@ -Fluent Python: example code -=========================== +Fluent Python, First Edition: example code +========================================== -Example code for the book `Fluent Python`_ by Luciano Ramalho (O'Reilly, 2014). +**This repository is archived and will not be updated. Please visit https://github.com/fluentpython/example-code-2e** - **BEWARE**: This is a work in progress, like the book itself. +Example code for the book `Fluent Python, First Edition` by Luciano Ramalho (O'Reilly, 2015). * Code here may change and disappear without warning. diff --git a/attic/concurrency/charfinder/charfinder.py b/attic/concurrency/charfinder/charfinder.py index d73f60b..72e21a4 100755 --- a/attic/concurrency/charfinder/charfinder.py +++ b/attic/concurrency/charfinder/charfinder.py @@ -63,9 +63,9 @@ import itertools from collections import namedtuple -RE_WORD = re.compile('\w+') +RE_WORD = re.compile(r'\w+') RE_UNICODE_NAME = re.compile('^[A-Z0-9 -]+$') -RE_CODEPOINT = re.compile('U\+([0-9A-F]{4,6})') +RE_CODEPOINT = re.compile(r'U\+([0-9A-F]{4,6})') INDEX_NAME = 'charfinder_index.pickle' MINIMUM_SAVE_LEN = 10000 @@ -165,7 +165,7 @@ def find_chars(self, query, start=0, stop=None): for word in tokenize(query): if word in self.index: result_sets.append(self.index[word]) - else: # shorcut: no such word + else: # shortcut: no such word result_sets = [] break if result_sets: diff --git a/attic/dicts/index_alex.py b/attic/dicts/index_alex.py index 27d7175..73db8c6 100644 --- a/attic/dicts/index_alex.py +++ b/attic/dicts/index_alex.py @@ -8,7 +8,7 @@ import sys import re -NONWORD_RE = re.compile('\W+') +NONWORD_RE = re.compile(r'\W+') idx = {} with open(sys.argv[1], encoding='utf-8') as fp: diff --git a/attic/sequences/sentence_slice.py b/attic/sequences/sentence_slice.py index d275989..918338d 100644 --- a/attic/sequences/sentence_slice.py +++ b/attic/sequences/sentence_slice.py @@ -6,9 +6,9 @@ import reprlib -RE_TOKEN = re.compile('\w+|\s+|[^\w\s]+') +RE_TOKEN = re.compile(r'\w+|\s+|[^\w\s]+') RE_WORD = re.compile('\w+') -RE_PUNCTUATION = re.compile('[^\w\s]+') +RE_PUNCTUATION = re.compile(r'[^\w\s]+') class SentenceSlice: