├── Chapter04 ├── conf.py ├── _build │ └── pdf │ │ ├── PySTLCookbook.pdf │ │ ├── PySTLCookbook.rtc │ │ └── PySTLCookbook.stylelog ├── filesdirs_03.py ├── filesdirs_07.py ├── filesdirs_06.py ├── filesdirs_04.py ├── filesdirs_01.py ├── filesdirs_05.py ├── filesdirs_02.py ├── filesdirs_08.py └── filesdirs_09.py ├── Chapter08 ├── wrapt-1.10.11.tar.gz ├── crypto_01.py ├── crypto_03.py ├── crypto_02.py └── crypto_04.py ├── Chapter06 ├── _build │ └── doctrees │ │ └── io.doctree ├── io_02.py ├── io_03.py ├── io_01.py ├── io_05.py ├── io_09.py ├── io_04.py ├── io_10.py ├── io_07.py ├── io_08.py └── io_06.py ├── Chapter14 ├── devtools_02.py ├── devtools_01.py ├── devtools_05.py ├── devtools_03.py ├── devtools_09.py ├── test_devtools_02.py ├── devtools_07.py ├── devtools_06.py ├── devtools_08.py └── devtools_04.py ├── Chapter02 ├── text_05.py ├── text_01.py ├── text_02.py ├── text_08.py ├── text_07.py ├── text_06.py ├── text_04.py └── text_03.py ├── Chapter05 ├── datetimes_10.py ├── datetimes_04.py ├── datetimes_03.py ├── datetimes_01.py ├── datetimes_06.py ├── datetimes_09.py ├── datetimes_08.py ├── datetimes_07.py ├── datetimes_05.py └── datetimes_02.py ├── Chapter07 ├── algorithms_09.py ├── algorithms_04.py ├── algorithms_02.py ├── algorithms_05.py ├── algorithms_07.py ├── algorithms_06.py ├── algorithms_11.py ├── algorithms_03.py ├── algorithms_13.py ├── algorithms_12.py ├── algorithms_01.py ├── algorithms_10.py └── algorithms_08.py ├── Chapter01 ├── datastructures_08.py ├── datastructures_03.py ├── datastructures_05.py ├── datastructures_04.py ├── datastructures_07.py ├── datastructures_01.py ├── datastructures_06.py └── datastructures_02.py ├── Chapter12 ├── multimedia_02.py ├── multimedia_04.py ├── multimedia_01.py └── multimedia_03.py ├── Chapter09 ├── concurrency_02.py ├── concurrency_03.py ├── concurrency_01.py ├── concurrency_06.py ├── concurrency_04.py └── concurrency_05.py ├── Chapter13 ├── gui_01.py ├── gui_02.py ├── gui_05.py ├── gui_04.py └── gui_03.py ├── Chapter11 ├── web_02.py ├── web_05.py ├── web_07.py ├── web_11.py ├── web_09.py ├── web_03.py ├── web_08.py ├── web_04.py ├── web_10.py ├── web_06.py └── web_01.py ├── Chapter03 ├── terminal_07.py ├── terminal_01.py ├── terminal_02.py ├── terminal_06.py ├── terminal_03.py ├── terminal_04.py ├── terminal_08.py ├── terminal_10.py ├── terminal_09.py └── terminal_05.py ├── extract_code.py ├── LICENSE ├── Chapter10 ├── networking_06.py ├── networking_01.py ├── networking_03.py ├── networking_04.py ├── networking_02.py └── networking_05.py └── README.md /Chapter04/conf.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter08/wrapt-1.10.11.tar.gz: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter04/_build/pdf/PySTLCookbook.pdf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter04/_build/pdf/PySTLCookbook.rtc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter06/_build/doctrees/io.doctree: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter04/_build/pdf/PySTLCookbook.stylelog: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter14/devtools_02.py: -------------------------------------------------------------------------------- 1 | def divide(x, y): 2 | return x / y 3 | -------------------------------------------------------------------------------- /Chapter08/crypto_01.py: -------------------------------------------------------------------------------- 1 | import getpass 2 | 3 | pwd = getpass.getpass() 4 | 5 | print(pwd) 6 | -------------------------------------------------------------------------------- /Chapter06/io_02.py: -------------------------------------------------------------------------------- 1 | with open('/var/log/install.log') as f: 2 | lines = list(f) 3 | 4 | print(lines[:5]) -------------------------------------------------------------------------------- /Chapter02/text_05.py: -------------------------------------------------------------------------------- 1 | import shlex 2 | 3 | text = 'I was sleeping at the "Windsdale Hotel"' 4 | print(shlex.split(text)) 5 | -------------------------------------------------------------------------------- /Chapter02/text_01.py: -------------------------------------------------------------------------------- 1 | import fnmatch 2 | print(fnmatch.fnmatch('hello.txt', '*.txt')) 3 | print(fnmatch.fnmatch('hello.zip', '*.txt')) 4 | -------------------------------------------------------------------------------- /Chapter04/filesdirs_03.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | print(list(pathlib.Path('.').glob('*.py'))) 4 | 5 | print(list(pathlib.Path('.').glob('**/*.py'))) -------------------------------------------------------------------------------- /Chapter04/filesdirs_07.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | def decode_filename(fname): 4 | fse = sys.getfilesystemencoding() 5 | return fname.decode(fse, "surrogateescape") -------------------------------------------------------------------------------- /Chapter05/datetimes_10.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | t = datetime.time(13, 30) 4 | d = datetime.date(2018, 1, 11) 5 | result = datetime.datetime.combine(d, t) 6 | 7 | print(result) -------------------------------------------------------------------------------- /Chapter07/algorithms_09.py: -------------------------------------------------------------------------------- 1 | import functools, operator 2 | 3 | values = range(10) 4 | mul3 = functools.partial(operator.mul, 3) 5 | print( 6 | list(map(mul3, values)) 7 | ) 8 | -------------------------------------------------------------------------------- /Chapter05/datetimes_04.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | ts = 1521588268 4 | d = datetime.datetime.utcfromtimestamp(ts) 5 | print(repr(d)) 6 | 7 | newts = d.timestamp() 8 | print(newts) 9 | -------------------------------------------------------------------------------- /Chapter01/datastructures_08.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | 3 | class RequestType(IntEnum): 4 | POST = 1 5 | GET = 2 6 | 7 | request_type = RequestType.POST 8 | print(request_type) 9 | -------------------------------------------------------------------------------- /Chapter07/algorithms_04.py: -------------------------------------------------------------------------------- 1 | names = [ 'Sam', 'Axel', 'Aerith' ] 2 | surnames = [ 'Fisher', 'Foley', 'Gainsborough' ] 3 | 4 | people = zip(names, surnames) 5 | print( 6 | list(people) 7 | ) 8 | -------------------------------------------------------------------------------- /Chapter01/datastructures_03.py: -------------------------------------------------------------------------------- 1 | from collections import ChainMap 2 | 3 | def f(a, b, c, d): 4 | print(a, b, c, d) 5 | 6 | d1 = {'a': 1, 'b': 2} 7 | d2 = {'c': 3, 'd': 4} 8 | f(**ChainMap(d1, d2)) 9 | -------------------------------------------------------------------------------- /Chapter07/algorithms_02.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | def iter_nth(iterable, nth): 4 | return next(itertools.islice(iterable, nth, nth+1)) 5 | 6 | values = (x for x in range(10)) 7 | print(iter_nth(values, 4)) 8 | -------------------------------------------------------------------------------- /Chapter14/devtools_01.py: -------------------------------------------------------------------------------- 1 | def divide(x, y): 2 | print('Going to divide {} / {}'.format(x, y)) 3 | import pdb; pdb.set_trace() # breakpoint() 4 | return x / y 5 | 6 | 7 | print( 8 | divide(3, 2) 9 | ) -------------------------------------------------------------------------------- /Chapter01/datastructures_05.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | rd = defaultdict(list) 4 | for name, num in [('ichi', 1), ('one', 1), ('uno', 1), ('un', 1)]: 5 | rd[num].append(name) 6 | 7 | print(rd) 8 | -------------------------------------------------------------------------------- /Chapter04/filesdirs_06.py: -------------------------------------------------------------------------------- 1 | import tempfile 2 | 3 | with tempfile.SpooledTemporaryFile(max_size=30) as temp: 4 | for i in range(3): 5 | temp.write(b'Line of text\n') 6 | 7 | temp.seek(0) 8 | print(temp.read()) 9 | -------------------------------------------------------------------------------- /Chapter04/filesdirs_04.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | stat = pathlib.Path('conf.py').stat() 4 | print(stat) 5 | 6 | print(pathlib.Path('conf.py').exists()) 7 | print(pathlib.Path('conf.py').is_dir()) 8 | print(pathlib.Path('_build').is_dir()) 9 | -------------------------------------------------------------------------------- /Chapter07/algorithms_05.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | values = [['a', 'b', 'c'], 4 | [1, 2, 3], 5 | ['X', 'Y', 'Z']] 6 | 7 | chained = itertools.chain.from_iterable(values) 8 | print( 9 | list(chained) 10 | ) 11 | -------------------------------------------------------------------------------- /Chapter01/datastructures_04.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | 3 | attrs = OrderedDict([('id', 'header'), ('style', 'background-color:red')]) 4 | print( 5 | ''.format(' '.join('%s="%s"' % a for a in attrs.items())) 6 | ) 7 | -------------------------------------------------------------------------------- /Chapter04/filesdirs_01.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def traverse(path): 5 | for basepath, directories, files in os.walk(path): 6 | for f in files: 7 | yield os.path.join(basepath, f) 8 | 9 | for f in traverse('.'): 10 | print(f) -------------------------------------------------------------------------------- /Chapter07/algorithms_07.py: -------------------------------------------------------------------------------- 1 | values = [ 1, 2, 3, 4, 5 ] 2 | 3 | import functools, operator 4 | print( 5 | functools.reduce(operator.add, values) 6 | ) 7 | 8 | import itertools 9 | print( 10 | list(itertools.accumulate(values, operator.add)) 11 | ) 12 | -------------------------------------------------------------------------------- /Chapter04/filesdirs_05.py: -------------------------------------------------------------------------------- 1 | import tempfile, os 2 | 3 | with tempfile.NamedTemporaryFile() as f: 4 | print(f.name) 5 | 6 | with tempfile.NamedTemporaryFile() as f: 7 | os.system('echo "Hello World" > %s' % f.name) 8 | f.seek(0) 9 | print(f.read()) 10 | 11 | 12 | -------------------------------------------------------------------------------- /Chapter05/datetimes_03.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | def asutc(d): 4 | return d.astimezone(datetime.timezone.utc) 5 | 6 | now = datetime.datetime.now().replace( 7 | tzinfo=datetime.timezone(datetime.timedelta(hours=1)) 8 | ) 9 | print(now) 10 | 11 | print(asutc(now)) 12 | -------------------------------------------------------------------------------- /Chapter12/multimedia_02.py: -------------------------------------------------------------------------------- 1 | import imghdr 2 | 3 | 4 | def detect_image_format(filename): 5 | return imghdr.what(filename) 6 | 7 | 8 | if __name__ == '__main__': 9 | import sys 10 | print(detect_image_format(sys.argv[1])) 11 | print(detect_image_format(open(sys.argv[1], 'rb'))) -------------------------------------------------------------------------------- /Chapter12/multimedia_04.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | import webbrowser 3 | 4 | 5 | def playfile(fpath): 6 | fpath = pathlib.Path(fpath).expanduser().resolve() 7 | webbrowser.open('file://{}'.format(fpath)) 8 | 9 | 10 | if __name__ == '__main__': 11 | import sys 12 | playfile(sys.argv[1]) -------------------------------------------------------------------------------- /Chapter06/io_03.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | with open('/tmp/packet.dump', 'wb') as f: 4 | data = struct.pack('>HHLL', 50291, 80, 2778997212, 644363807) 5 | f.write(data) 6 | print(data) 7 | 8 | with open('/tmp/packet.dump', 'rb') as f: 9 | data = struct.unpack_from('>HHLL', f.read()) 10 | print(data) 11 | 12 | -------------------------------------------------------------------------------- /Chapter07/algorithms_06.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | c = itertools.product(('A', 'B', 'C'), repeat=2) 3 | 4 | print( 5 | list(c) 6 | ) 7 | 8 | c = itertools.permutations(('A', 'B', 'C'), 2) 9 | print( 10 | list(c) 11 | ) 12 | 13 | c = itertools.combinations(('A', 'B', 'C'), 2) 14 | print( 15 | list(c) 16 | ) 17 | -------------------------------------------------------------------------------- /Chapter06/io_01.py: -------------------------------------------------------------------------------- 1 | # Write a file with latin-1 encoding 2 | with open('/tmp/somefile.txt', mode='w', encoding='latin-1') as f: 3 | f.write('This is some latin1 text: "è già ora"') 4 | 5 | # Read back file with latin-1 encoding. 6 | with open('/tmp/somefile.txt', encoding='latin-1') as f: 7 | txt = f.read() 8 | print(txt) -------------------------------------------------------------------------------- /Chapter05/datetimes_01.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | def now(): 4 | return datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) 5 | def astimezone(d, offset): 6 | return d.astimezone(datetime.timezone(datetime.timedelta(hours=offset))) 7 | 8 | d = now() 9 | print(d) 10 | 11 | d = astimezone(d, 1) 12 | print(d) 13 | -------------------------------------------------------------------------------- /Chapter14/devtools_05.py: -------------------------------------------------------------------------------- 1 | def function1(): 2 | l = [] 3 | for i in range(100): 4 | l.append(i) 5 | return l 6 | 7 | 8 | def function2(): 9 | return [i for i in range(100)] 10 | 11 | 12 | import timeit 13 | 14 | print( 15 | timeit.timeit(function1) 16 | ) 17 | print( 18 | timeit.timeit(function2) 19 | ) -------------------------------------------------------------------------------- /Chapter01/datastructures_07.py: -------------------------------------------------------------------------------- 1 | class Bunch(dict): 2 | def __getattribute__(self, key): 3 | try: 4 | return self[key] 5 | except KeyError: 6 | raise AttributeError(key) 7 | 8 | def __setattr__(self, key, value): 9 | self[key] = value 10 | 11 | b = Bunch(a=5) 12 | print(b.a) 13 | print(b['a']) 14 | -------------------------------------------------------------------------------- /Chapter14/devtools_03.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | 3 | def print_division(x, y): 4 | print(x / y) 5 | 6 | if __name__ == '__main__': 7 | with mock.patch('builtins.print') as mprint: 8 | print_division(4, 2) 9 | 10 | mprint.assert_called_with(2) 11 | 12 | mock_args, mock_kwargs = mprint.call_args 13 | print(mock_args) 14 | -------------------------------------------------------------------------------- /Chapter05/datetimes_06.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | def shiftdate(d, days): 4 | return ( 5 | d.replace(hour=0, minute=0, second=0, microsecond=0) + 6 | datetime.timedelta(days=days) 7 | ) 8 | 9 | now = datetime.datetime.utcnow() 10 | print(now) 11 | print(shiftdate(now, 1)) 12 | print(shiftdate(now, -1)) 13 | print(shiftdate(now, 11)) 14 | -------------------------------------------------------------------------------- /Chapter02/text_02.py: -------------------------------------------------------------------------------- 1 | s = 'Today the weather is nice' 2 | s2 = 'Today the weater is nice' 3 | s3 = 'Yesterday the weather was nice' 4 | s4 = 'Today my dog ate steak' 5 | 6 | import difflib 7 | print(difflib.SequenceMatcher(None, s, s2, False).ratio()) 8 | print(difflib.SequenceMatcher(None, s, s3, False).ratio()) 9 | print(difflib.SequenceMatcher(None, s, s4, False).ratio()) 10 | -------------------------------------------------------------------------------- /Chapter07/algorithms_11.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | 4 | def decorator(f): 5 | @wraps(f) 6 | def _f(*args, **kwargs): 7 | return f(*args, **kwargs) 8 | return _f 9 | 10 | 11 | @decorator 12 | def sumthree(a, b): 13 | """Sums a and b""" 14 | return a + back 15 | 16 | 17 | print(sumthree.__name__) 18 | 19 | print(sumthree.__doc__) 20 | -------------------------------------------------------------------------------- /Chapter01/datastructures_01.py: -------------------------------------------------------------------------------- 1 | txt = "This is a vast world you can't traverse world in a day" 2 | 3 | from collections import Counter 4 | counts = Counter(txt.split()) 5 | print(counts) 6 | 7 | print(counts.most_common(2)) 8 | 9 | print(sum(counts.values())) 10 | 11 | print(Counter(["hello", "world"]) + Counter(["hello", "you"])) 12 | print(Counter(["hello", "world"]) & Counter(["hello", "you"])) -------------------------------------------------------------------------------- /Chapter05/datetimes_09.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | def workdays(d, end, excluded=(6, 7)): 4 | days = [] 5 | while d.date() < end.date(): 6 | if d.isoweekday() not in excluded: 7 | days.append(d) 8 | d += datetime.timedelta(days=1) 9 | return days 10 | 11 | print(workdays(datetime.datetime(2018, 3, 22), 12 | datetime.datetime(2018, 3, 26))) 13 | -------------------------------------------------------------------------------- /Chapter07/algorithms_03.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | def group_by_key(iterable, key): 4 | iterable = sorted(iterable, key=key) 5 | return {k: list(g) for k,g in itertools.groupby(iterable, key)} 6 | 7 | names = [('Alex', 'Zanardi'), 8 | ('Julius', 'Caesar'), 9 | ('Anakin', 'Skywalker'), 10 | ('Joseph', 'Joestar')] 11 | 12 | print( 13 | group_by_key(names, lambda v: v[0][0]) 14 | ) 15 | -------------------------------------------------------------------------------- /Chapter09/concurrency_02.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | async def countdown(identifier, n): 4 | while n > 0: 5 | print('left:', n, '({})'.format(identifier)) 6 | await asyncio.sleep(1) 7 | n -= 1 8 | 9 | async def main(): 10 | await asyncio.wait([ 11 | countdown("A", 2), 12 | countdown("B", 3) 13 | ]) 14 | 15 | loop = asyncio.get_event_loop() 16 | loop.run_until_complete(main()) 17 | loop.close() -------------------------------------------------------------------------------- /Chapter14/devtools_09.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def slowfunc(goslow=False): 4 | l = [] 5 | for i in range(100): 6 | l.append(i) 7 | if goslow: 8 | time.sleep(0.01) 9 | return l 10 | 11 | 12 | from cProfile import Profile 13 | 14 | profiler = Profile() 15 | profiler.runcall(slowfunc, True) 16 | profiler.print_stats() 17 | 18 | profiler = Profile() 19 | profiler.runcall(slowfunc, False) 20 | profiler.print_stats() -------------------------------------------------------------------------------- /Chapter02/text_08.py: -------------------------------------------------------------------------------- 1 | cols = ['hello world', 2 | 'this is a long text, maybe longer than expected, surely long enough', 3 | 'one more column'] 4 | COLSIZE = 20 5 | import textwrap, itertools 6 | 7 | def maketable(cols): 8 | return '\n'.join(map(' | '.join, itertools.zip_longest(*[ 9 | [s.ljust(COLSIZE) for s in textwrap.wrap(col, COLSIZE)] for col in cols 10 | ], fillvalue=' '*COLSIZE))) 11 | 12 | print(maketable(cols)) 13 | -------------------------------------------------------------------------------- /Chapter05/datetimes_08.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | def monthweekdays(month, weekday): 4 | now = datetime.datetime.utcnow() 5 | d = now.replace(day=1, month=month, hour=0, minute=0, second=0, microsecond=0) 6 | days = [] 7 | while d.month == month: 8 | if d.isoweekday() == weekday: 9 | days.append(d) 10 | d += datetime.timedelta(days=1) 11 | return days 12 | 13 | print(monthweekdays(3, 1)) 14 | print(monthweekdays(3, 1)[2]) 15 | -------------------------------------------------------------------------------- /Chapter07/algorithms_13.py: -------------------------------------------------------------------------------- 1 | from contextlib import ExitStack 2 | import contextlib 3 | 4 | @contextlib.contextmanager 5 | def first(): 6 | print('First') 7 | yield 8 | 9 | @contextlib.contextmanager 10 | def second(): 11 | print('Second') 12 | yield 13 | 14 | for n in range(5): 15 | with ExitStack() as stack: 16 | stack.enter_context(first()) 17 | if n % 2 == 0: 18 | stack.enter_context(second()) 19 | print('NUMBER: {}'.format(n)) 20 | -------------------------------------------------------------------------------- /Chapter13/gui_01.py: -------------------------------------------------------------------------------- 1 | from tkinter import messagebox, Tk 2 | 3 | 4 | def alert(title, message, kind='info', hidemain=True): 5 | if kind not in ('error', 'warning', 'info'): 6 | raise ValueError('Unsupported alert kind.') 7 | 8 | show_method = getattr(messagebox, 'show{}'.format(kind)) 9 | show_method(title, message) 10 | 11 | 12 | if __name__ == '__main__': 13 | Tk().withdraw() 14 | alert('Hello', 'Hello World') 15 | alert('Hello Again', 'Hello World 2', kind='warning') -------------------------------------------------------------------------------- /Chapter01/datastructures_06.py: -------------------------------------------------------------------------------- 1 | import time 2 | import heapq 3 | 4 | class PriorityQueue(object): 5 | def __init__(self): 6 | self._q = [] 7 | 8 | def add(self, value, priority=0): 9 | heapq.heappush(self._q, (priority, time.time(), value)) 10 | 11 | def pop(self): 12 | return heapq.heappop(self._q)[-1] 13 | 14 | 15 | def f1(): print('hello') 16 | def f2(): print('world') 17 | 18 | pq = PriorityQueue() 19 | pq.add(f2, priority=1) 20 | pq.add(f1, priority=0) 21 | pq.pop()() 22 | pq.pop()() 23 | -------------------------------------------------------------------------------- /Chapter11/web_02.py: -------------------------------------------------------------------------------- 1 | import urllib.parse 2 | 3 | def parse_url(url): 4 | """Parses an URL of the most widspread format. 5 | 6 | This takes for granted there is a single set of parameters 7 | for the whole path. 8 | """ 9 | parts = urllib.parse.urlparse(url) 10 | parsed = vars(parts) 11 | parsed['query'] = urllib.parse.parse_qs(parts.query) 12 | return parsed 13 | 14 | url = 'http://user:pwd@host.com:80/path/subpath?arg1=val1&arg2=val2#fragment' 15 | result = parse_url(url) 16 | print(result) 17 | -------------------------------------------------------------------------------- /Chapter05/datetimes_07.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | def shiftmonth(d, months): 4 | for _ in range(abs(months)): 5 | if months > 0: 6 | d = d.replace(day=5) + datetime.timedelta(days=28) 7 | else: 8 | d = d.replace(day=1) - datetime.timedelta(days=1) 9 | d = d.replace(day=1, hour=0, minute=0, second=0, microsecond=0) 10 | return d 11 | 12 | now = datetime.datetime.utcnow() 13 | print(now) 14 | print(shiftmonth(now, 1)) 15 | print(shiftmonth(now, -1)) 16 | print(shiftmonth(now, 10)) 17 | -------------------------------------------------------------------------------- /Chapter06/io_05.py: -------------------------------------------------------------------------------- 1 | import shelve 2 | 3 | with shelve.open('/tmp/shelf.db') as shelf: 4 | shelf['value'] = 5 5 | 6 | with shelve.open('/tmp/shelf.db') as shelf: 7 | print(shelf['value']) 8 | 9 | class MyClass(object): 10 | def __init__(self, value): 11 | self.value = value 12 | 13 | with shelve.open('/tmp/shelf.db') as shelf: 14 | shelf['value'] = MyClass(5) 15 | 16 | with shelve.open('/tmp/shelf.db') as shelf: 17 | print(shelf['value']) 18 | 19 | with shelve.open('/tmp/shelf.db') as shelf: 20 | print(shelf['value'].value) 21 | -------------------------------------------------------------------------------- /Chapter04/filesdirs_02.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | path = pathlib.Path('somefile.txt') 4 | path.write_text('Hello World') # Write some text into file. 5 | 6 | print(path.resolve()) # Print absolute path 7 | 8 | print(path.read_text()) # Check the file content 9 | 10 | path.unlink() # Destroy the file 11 | try: 12 | print(path.resolve()) # Print absolute path 13 | except Exception as e: 14 | print(e) 15 | 16 | path = pathlib.Path('.') 17 | path = path.resolve() 18 | print(path) 19 | 20 | path = path / '..' 21 | print(path.resolve()) 22 | -------------------------------------------------------------------------------- /Chapter04/filesdirs_08.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | 3 | def copydir(source, dest, ignore=None): 4 | """Copy source to dest and ignore any file matching ignore pattern.""" 5 | shutil.copytree(source, dest, ignore_dangling_symlinks=True, 6 | ignore=shutil.ignore_patterns(*ignore) if ignore else None) 7 | 8 | import glob 9 | print(glob.glob('_build/pdf/*')) 10 | 11 | print(glob.glob('/tmp/buildcopy/*')) 12 | 13 | copydir('_build/pdf', '/tmp/buildcopy', ignore=('*.rtc', '*.stylelog')) 14 | 15 | print(glob.glob('/tmp/buildcopy/*')) 16 | -------------------------------------------------------------------------------- /Chapter06/io_09.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | with open('/tmp/table.csv', 'w', encoding='utf-8') as f: 4 | writer = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC) 5 | writer.writerow(("ID","Name","Surname","Language")) 6 | writer.writerow((1,"Alessandro","Molina","Italian")) 7 | writer.writerow((2,"Mika","Häkkinen","Suomi")) 8 | writer.writerow((3,"Sebastian","Vettel","Deutsch")) 9 | 10 | with open('/tmp/table.csv', 'r', encoding='utf-8', newline='') as f: 11 | reader = csv.DictReader(f) 12 | for row in reader: 13 | print(row) 14 | -------------------------------------------------------------------------------- /Chapter07/algorithms_12.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | 3 | @contextlib.contextmanager 4 | def logentrance(): 5 | print('Enter') 6 | yield 7 | print('Exit') 8 | 9 | 10 | with logentrance(): 11 | print('This is inside') 12 | 13 | 14 | @contextlib.contextmanager 15 | def logentrance(): 16 | print('Enter') 17 | try: 18 | yield 19 | except: 20 | print('Exception') 21 | raise 22 | finally: 23 | print('Exit') 24 | 25 | 26 | with logentrance(): 27 | raise Exception('This is an error') 28 | 29 | -------------------------------------------------------------------------------- /Chapter05/datetimes_05.py: -------------------------------------------------------------------------------- 1 | import locale 2 | import contextlib 3 | import datetime 4 | 5 | 6 | @contextlib.contextmanager 7 | def switchlocale(name): 8 | prev = locale.getlocale() 9 | locale.setlocale(locale.LC_ALL, name) 10 | yield 11 | locale.setlocale(locale.LC_ALL, prev) 12 | 13 | 14 | def format_date(loc, d): 15 | with switchlocale(loc): 16 | fmt = locale.nl_langinfo(locale.D_T_FMT) 17 | return d.strftime(fmt) 18 | 19 | print(format_date('de_DE', datetime.datetime.utcnow())) 20 | print(format_date('en_GB', datetime.datetime.utcnow())) 21 | -------------------------------------------------------------------------------- /Chapter09/concurrency_03.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def fib(n, seen): 4 | if n not in seen and n % 5 == 0: 5 | # Print out only numbers we didn't yet compute 6 | print(os.getpid(), '->', n) 7 | seen.add(n) 8 | 9 | if n < 2: 10 | return n 11 | return fib(n-2, seen) + fib(n-1, seen) 12 | 13 | from multiprocessing import Pool 14 | pool = Pool() 15 | t1 = pool.apply_async(fib, args=(20, set())) 16 | t2 = pool.apply_async(fib, args=(22, set())) 17 | pool.close() 18 | pool.join() 19 | print( 20 | t1.get() 21 | ) 22 | print( 23 | t2.get() 24 | ) 25 | -------------------------------------------------------------------------------- /Chapter03/terminal_07.py: -------------------------------------------------------------------------------- 1 | import shlex 2 | import subprocess 3 | 4 | def run(command): 5 | try: 6 | result = subprocess.check_output(shlex.split(command), 7 | stderr=subprocess.STDOUT) 8 | return 0, result 9 | except subprocess.CalledProcessError as e: 10 | return e.returncode, e.output 11 | for path in ('/', '/should_not_exist'): 12 | status, out = run('ls "{}"'.format(path)) 13 | if status == 0: 14 | print('') 15 | else: 16 | print(''.format(status)) 17 | print(out) 18 | -------------------------------------------------------------------------------- /Chapter03/terminal_01.py: -------------------------------------------------------------------------------- 1 | import logging, sys 2 | 3 | logging.basicConfig(level=logging.INFO, stream=sys.stderr, 4 | format='%(asctime)s %(name)s %(levelname)s: %(message)s') 5 | log = logging.getLogger(__name__) 6 | 7 | def dosum(a, b, count=1): 8 | log.info('Starting sum') 9 | if a == b == 0: 10 | log.warning('Will be just 0 for any count') 11 | res = (a + b) * count 12 | log.info('(%s + %s) * %s = %s' % (a, b, count, res)) 13 | print(res) 14 | 15 | dosum(5, 3) 16 | 17 | dosum(5, 3, count=2) 18 | 19 | dosum(0, 1, count=5) 20 | 21 | dosum(0, 0) 22 | -------------------------------------------------------------------------------- /Chapter07/algorithms_01.py: -------------------------------------------------------------------------------- 1 | import bisect 2 | 3 | values = [ 5, 3, 1, 7 ] 4 | print(5 in values) 5 | 6 | sorted_values = sorted(values) 7 | print(sorted_values) 8 | 9 | def bisect_search(container, value): 10 | index = bisect.bisect_left(container, value) 11 | return index < len(container) and container[index] == value 12 | 13 | print(bisect_search(sorted_values, 5)) 14 | 15 | 16 | import timeit 17 | values = list(range(1000)) 18 | 19 | print(900 in values) 20 | print(bisect_search(values, 900)) 21 | 22 | print(timeit.timeit(lambda: 900 in values)) 23 | print(timeit.timeit(lambda: bisect_search(values, 900))) 24 | -------------------------------------------------------------------------------- /Chapter01/datastructures_02.py: -------------------------------------------------------------------------------- 1 | import os 2 | from collections import ChainMap 3 | 4 | commnad_line_options = {} 5 | config_file_options = {'optname': 'somevalue'} 6 | 7 | options = ChainMap(commnad_line_options, os.environ, config_file_options) 8 | value = options.get('optname', 'default-value') 9 | print(value) 10 | 11 | import os 12 | from collections import ChainMap, defaultdict 13 | 14 | options = ChainMap(commnad_line_options, os.environ, config_file_options, 15 | defaultdict(lambda: 'default-value')) 16 | value = options['optname'] 17 | value2 = options['other-option'] 18 | print(value) 19 | print(value2) -------------------------------------------------------------------------------- /Chapter14/test_devtools_02.py: -------------------------------------------------------------------------------- 1 | from devtools_02 import divide 2 | import unittest 3 | 4 | 5 | class TestDivision(unittest.TestCase): 6 | def setUp(self): 7 | self.num = 6 8 | 9 | def test_int_division(self): 10 | res = divide(self.num, 3) 11 | self.assertEqual(res, 2) 12 | 13 | def test_float_division(self): 14 | res = divide(self.num, 4) 15 | self.assertEqual(res, 1.5) 16 | 17 | def test_divide_zero(self): 18 | with self.assertRaises(ZeroDivisionError) as err: 19 | res = divide(self.num, 0) 20 | self.assertEqual(str(err.exception), 'division by zero') 21 | 22 | -------------------------------------------------------------------------------- /Chapter02/text_07.py: -------------------------------------------------------------------------------- 1 | import unicodedata, sys 2 | 3 | class unaccented_map(dict): 4 | def __missing__(self, key): 5 | ch = self.get(key) 6 | if ch is not None: 7 | return ch 8 | de = unicodedata.decomposition(chr(key)) 9 | if de: 10 | try: 11 | ch = int(de.split(None, 1)[0], 16) 12 | except (IndexError, ValueError): 13 | ch = key 14 | else: 15 | ch = key 16 | self[key] = ch 17 | return ch 18 | 19 | unaccented_map = unaccented_map() 20 | print('Über'.translate(unaccented_map)) 21 | print('garçon'.translate(unaccented_map)) -------------------------------------------------------------------------------- /Chapter02/text_06.py: -------------------------------------------------------------------------------- 1 | txt = """And he looked over at the alarm clock, 2 | ticking on the chest of drawers. "God in Heaven!" he thought. 3 | It was half past six and the hands were quietly moving forwards, 4 | it was even later than half past, more like quarter to seven. 5 | Had the alarm clock not rung? He could see from the bed that it 6 | had been set for four o'clock as it should have been; it certainly must have rung. 7 | Yes, but was it possible to quietly sleep through that furniture-rattling noise? 8 | True, he had not slept peacefully, but probably all the more deeply because of that.""" 9 | 10 | import string 11 | trans = str.maketrans('', '', string.punctuation) 12 | print(txt.lower().translate(trans)) -------------------------------------------------------------------------------- /Chapter06/io_04.py: -------------------------------------------------------------------------------- 1 | import zipfile 2 | import os 3 | 4 | def zipdir(archive_name, directory): 5 | with zipfile.ZipFile( 6 | archive_name, 'w', compression=zipfile.ZIP_DEFLATED 7 | ) as archive: 8 | for root, dirs, files in os.walk(directory): 9 | for filename in files: 10 | abspath = os.path.join(root, filename) 11 | relpath = os.path.relpath(abspath, directory) 12 | archive.write(abspath, relpath) 13 | 14 | zipdir('/tmp/test.zip', '_build/doctrees') 15 | with zipfile.ZipFile('/tmp/test.zip') as archive: 16 | for n in archive.namelist(): 17 | print(n) 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Chapter02/text_04.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | class TemplateFormatter(string.Formatter): 4 | def get_field(self, field_name, args, kwargs): 5 | if field_name.startswith("$"): 6 | code = field_name[1:] 7 | val = eval(code, {}, dict(kwargs)) 8 | return val, field_name 9 | else: 10 | return super(TemplateFormatter, self).get_field(field_name, args, kwargs) 11 | messages = ['Message 1', 'Message 2'] 12 | 13 | tmpl = TemplateFormatter() 14 | txt = tmpl.format("Hello {name}, " 15 | "You have {$len(messages)} message{$len(messages) and 's'}:\n{$'\\n'.join(messages)}", 16 | name='Alessandro', messages=messages) 17 | print(txt) 18 | -------------------------------------------------------------------------------- /Chapter07/algorithms_10.py: -------------------------------------------------------------------------------- 1 | from functools import singledispatch 2 | 3 | @singledispatch 4 | def human_readable(d): 5 | raise ValueError('Unsupported argument type %s' % type(d)) 6 | 7 | @human_readable.register(dict) 8 | def human_readable_dict(d): 9 | for key, value in d.items(): 10 | print('{}: {}'.format(key, value)) 11 | 12 | @human_readable.register(list) 13 | @human_readable.register(tuple) 14 | def human_readable_list(d): 15 | for key, value in d: 16 | print('{}: {}'.format(key, value)) 17 | 18 | human_readable({'name': 'Tifa', 'surname': 'Lockhart'}) 19 | 20 | human_readable([('name', 'Nobuo'), ('surname', 'Uematsu')]) 21 | 22 | try: 23 | human_readable(5) 24 | except Exception as e: 25 | print(e) 26 | -------------------------------------------------------------------------------- /Chapter03/terminal_02.py: -------------------------------------------------------------------------------- 1 | import logging, sys 2 | 3 | if __name__ == '__main__': 4 | if len(sys.argv) < 2: 5 | print('Please provide logging file name as argument') 6 | sys.exit(1) 7 | 8 | logging_file = sys.argv[1] 9 | logging.basicConfig(level=logging.INFO, filename=logging_file, 10 | format='%(asctime)s %(name)s %(levelname)s: %(message)s') 11 | 12 | log = logging.getLogger(__name__) 13 | 14 | def fibo(num): 15 | log.info('Computing up to %sth fibonacci number', num) 16 | a, b = 0, 1 17 | for n in range(num): 18 | a, b = b, a+b 19 | print(b, '', end='') 20 | print(b) 21 | 22 | if __name__ == '__main__': 23 | import datetime 24 | fibo(datetime.datetime.now().second) -------------------------------------------------------------------------------- /Chapter12/multimedia_01.py: -------------------------------------------------------------------------------- 1 | import mimetypes 2 | 3 | def guess_file_type(filename): 4 | if not getattr(guess_file_type, 'initialised', False): 5 | mimetypes.init() 6 | guess_file_type.initialised = True 7 | file_type, encoding = mimetypes.guess_type(filename) 8 | return file_type 9 | 10 | 11 | 12 | if __name__ == '__main__': 13 | print( 14 | guess_file_type('~/Pictures/5565_1680x1050.jpg') 15 | ) 16 | print( 17 | guess_file_type('~/Pictures/5565_1680x1050.jpeg') 18 | ) 19 | print( 20 | guess_file_type('~/Pictures/avatar.png') 21 | ) 22 | print( 23 | guess_file_type('/tmp/unable_to_guess.blob') 24 | ) 25 | print( 26 | guess_file_type('/this/does/not/exists.txt') 27 | ) -------------------------------------------------------------------------------- /Chapter03/terminal_06.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import textwrap, itertools 3 | 4 | def maketable(cols): 5 | term_size = shutil.get_terminal_size(fallback=(80, 24)) 6 | colsize = (term_size.columns // len(cols)) - 3 7 | if colsize < 1: 8 | raise ValueError('Column too small') 9 | return '\n'.join(map(' | '.join, itertools.zip_longest(*[ 10 | [s.ljust(colsize) for s in textwrap.wrap(col, colsize)] for col in cols 11 | ], fillvalue=' '*colsize))) 12 | COLUMNS = 5 13 | TEXT = ['Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ' 14 | 'Aenean commodo ligula eget dolor. Aenean massa. ' 15 | 'Cum sociis natoque penatibus et magnis dis parturient montes, ' 16 | 'nascetur ridiculus mus'] * COLUMNS 17 | 18 | print(maketable(TEXT)) -------------------------------------------------------------------------------- /Chapter08/crypto_03.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | def verify_file(filepath, expectedhash, hashtype='md5'): 4 | with open(filepath, 'rb') as f: 5 | try: 6 | filehash = getattr(hashlib, hashtype)() 7 | except AttributeError: 8 | raise ValueError( 9 | 'Unsupported hashing type %s' % hashtype 10 | ) from None 11 | 12 | while True: 13 | data = f.read(4096) 14 | if not data: 15 | break 16 | filehash.update(data) 17 | 18 | return filehash.hexdigest() == expectedhash 19 | 20 | 21 | verifies = verify_file( 22 | 'wrapt-1.10.11.tar.gz', 23 | 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', 24 | 'sha256' 25 | ) 26 | print(verifies) -------------------------------------------------------------------------------- /Chapter09/concurrency_01.py: -------------------------------------------------------------------------------- 1 | def fetch_url(url): 2 | """Fetch content of a given url from the web""" 3 | import urllib.request 4 | response = urllib.request.urlopen(url) 5 | return response.read() 6 | 7 | 8 | def wait_until(predicate): 9 | """Waits until the given predicate returns True""" 10 | import time 11 | seconds = 0 12 | while not predicate(): 13 | print('Waiting...') 14 | time.sleep(1.0) 15 | seconds += 1 16 | print('Done!') 17 | return seconds 18 | 19 | 20 | from multiprocessing.pool import ThreadPool 21 | pool = ThreadPool(4) 22 | t1 = pool.apply_async(fetch_url, args=('https://httpbin.org/delay/3',)) 23 | t2 = pool.apply_async(wait_until, args=(t1.ready, )) 24 | pool.close() 25 | pool.join() 26 | print('Total Time:', t2.get()) 27 | print('Content:', t1.get()) 28 | -------------------------------------------------------------------------------- /Chapter11/web_05.py: -------------------------------------------------------------------------------- 1 | import string 2 | import cgi 3 | 4 | class HTMLFormatter(string.Formatter): 5 | def get_field(self, field_name, args, kwargs): 6 | val, key = super(HTMLFormatter, self).get_field(field_name, args, kwargs) 7 | if hasattr(val, '__html__'): 8 | val = val.__html__() 9 | elif isinstance(val, str): 10 | val = cgi.escape(val) 11 | return val, key 12 | 13 | 14 | class Markup: 15 | def __init__(self, v): 16 | self.v = v 17 | def __str__(self): 18 | return self.v 19 | def __html__(self): 20 | return str(self) 21 | 22 | 23 | html = HTMLFormatter().format('Hello {name}, you are {title}', 24 | name='Name', 25 | title=Markup('a developer')) 26 | print(html) -------------------------------------------------------------------------------- /Chapter09/concurrency_06.py: -------------------------------------------------------------------------------- 1 | import multiprocessing 2 | 3 | manager = multiprocessing.Manager() 4 | namespace = manager.Namespace() 5 | def set_first_variable(): 6 | namespace.first = 42 7 | p = multiprocessing.Process(target=set_first_variable) 8 | p.start() 9 | p.join() 10 | 11 | 12 | def set_second_variable(): 13 | namespace.second = dict(value=42) 14 | p = multiprocessing.Process(target=set_second_variable) 15 | p.start() 16 | p.join() 17 | 18 | 19 | import datetime 20 | def set_custom_variable(): 21 | namespace.last = datetime.datetime.utcnow() 22 | p = multiprocessing.Process(target=set_custom_variable) 23 | p.start() 24 | p.join() 25 | 26 | 27 | 28 | def print_variables(): 29 | print(namespace.first, namespace.second, namespace.last) 30 | p = multiprocessing.Process(target=print_variables) 31 | p.start() 32 | p.join() 33 | -------------------------------------------------------------------------------- /Chapter11/web_07.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import socketserver 3 | from http.server import SimpleHTTPRequestHandler, HTTPServer 4 | 5 | class HTTPDirectoryRequestHandler(SimpleHTTPRequestHandler): 6 | SERVED_DIRECTORY = '.' 7 | 8 | def translate_path(self, path): 9 | path = super().translate_path(path) 10 | relpath = os.path.relpath(path) 11 | return os.path.join(self.SERVED_DIRECTORY, relpath) 12 | 13 | 14 | class ThreadedHTTPServer(socketserver.ThreadingMixIn, HTTPServer): 15 | pass 16 | 17 | 18 | def serve_directory(path, port=8001): 19 | class ConfiguredHandler(HTTPDirectoryRequestHandler): 20 | SERVED_DIRECTORY = path 21 | httpd = ThreadedHTTPServer(("", port), ConfiguredHandler) 22 | print("serving on port", port) 23 | try: 24 | httpd.serve_forever() 25 | except KeyboardInterrupt: 26 | httpd.server_close() 27 | 28 | serve_directory('/tmp') -------------------------------------------------------------------------------- /Chapter05/datetimes_02.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | def parse_iso8601(strdate): 4 | date, time = strdate.split('T', 1) 5 | if '-' in time: 6 | time, tz = time.split('-') 7 | tz = '-' + tz 8 | elif '+' in time: 9 | time, tz = time.split('+') 10 | tz = '+' + tz 11 | elif 'Z' in time: 12 | time = time[:-1] 13 | tz = '+0000' 14 | date = date.replace('-', '') 15 | time = time.replace(':', '') 16 | tz = tz.replace(':', '') 17 | return datetime.datetime.strptime('{}T{}{}'.format(date, time, tz), 18 | "%Y%m%dT%H%M%S%z") 19 | 20 | print(parse_iso8601('2018-03-19T22:00Z')) 21 | print(parse_iso8601('2018-03-19T2200Z')) 22 | print(parse_iso8601('2018-03-19T22:00:03Z')) 23 | print(parse_iso8601('20180319T22:00:03Z')) 24 | print(parse_iso8601('20180319T22:00:03+05:00')) 25 | print(parse_iso8601('20180319T22:00:03+0500')) 26 | -------------------------------------------------------------------------------- /Chapter03/terminal_03.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import logging.config 3 | 4 | # OSX logs through /var/run/syslog this should be /dev/log 5 | # on Linux system or a tuple ('ADDRESS', PORT) to log to a remote server 6 | SYSLOG_ADDRESS = '/var/run/syslog' 7 | 8 | logging.config.dictConfig({ 9 | 'version': 1, 10 | 'formatters': { 11 | 'default': { 12 | 'format': '%(asctime)s %(name)s: %(levelname)s %(message)s' 13 | }, 14 | }, 15 | 'handlers': { 16 | 'syslog': { 17 | 'class': 'logging.handlers.SysLogHandler', 18 | 'formatter': 'default', 19 | 'address': SYSLOG_ADDRESS 20 | } 21 | }, 22 | 'root': { 23 | 'handlers': ['syslog'], 24 | 'level': 'INFO' 25 | } 26 | }) 27 | 28 | log = logging.getLogger() 29 | log.info('Hello Syslog!') 30 | 31 | # Try to read syslog output 32 | import os 33 | os.system('syslog | tail -n 2') -------------------------------------------------------------------------------- /Chapter11/web_11.py: -------------------------------------------------------------------------------- 1 | import time 2 | from http.cookies import SimpleCookie 3 | 4 | from web_06 import WSGIApplication 5 | 6 | app = WSGIApplication() 7 | 8 | @app.route('/') 9 | def index(req, resp): 10 | if 'HTTP_COOKIE' in req.environ: 11 | cookies = SimpleCookie(req.environ['HTTP_COOKIE']) 12 | if 'identity' in cookies: 13 | return b'Welcome back, %b' % cookies['identity'].value.encode('utf-8') 14 | return b'Visit /identity to get an identity' 15 | 16 | 17 | @app.route('/identity') 18 | def identity(req, resp): 19 | identity = int(time.time()) 20 | 21 | cookie = SimpleCookie() 22 | cookie['identity'] = 'USER: {}'.format(identity) 23 | 24 | for set_cookie in cookie.values(): 25 | resp.headers.add_header('Set-Cookie', set_cookie.OutputString()) 26 | return b'Go back to index to check your identity' 27 | 28 | 29 | app.serve() -------------------------------------------------------------------------------- /Chapter14/devtools_07.py: -------------------------------------------------------------------------------- 1 | import ast 2 | 3 | def run_python(code, mode='evalsafe'): 4 | if mode == 'evalsafe': 5 | return ast.literal_eval(code) 6 | elif mode == 'eval': 7 | return eval(compile(code, '', mode='eval')) 8 | elif mode == 'exec': 9 | return exec(compile(code, '', mode='exec')) 10 | else: 11 | raise ValueError('Unsupported execution model {}'.format(mode)) 12 | 13 | 14 | if __name__ == '__main__': 15 | print(run_python('[1, 2, 3]')) 16 | 17 | try: 18 | print(run_python('[1, 2, 3][0]')) 19 | except Exception as e: 20 | print(e) 21 | 22 | print(run_python('[1, 2, 3][0]', 'eval')) 23 | 24 | try: 25 | print(run_python(''' 26 | def x(): 27 | print("printing hello") 28 | x() 29 | ''', 'eval')) 30 | except Exception as e: 31 | print(e) 32 | 33 | print(run_python(''' 34 | def x(): 35 | print("printing hello") 36 | x() 37 | ''', 'exec')) -------------------------------------------------------------------------------- /Chapter06/io_10.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | 4 | with sqlite3.connect('/tmp/test.db') as db: 5 | try: 6 | db.execute('''CREATE TABLE people ( 7 | id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 8 | name TEXT, 9 | surname TEXT, 10 | language TEXT 11 | )''') 12 | except sqlite3.OperationalError: 13 | # Table already exists 14 | pass 15 | 16 | sql = 'INSERT INTO people (name, surname, language) VALUES (?, ?, ?)' 17 | db.execute(sql, ("Alessandro", "Molina", "Italian")) 18 | db.execute(sql, ("Mika", "Häkkinen", "Suomi")) 19 | db.execute(sql, ("Sebastian", "Vettel", "Deutsch")) 20 | 21 | with sqlite3.connect('/tmp/test.db') as db: 22 | db.row_factory = sqlite3.Row 23 | cursor = db.cursor() 24 | for row in cursor.execute('SELECT * FROM people WHERE language != :language', 25 | {'language': 'Italian'}): 26 | print(dict(row)) 27 | -------------------------------------------------------------------------------- /Chapter03/terminal_04.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import operator 3 | import logging 4 | import functools 5 | 6 | parser = argparse.ArgumentParser( 7 | description='Applies an operation to one or more numbers' 8 | ) 9 | parser.add_argument("number", 10 | help="One or more numbers to perform an operation on.", 11 | nargs='+', type=int) 12 | parser.add_argument('-o', '--operation', 13 | help="The operation to perform on numbers.", 14 | choices=['add', 'sub', 'mul', 'div'], default='add') 15 | parser.add_argument("-v", "--verbose", action="store_true", 16 | help="increase output verbosity") 17 | 18 | opts = parser.parse_args() 19 | 20 | logging.basicConfig(level=logging.INFO if opts.verbose else logging.WARNING) 21 | log = logging.getLogger() 22 | 23 | operation = getattr(operator, opts.operation) 24 | log.info('Applying %s to %s', opts.operation, opts.number) 25 | print(functools.reduce(operation, opts.number)) 26 | -------------------------------------------------------------------------------- /Chapter07/algorithms_08.py: -------------------------------------------------------------------------------- 1 | import operator 2 | 3 | operators = { 4 | '+': operator.add, 5 | '-': operator.sub, 6 | '*': operator.mul, 7 | '/': operator.truediv 8 | } 9 | 10 | def calculate(expression): 11 | parts = expression.split() 12 | 13 | try: 14 | result = int(parts[0]) 15 | except: 16 | raise ValueError('First argument of expression must be numberic') 17 | 18 | operator = None 19 | for part in parts[1:]: 20 | try: 21 | num = int(part) 22 | if operator is None: 23 | raise ValueError('No operator proviede for the numbers') 24 | except ValueError: 25 | if operator: 26 | raise ValueError('operator already provided') 27 | operator = operators[part] 28 | else: 29 | result = operator(result, num) 30 | operator = None 31 | 32 | return result 33 | 34 | print(calculate('5 + 3')) 35 | 36 | print(calculate('1 + 2 + 3')) 37 | 38 | print(calculate('3 * 2 + 4')) 39 | -------------------------------------------------------------------------------- /Chapter13/gui_02.py: -------------------------------------------------------------------------------- 1 | from tkinter import messagebox 2 | from tkinter import simpledialog 3 | from tkinter import filedialog 4 | 5 | 6 | def dialog(ask, title, message=None, **kwargs): 7 | for widget in (messagebox, simpledialog, filedialog): 8 | show = getattr(widget, 'ask{}'.format(ask), None) 9 | if show: 10 | break 11 | else: 12 | raise ValueError('Unsupported type of dialog: {}'.format(ask)) 13 | 14 | options = dict(kwargs, title=title) 15 | for arg, replacement in dialog.argsmap.get(widget, {}).items(): 16 | options[replacement] = locals()[arg] 17 | return show(**options) 18 | dialog.argsmap = { 19 | messagebox: {'message': 'message'}, 20 | simpledialog: {'message': 'prompt'} 21 | } 22 | 23 | 24 | if __name__ == '__main__': 25 | from tkinter import Tk 26 | 27 | Tk().withdraw() 28 | for ask in ('okcancel', 'retrycancel', 'yesno', 'yesnocancel', 29 | 'string', 'integer', 'float', 'directory', 'openfilename'): 30 | choice = dialog(ask, 'This is title', 'What?') 31 | print('{}: {}'.format(ask, choice)) -------------------------------------------------------------------------------- /Chapter14/devtools_06.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | 4 | def inspect_object(o): 5 | if inspect.isfunction(o) or inspect.ismethod(o): 6 | print('FUNCTION, arguments:', inspect.signature(o)) 7 | elif inspect.isclass(o): 8 | print('CLASS, methods:', inspect.getmembers(o, inspect.isfunction)) 9 | else: 10 | print('OBJECT ({}): {}'.format( 11 | o.__class__, 12 | [(n, v) for n, v in inspect.getmembers(o) if not n.startswith('__')] 13 | )) 14 | 15 | 16 | 17 | class MyClass: 18 | def __init__(self): 19 | self.value = 5 20 | 21 | def sum_to_value(self, other): 22 | return self.value + other 23 | 24 | inspect_object(MyClass.sum_to_value) 25 | # FUNCTION, arguments: (self, other) 26 | 27 | inspect_object(MyClass()) 28 | # OBJECT (): [('sum_to_value', >), ('value', 5)] 29 | 30 | inspect_object(MyClass) 31 | # CLASS, methods: [('__init__', ), ('sum_to_value', )] -------------------------------------------------------------------------------- /extract_code.py: -------------------------------------------------------------------------------- 1 | """Extracts code snippets from rst files of each chapter""" 2 | import sys 3 | import os 4 | from docutils.core import publish_doctree 5 | 6 | with open(sys.argv[1]) as f: 7 | doctree = publish_doctree(f.read()) 8 | 9 | count = 0 10 | titles = doctree.traverse(condition=lambda node: node.tagname == 'title') 11 | for idx, title in enumerate(titles): 12 | if title.astext() != 'How to do it': 13 | continue 14 | 15 | count += 1 16 | section = title.parent 17 | code_blocks = section.traverse(condition=lambda node: node.tagname == 'literal_block') 18 | source_code = '\n'.join([block.astext() for block in code_blocks]) 19 | 20 | chapter_name = os.path.basename(sys.argv[1]).split('.')[0] 21 | source_file = '%s/%s_%02d.py' % (chapter_name, chapter_name, count) 22 | if os.path.exists(source_file): 23 | continue 24 | 25 | try: 26 | os.makedirs(chapter_name) 27 | except: 28 | pass 29 | 30 | with open(source_file, 'w') as s: 31 | s.write(source_code) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 | -------------------------------------------------------------------------------- /Chapter04/filesdirs_09.py: -------------------------------------------------------------------------------- 1 | import tempfile, os 2 | 3 | 4 | class safe_open(object): 5 | def __init__(self, path, mode='w+b'): 6 | self._target = path 7 | self._mode = mode 8 | 9 | def __enter__(self): 10 | self._file = tempfile.NamedTemporaryFile(self._mode, delete=False) 11 | return self._file 12 | 13 | def __exit__(self, exc_type, exc_value, traceback): 14 | self._file.close() 15 | if exc_type is None: 16 | os.rename(self._file.name, self._target) 17 | else: 18 | os.unlink(self._file.name) 19 | 20 | 21 | with safe_open('/tmp/myfile') as f: 22 | f.write(b'Hello World') 23 | print(open('/tmp/myfile').read()) 24 | 25 | with open('/tmp/myfile', 'wb+') as f: 26 | f.write(b'Replace the hello world, ') 27 | raise Exception('but crash meanwhile!') 28 | f.write(b'expect to write some more') 29 | print(open('/tmp/myfile').read()) 30 | 31 | with safe_open('/tmp/myfile') as f: 32 | f.write(b'Replace the hello world, ') 33 | raise Exception('but crash meanwhile!') 34 | f.write(b'expect to write some more') 35 | print(open('/tmp/myfile').read()) 36 | -------------------------------------------------------------------------------- /Chapter13/gui_05.py: -------------------------------------------------------------------------------- 1 | import tkinter 2 | 3 | 4 | def set_menu(window, choices): 5 | menubar = tkinter.Menu(root) 6 | window.config(menu=menubar) 7 | 8 | def _set_choices(menu, choices): 9 | for label, command in choices.items(): 10 | if isinstance(command, dict): 11 | # Submenu 12 | submenu = tkinter.Menu(menu) 13 | menu.add_cascade(label=label, menu=submenu) 14 | _set_choices(submenu, command) 15 | elif label == '-' and command == '-': 16 | # Separator 17 | menu.add_separator() 18 | else: 19 | # Simple choice 20 | menu.add_command(label=label, command=command) 21 | 22 | _set_choices(menubar, choices) 23 | 24 | 25 | if __name__ == '__main__': 26 | import sys 27 | root = tkinter.Tk() 28 | 29 | from collections import OrderedDict 30 | set_menu(root, { 31 | 'File': OrderedDict([ 32 | ('Open', lambda: print('Open!')), 33 | ('Save', lambda: print('Save')), 34 | ('-', '-'), 35 | ('Quit', lambda: sys.exit(0)) 36 | ]) 37 | }) 38 | root.mainloop() -------------------------------------------------------------------------------- /Chapter11/web_09.py: -------------------------------------------------------------------------------- 1 | import cgi 2 | 3 | from web_06 import WSGIApplication 4 | import base64 5 | 6 | app = WSGIApplication() 7 | 8 | @app.route('/') 9 | def index(req, resp): 10 | return ( 11 | b'
' 12 | b' ' 13 | b' ' 14 | b'
' 15 | ) 16 | 17 | @app.route('/upload') 18 | def upload(req, resp): 19 | form = cgi.FieldStorage(fp=req.environ['wsgi.input'], 20 | environ=req.environ) 21 | if 'uploadedfile' not in form: 22 | return b'Nothing uploaded' 23 | 24 | uploadedfile = form['uploadedfile'] 25 | if uploadedfile.type.startswith('image'): 26 | # User uploaded an image, show it 27 | return b'' % ( 28 | uploadedfile.type.encode('ascii'), 29 | base64.b64encode(uploadedfile.file.read()) 30 | ) 31 | elif uploadedfile.type.startswith('text'): 32 | return uploadedfile.file.read() 33 | else: 34 | return b'You uploaded %b' % uploadedfile.filename.encode('utf-8') 35 | 36 | 37 | app.serve() -------------------------------------------------------------------------------- /Chapter08/crypto_02.py: -------------------------------------------------------------------------------- 1 | import hashlib, binascii, os 2 | 3 | def hash_password(password): 4 | """Hash a password for storing.""" 5 | salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') 6 | pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'), 7 | salt, 100000) 8 | pwdhash = binascii.hexlify(pwdhash) 9 | return (salt + pwdhash).decode('ascii') 10 | 11 | 12 | def verify_password(stored_password, provided_password): 13 | """Verify a stored password against one provided by user""" 14 | salt = stored_password[:64] 15 | stored_password = stored_password[64:] 16 | pwdhash = hashlib.pbkdf2_hmac('sha512', 17 | provided_password.encode('utf-8'), 18 | salt.encode('ascii'), 19 | 100000) 20 | pwdhash = binascii.hexlify(pwdhash).decode('ascii') 21 | return pwdhash == stored_password 22 | 23 | 24 | stored_password = hash_password('ThisIsAPassWord') 25 | print(stored_password) 26 | 27 | print( 28 | verify_password(stored_password, 'ThisIsAPassWord') 29 | ) 30 | 31 | print( 32 | verify_password(stored_password, 'WrongPassword') 33 | ) 34 | -------------------------------------------------------------------------------- /Chapter08/crypto_04.py: -------------------------------------------------------------------------------- 1 | import hashlib, hmac, time 2 | 3 | def compute_signature(message, secret): 4 | message = message.encode('utf-8') 5 | timestamp = str(int(time.time()*100)).encode('ascii') 6 | 7 | hashdata = message + timestamp 8 | signature = hmac.new(secret.encode('ascii'), 9 | hashdata, 10 | hashlib.sha256).hexdigest() 11 | return { 12 | 'message': message, 13 | 'signature': signature, 14 | 'timestamp': timestamp 15 | } 16 | 17 | 18 | def verify_signature(signed_message, secret): 19 | timestamp = signed_message['timestamp'] 20 | expected_signature = signed_message['signature'] 21 | message = signed_message['message'] 22 | 23 | hashdata = message + timestamp 24 | signature = hmac.new(secret.encode('ascii'), 25 | hashdata, 26 | hashlib.sha256).hexdigest() 27 | return signature == expected_signature 28 | 29 | signed_msg = compute_signature('Hello World', 'very_secret') 30 | print( 31 | verify_signature(signed_msg, 'very_secret') 32 | ) 33 | 34 | 35 | signed_msg['message'] = b'Hello Boat' 36 | print( 37 | verify_signature(signed_msg, 'very_secret') 38 | ) 39 | -------------------------------------------------------------------------------- /Chapter13/gui_04.py: -------------------------------------------------------------------------------- 1 | import tkinter 2 | from tkinter import simpledialog 3 | 4 | 5 | class ChoiceDialog(simpledialog.Dialog): 6 | def __init__(self, parent, title, text, items): 7 | self.selection = None 8 | self._items = items 9 | self._text = text 10 | super().__init__(parent, title=title) 11 | 12 | def body(self, parent): 13 | self._message = tkinter.Message(parent, text=self._text, aspect=400) 14 | self._message.pack(expand=1, fill=tkinter.BOTH) 15 | self._list = tkinter.Listbox(parent) 16 | self._list.pack(expand=1, fill=tkinter.BOTH, side=tkinter.TOP) 17 | for item in self._items: 18 | self._list.insert(tkinter.END, item) 19 | return self._list 20 | 21 | def validate(self): 22 | if not self._list.curselection(): 23 | return 0 24 | return 1 25 | 26 | def apply(self): 27 | self.selection = self._items[self._list.curselection()[0]] 28 | 29 | 30 | 31 | if __name__ == '__main__': 32 | tk = tkinter.Tk() 33 | tk.withdraw() 34 | 35 | dialog = ChoiceDialog(tk, 'Pick one', 36 | text='Please, pick a choice?', 37 | items=['first', 'second', 'third']) 38 | print('Selected "{}"'.format(dialog.selection)) -------------------------------------------------------------------------------- /Chapter14/devtools_08.py: -------------------------------------------------------------------------------- 1 | def function(should_print=False): 2 | a = 1 3 | b = 2 4 | if should_print: 5 | print('Usually does not execute!') 6 | return a + b 7 | 8 | 9 | import trace 10 | import collections 11 | 12 | 13 | def report_tracing(func, *args, **kwargs): 14 | outputs = collections.defaultdict(list) 15 | 16 | tracing = trace.Trace(trace=False) 17 | tracing.runfunc(func, *args, **kwargs) 18 | 19 | traced = collections.defaultdict(set) 20 | for filename, line in tracing.results().counts: 21 | traced[filename].add(line) 22 | 23 | for filename, tracedlines in traced.items(): 24 | with open(filename) as f: 25 | for idx, fileline in enumerate(f, start=1): 26 | outputs[filename].append((idx, idx in tracedlines, fileline)) 27 | 28 | return outputs 29 | 30 | 31 | def print_traced_execution(tracings): 32 | for filename, tracing in tracings.items(): 33 | print(filename) 34 | for idx, executed, content in tracing: 35 | print('{:04d}{} {}'.format(idx, 36 | '+' if executed else ' ', 37 | content), 38 | end='') 39 | print() 40 | 41 | 42 | print_traced_execution( 43 | report_tracing(function) 44 | ) 45 | 46 | print_traced_execution( 47 | report_tracing(function, True) 48 | ) -------------------------------------------------------------------------------- /Chapter11/web_03.py: -------------------------------------------------------------------------------- 1 | import urllib.request 2 | import urllib.parse 3 | import json 4 | 5 | 6 | def http_request(url, query=None, method=None, headers={}, data=None): 7 | """Perform an HTTP request and return the associated response.""" 8 | parts = vars(urllib.parse.urlparse(url)) 9 | if query: 10 | parts['query'] = urllib.parse.urlencode(query) 11 | 12 | url = urllib.parse.ParseResult(**parts).geturl() 13 | r = urllib.request.Request(url=url, method=method, headers=headers, 14 | data=data) 15 | with urllib.request.urlopen(r) as resp: 16 | msg, resp = resp.info(), resp.read() 17 | 18 | if msg.get_content_type() == 'application/json': 19 | resp = json.loads(resp.decode('utf-8')) 20 | 21 | return msg, resp 22 | 23 | 24 | if __name__ == '__main__': 25 | msg, resp = http_request( 26 | 'https://httpbin.org/get', 27 | query={ 28 | 'a': 'Hello', 29 | 'b': 'World' 30 | } 31 | ) 32 | print(msg.get_content_type(), resp) 33 | 34 | msg, resp = http_request('https://httpbin.org/bytes/16') 35 | print(msg.get_content_type(), resp) 36 | 37 | msg, resp = http_request('https://httpbin.org/post', method='POST', 38 | data='This is my posted data!'.encode('ascii'), 39 | headers={'Content-Type': 'text/plain'}) 40 | print(msg.get_content_type(), resp) -------------------------------------------------------------------------------- /Chapter09/concurrency_04.py: -------------------------------------------------------------------------------- 1 | import concurrent.futures 2 | import urllib.request 3 | import time 4 | 5 | def benchmark_url(url): 6 | begin = time.time() 7 | with urllib.request.urlopen(url) as conn: 8 | conn.read() 9 | return (time.time() - begin, url) 10 | 11 | class UrlsBenchmarker(object): 12 | def __init__(self, urls): 13 | self._urls = urls 14 | 15 | def run(self, executor): 16 | futures = self._benchmark_urls(executor) 17 | fastest = min([ 18 | future.result() for future in 19 | concurrent.futures.as_completed(futures) 20 | ]) 21 | print('Fastest Url: {1}, in {0}'.format(*fastest)) 22 | 23 | def _benchmark_urls(self, executor): 24 | futures = [] 25 | for url in self._urls: 26 | future = executor.submit(benchmark_url, url) 27 | future.add_done_callback(self._print_timing) 28 | futures.append(future) 29 | return futures 30 | 31 | def _print_timing(self, future): 32 | print('Url {1} downloaded in {0}'.format( 33 | *future.result() 34 | )) 35 | 36 | 37 | import concurrent.futures 38 | with concurrent.futures.ThreadPoolExecutor() as executor: 39 | UrlsBenchmarker([ 40 | 'http://time.com/', 41 | 'http://www.cnn.com/', 42 | 'http://www.facebook.com/', 43 | 'http://www.apple.com/', 44 | ]).run(executor) 45 | -------------------------------------------------------------------------------- /Chapter10/networking_06.py: -------------------------------------------------------------------------------- 1 | import xmlrpc.server 2 | 3 | 4 | class XMLRPCServices: 5 | class ExposedServices: 6 | pass 7 | 8 | def __init__(self, **services): 9 | self.services = self.ExposedServices() 10 | for name, service in services.items(): 11 | setattr(self.services, name, service) 12 | 13 | def serve(self, host='localhost', port=8000): 14 | print('Serving XML-RPC on {}:{}'.format(host, port)) 15 | self.server = xmlrpc.server.SimpleXMLRPCServer((host, port)) 16 | self.server.register_introspection_functions() 17 | self.server.register_instance(self.services, 18 | allow_dotted_names=True) 19 | self.server.serve_forever() 20 | 21 | def stop(self): 22 | self.server.shutdown() 23 | self.server.server_close() 24 | 25 | 26 | class MathServices: 27 | def double(self, v): 28 | return v**2 29 | 30 | class TimeServices: 31 | def currentTime(self): 32 | import datetime 33 | return datetime.datetime.utcnow() 34 | 35 | xmlrpcserver = XMLRPCServices(math=MathServices(), 36 | time=TimeServices()) 37 | 38 | import threading 39 | server_thread = threading.Thread(target=xmlrpcserver.serve) 40 | server_thread.start() 41 | 42 | from xmlrpc.client import ServerProxy 43 | client = ServerProxy("http://localhost:8000") 44 | print( 45 | client.time.currentTime() 46 | ) 47 | 48 | xmlrpcserver.stop() 49 | server_thread.join() -------------------------------------------------------------------------------- /Chapter14/devtools_04.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import logging.handlers 3 | import functools 4 | 5 | crashlogger = logging.getLogger('__crashes__') 6 | 7 | def configure_crashreport(mailhost, fromaddr, toaddrs, subject, 8 | credentials, tls=False): 9 | if configure_crashreport._configured: 10 | return 11 | 12 | crashlogger.addHandler( 13 | logging.handlers.SMTPHandler( 14 | mailhost=mailhost, 15 | fromaddr=fromaddr, 16 | toaddrs=toaddrs, 17 | subject=subject, 18 | credentials=credentials, 19 | secure=tuple() if tls else None 20 | ) 21 | ) 22 | configure_crashreport._configured = True 23 | configure_crashreport._configured = False 24 | 25 | 26 | def crashreport(f): 27 | @functools.wraps(f) 28 | def _crashreport(*args, **kwargs): 29 | try: 30 | return f(*args, **kwargs) 31 | except Exception as e: 32 | crashlogger.exception( 33 | '{} crashed\n'.format(f.__name__) 34 | ) 35 | raise 36 | return _crashreport 37 | 38 | 39 | if __name__ == '__main__': 40 | @crashreport 41 | def main(): 42 | 3 / 0 43 | 44 | configure_crashreport( 45 | 'your-smtp-host.com', 46 | 'no-reply@your-smtp-host.com', 47 | 'crashes_receiver@another-smtp-host.com', 48 | 'Automatic Crash Report from TestApp', 49 | ('smtpserver_username', 'smtpserver_password'), 50 | tls=True 51 | ) 52 | main() -------------------------------------------------------------------------------- /Chapter02/text_03.py: -------------------------------------------------------------------------------- 1 | dictionary = {'ability', 'able', 'about', 'above', 'accept', 'according', 2 | 'account', 'across', 'act', 'action', 'activity', 'actually', 3 | 'add', 'address', 'administration', 'admit', 'adult', 'affect', 4 | 'after', 'again', 'against', 'age', 'agency', 'agent', 'ago', 5 | 'agree', 'agreement', 'ahead', 'air', 'all', 'allow', 'almost', 6 | 'alone', 'along', 'already', 'also', 'although', 'always', 7 | 'American', 'among', 'amount', 'analysis', 'and', 'animal', 8 | 'another', 'answer', 'any', 'anyone', 'anything', 'appear', 9 | 'apply', 'approach', 'area', 'argue', 'arm', 'around', 'arrive', 10 | 'art', 'article', 'artist', 'as', 'ask', 'assume', 'at', 'attack', 11 | 'attention', 'attorney', 'audience', 'author', 'authority', 12 | 'available', 'avoid', 'away', 'baby', 'back', 'bad', 'bag', 13 | 'ball', 'bank', 'bar', 'base', 'be', 'beat', 'beautiful', 14 | 'because', 'become'} 15 | import difflib 16 | 17 | def suggest(phrase): 18 | changes = 0 19 | words = phrase.split() 20 | for idx, w in enumerate(words): 21 | if w not in dictionary: 22 | changes += 1 23 | matches = difflib.get_close_matches(w, dictionary) 24 | if matches: 25 | words[idx] = matches[0] 26 | return changes, ' '.join(words) 27 | 28 | 29 | print(suggest('assume ani answer')) 30 | print(suggest('anoter agrement ahead')) 31 | print(suggest('beautiful art')) 32 | -------------------------------------------------------------------------------- /Chapter09/concurrency_05.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import sched 3 | import functools 4 | 5 | 6 | class BackgroundScheduler(threading.Thread): 7 | def __init__(self, start=True): 8 | self._scheduler = sched.scheduler() 9 | self._running = True 10 | super().__init__(daemon=True) 11 | if start: 12 | self.start() 13 | 14 | def run_at(self, time, action, args=None, kwargs=None): 15 | self._scheduler.enterabs(time, 0, action, 16 | argument=args or tuple(), 17 | kwargs=kwargs or {}) 18 | 19 | def run_after(self, delay, action, args=None, kwargs=None): 20 | self._scheduler.enter(delay, 0, action, 21 | argument=args or tuple(), 22 | kwargs=kwargs or {}) 23 | 24 | def run_every(self, seconds, action, args=None, kwargs=None): 25 | @functools.wraps(action) 26 | def _f(*args, **kwargs): 27 | try: 28 | action(*args, **kwargs) 29 | finally: 30 | self.run_after(seconds, _f, args=args, kwargs=kwargs) 31 | self.run_after(seconds, _f, args=args, kwargs=kwargs) 32 | 33 | def run(self): 34 | while self._running: 35 | delta = self._scheduler.run(blocking=False) 36 | if delta is None: 37 | delta = 0.5 38 | self._scheduler.delayfunc(min(delta, 0.5)) 39 | 40 | def stop(self): 41 | self._running = False 42 | 43 | import time 44 | s = BackgroundScheduler() 45 | s.run_every(2, lambda: print('Hello World')) 46 | time.sleep(5) 47 | s.stop() 48 | s.join() -------------------------------------------------------------------------------- /Chapter13/gui_03.py: -------------------------------------------------------------------------------- 1 | import tkinter 2 | from tkinter import simpledialog 3 | from tkinter import ttk 4 | 5 | from queue import Queue 6 | 7 | class ProgressDialog(simpledialog.SimpleDialog): 8 | def __init__(self, master, text='', title=None, class_=None): 9 | super().__init__(master=master, text=text, title=title, class_=class_) 10 | self.default = None 11 | self.cancel = None 12 | 13 | self._queue = Queue() 14 | self._bar = ttk.Progressbar(self.root, orient="horizontal", 15 | length=200, mode="determinate") 16 | self._bar.pack(expand=True, fill=tkinter.X, side=tkinter.BOTTOM) 17 | self.root.attributes("-topmost", True) 18 | self.root.after(200, self._update) 19 | 20 | def set_progress(self, value): 21 | self._queue.put(value) 22 | 23 | def _update(self): 24 | while self._queue.qsize(): 25 | try: 26 | self._bar['value'] = self._queue.get(0) 27 | except Queue.Empty: 28 | pass 29 | self.root.after(200, self._update) 30 | 31 | 32 | 33 | 34 | if __name__ == '__main__': 35 | root = tkinter.Tk() 36 | root.withdraw() 37 | 38 | p = ProgressDialog(master=root, text='Downloading Something...', 39 | title='Download') 40 | 41 | import threading 42 | def _do_progress(): 43 | import time 44 | for i in range(1, 11): 45 | time.sleep(0.5) 46 | p.set_progress(i*10) 47 | p.done(0) 48 | t = threading.Thread(target=_do_progress) 49 | t.start() 50 | 51 | p.go() 52 | print('Download Completed!') -------------------------------------------------------------------------------- /Chapter06/io_07.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | from contextlib import contextmanager 3 | 4 | 5 | class XMLDocument(object): 6 | def __init__(self, root='document', mode='xml'): 7 | self._root = ET.Element(root) 8 | self._mode = mode 9 | 10 | def __str__(self): 11 | return ET.tostring(self._root, encoding='unicode', method=self._mode) 12 | 13 | def write(self, fobj): 14 | ET.ElementTree(self._root).write(fobj) 15 | 16 | def __enter__(self): 17 | return XMLDocumentBuilder(self._root) 18 | 19 | def __exit__(self, exc_type, value, traceback): 20 | return 21 | 22 | 23 | class XMLDocumentBuilder(object): 24 | def __init__(self, root): 25 | self._current = [root] 26 | 27 | def tag(self, *args, **kwargs): 28 | el = ET.Element(*args, **kwargs) 29 | self._current[-1].append(el) 30 | @contextmanager 31 | def _context(): 32 | self._current.append(el) 33 | try: 34 | yield el 35 | finally: 36 | self._current.pop() 37 | return _context() 38 | 39 | def text(self, text): 40 | if self._current[-1].text is None: 41 | self._current[-1].text = '' 42 | self._current[-1].text += text 43 | doc = XMLDocument('html', mode='html') 44 | 45 | with doc as _: 46 | with _.tag('head'): 47 | with _.tag('title'): _.text('This is the title') 48 | with _.tag('body'): 49 | with _.tag('div', id='main-div'): 50 | with _.tag('h1'): _.text('My Document') 51 | with _.tag('strong'): _.text('Hello World') 52 | _.tag('img', src='http://via.placeholder.com/150x150') 53 | 54 | print(doc) 55 | -------------------------------------------------------------------------------- /Chapter11/web_08.py: -------------------------------------------------------------------------------- 1 | from web_06 import WSGIApplication 2 | from wsgiref.simple_server import make_server 3 | import cgitb 4 | import sys 5 | 6 | app = WSGIApplication() 7 | 8 | @app.route('/crash') 9 | def crash(req, resp): 10 | raise RuntimeError('This is a crash!') 11 | 12 | 13 | class ErrorMiddleware: 14 | """Wrap a WSGI application to display errors in the browser""" 15 | def __init__(self, app): 16 | self.app = app 17 | 18 | def __call__(self, environ, start_response): 19 | app_iter = None 20 | try: 21 | app_iter = self.app(environ, start_response) 22 | for item in app_iter: 23 | yield item 24 | except: 25 | try: 26 | start_response('500 INTERNAL SERVER ERROR', [ 27 | ('Content-Type', 'text/html; charset=utf-8'), 28 | ('X-XSS-Protection', '0'), 29 | ]) 30 | except Exception: 31 | # There has been output but an error occurred later on. 32 | # In that situation we can do nothing fancy anymore, 33 | # better log something into the error log and fallback. 34 | environ['wsgi.errors'].write( 35 | 'Debugging middleware caught exception in streamed ' 36 | 'response after response headers were already sent.\n' 37 | ) 38 | else: 39 | yield cgitb.html(sys.exc_info()).encode('utf-8') 40 | finally: 41 | if hasattr(app_iter, 'close'): 42 | app_iter.close() 43 | 44 | 45 | httpd = make_server('', 8000, ErrorMiddleware(app)) 46 | print("Serving on port 8000...") 47 | httpd.serve_forever() -------------------------------------------------------------------------------- /Chapter03/terminal_08.py: -------------------------------------------------------------------------------- 1 | import shutil, sys 2 | 3 | def withprogressbar(func): 4 | """Decorates ``func`` to display a progress bar while running. 5 | 6 | The decorated function can yield values from 0 to 100 to 7 | display the progress. 8 | """ 9 | def _func_with_progress(*args, **kwargs): 10 | max_width, _ = shutil.get_terminal_size() 11 | 12 | gen = func(*args, **kwargs) 13 | while True: 14 | try: 15 | progress = next(gen) 16 | except StopIteration as exc: 17 | sys.stdout.write('\n') 18 | return exc.value 19 | else: 20 | # Build the displayed message so we can compute 21 | # how much space is left for the progress bar itself. 22 | message = '[%s] {}%%'.format(progress) 23 | bar_width = max_width - len(message) + 3 # Add 3 characters to cope for the %s and %% 24 | 25 | filled = int(round(bar_width / 100.0 * progress)) 26 | spaceleft = bar_width - filled 27 | bar = '=' * filled + ' ' * spaceleft 28 | sys.stdout.write((message+'\r') % bar) 29 | sys.stdout.flush() 30 | 31 | return _func_with_progress 32 | import time 33 | 34 | @withprogressbar 35 | def wait(seconds): 36 | """Waits ``seconds`` seconds and returns how long it waited.""" 37 | start = time.time() 38 | step = seconds / 100.0 39 | for i in range(1, 101): 40 | time.sleep(step) 41 | yield i # Send % of progress to withprogressbar 42 | 43 | # Return how much time passed since we started, 44 | # which is in fact how long we waited for real. 45 | return time.time() - start 46 | 47 | print('WAITED', wait(5)) 48 | -------------------------------------------------------------------------------- /Chapter03/terminal_10.py: -------------------------------------------------------------------------------- 1 | import curses 2 | from curses.textpad import Textbox, rectangle 3 | 4 | 5 | class TextInput(object): 6 | @classmethod 7 | def show(cls, message, content=None): 8 | return curses.wrapper(cls(message, content)._show) 9 | 10 | def __init__(self, message, content): 11 | self._message = message 12 | self._content = content 13 | 14 | def _show(self, stdscr): 15 | # Set a reasonable size for our input box. 16 | lines, cols = curses.LINES - 10, curses.COLS - 40 17 | 18 | y_begin, x_begin = (curses.LINES - lines) // 2, (curses.COLS - cols) // 2 19 | editwin = curses.newwin(lines, cols, y_begin, x_begin) 20 | editwin.addstr(0, 1, "{}: (hit Ctrl-G to submit)".format(self._message)) 21 | rectangle(editwin, 1, 0, lines-2, cols-1) 22 | editwin.refresh() 23 | 24 | inputwin = curses.newwin(lines-4, cols-2, y_begin+2, x_begin+1) 25 | box = Textbox(inputwin) 26 | self._load(box, self._content) 27 | return self._edit(box) 28 | 29 | def _load(self, box, text): 30 | if not text: 31 | return 32 | for c in text: 33 | box._insert_printable_char(c) 34 | 35 | def _edit(self, box): 36 | while True: 37 | ch = box.win.getch() 38 | if not ch: 39 | continue 40 | if ch == 127: 41 | ch = curses.KEY_BACKSPACE 42 | if not box.do_command(ch): 43 | break 44 | box.win.refresh() 45 | return box.gather() 46 | 47 | result = TextInput.show('Insert your name:') 48 | print('Your name:', result) 49 | result = TextInput.show('Insert your name:', 50 | content='Some Text\nTo be edited') 51 | print('Your name:', result) -------------------------------------------------------------------------------- /Chapter10/networking_01.py: -------------------------------------------------------------------------------- 1 | from email.header import Header 2 | from email.mime.text import MIMEText 3 | from email.utils import parseaddr, formataddr 4 | from smtplib import SMTP 5 | 6 | 7 | class EmailSender(object): 8 | def __init__(self, host="localhost", port=25, login="", password=""): 9 | self._host = host 10 | self._port = int(port) 11 | self._login = login 12 | self._password = password 13 | 14 | def send(self, sender, recipient, subject, body): 15 | header_charset = 'UTF-8' 16 | body_charset = 'UTF-8' 17 | 18 | sender_name, sender_addr = parseaddr(sender) 19 | recipient_name, recipient_addr = parseaddr(recipient) 20 | 21 | sender_name = str(Header(sender_name, header_charset)) 22 | recipient_name = str(Header(recipient_name, header_charset)) 23 | 24 | msg = MIMEText(body.encode(body_charset), 'plain', body_charset) 25 | msg['From'] = formataddr((sender_name, sender_addr)) 26 | msg['To'] = formataddr((recipient_name, recipient_addr)) 27 | msg['Subject'] = Header(subject, header_charset) 28 | 29 | smtp = SMTP(self._host, self._port) 30 | try: 31 | smtp.starttls() 32 | except: 33 | pass 34 | smtp.login(self._login, self._password) 35 | smtp.sendmail(sender, recipient, msg.as_string()) 36 | smtp.quit() 37 | 38 | 39 | es = EmailSender('mail.myserver.it', 40 | login=EMAIL_ADDRESS, 41 | password=PASSWORD) 42 | es.send(sender='Sender ', 43 | recipient=EMAIL_ADDRESS, 44 | subject='Hello my friend!', 45 | body='''Here is a little email for you''') -------------------------------------------------------------------------------- /Chapter06/io_08.py: -------------------------------------------------------------------------------- 1 | import xml.etree.ElementTree as ET 2 | from html.parser import HTMLParser 3 | 4 | 5 | class ETHTMLParser(HTMLParser): 6 | SELF_CLOSING = {'br', 'img', 'area', 'base', 'col', 'command', 'embed', 'hr', 7 | 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 8 | 'source', 'track', 'wbr'} 9 | 10 | def __init__(self, *args, **kwargs): 11 | super(ETHTMLParser, self).__init__(*args, **kwargs) 12 | self._builder = ET.TreeBuilder() 13 | self._stack = [] 14 | 15 | @property 16 | def _last_tag(self): 17 | return self._stack[-1] if self._stack else None 18 | 19 | def _handle_selfclosing(self): 20 | last_tag = self._last_tag 21 | if last_tag in self.SELF_CLOSING: 22 | self.handle_endtag(last_tag) 23 | 24 | def handle_starttag(self, tag, attrs): 25 | self._handle_selfclosing() 26 | self._stack.append(tag) 27 | self._builder.start(tag, dict(attrs)) 28 | 29 | def handle_endtag(self, tag): 30 | if tag != self._last_tag: 31 | self._handle_selfclosing() 32 | self._stack.pop() 33 | self._builder.end(tag) 34 | 35 | def handle_data(self, data): 36 | self._handle_selfclosing() 37 | self._builder.data(data) 38 | 39 | def close(self): 40 | return self._builder.close() 41 | 42 | text = ''' 43 | 44 | 45 |

hi

46 |
47 | 48 | 49 | 50 | ''' 51 | 52 | parser = ETHTMLParser() 53 | parser.feed(text) 54 | root = parser.close() 55 | 56 | print(ET.tostring(root, encoding='unicode')) 57 | 58 | def print_node(el, depth=0): 59 | print(' '*depth, el) 60 | for child in el: 61 | print_node(child, depth + 1) 62 | print_node(root) 63 | -------------------------------------------------------------------------------- /Chapter06/io_06.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | 3 | 4 | def read_config(config_text, schema=None): 5 | """Read options from ``config_text`` applying given ``schema``""" 6 | schema = schema or {} 7 | 8 | cfg = configparser.ConfigParser( 9 | interpolation=configparser.ExtendedInterpolation() 10 | ) 11 | try: 12 | cfg.read_string(config_text) 13 | except configparser.MissingSectionHeaderError: 14 | config_text = '[main]\n' + config_text 15 | cfg.read_string(config_text) 16 | 17 | config = {} 18 | for section in schema: 19 | options = config.setdefault(section, {}) 20 | for option, option_schema in schema[section].items(): 21 | options[option] = option_schema.get('default') 22 | for section in cfg.sections(): 23 | options = config.setdefault(section, {}) 24 | section_schema = schema.get(section, {}) 25 | for option in cfg.options(section): 26 | option_schema = section_schema.get(option, {}) 27 | getter = 'get' + option_schema.get('type', '') 28 | options[option] = getattr(cfg, getter)(section, option) 29 | return config 30 | 31 | 32 | config_text = ''' 33 | debug = true 34 | 35 | [registry] 36 | name = Alessandro 37 | surname = Molina 38 | 39 | [extra] 40 | likes = spicy food 41 | countrycode = 39 42 | ''' 43 | 44 | config = read_config(config_text, { 45 | 'main': { 46 | 'debug': {'type': 'boolean'} 47 | }, 48 | 'registry': { 49 | 'name': {'default': 'unknown'}, 50 | 'surname': {'default': 'unknown'}, 51 | 'middlename': {'default': ''}, 52 | }, 53 | 'extra': { 54 | 'countrycode': {'type': 'int'}, 55 | 'age': {'type': 'int', 'default': 0} 56 | }, 57 | 'more': { 58 | 'verbose': {'type': 'int', 'default': 0} 59 | } 60 | }) 61 | 62 | import pprint 63 | pprint.pprint(config) -------------------------------------------------------------------------------- /Chapter10/networking_03.py: -------------------------------------------------------------------------------- 1 | import ftplib 2 | 3 | 4 | class FTPCLient: 5 | def __init__(self, host, username='', password=''): 6 | self._client = ftplib.FTP_TLS(timeout=10) 7 | self._client.connect(host) 8 | 9 | # enable TLS 10 | try: 11 | self._client.auth() 12 | except ftplib.error_perm: 13 | # TLS authentication not supported 14 | # fallback to a plain FTP client 15 | self._client.close() 16 | self._client = ftplib.FTP(timeout=10) 17 | self._client.connect(host) 18 | 19 | self._client.login(username, password) 20 | 21 | if hasattr(self._client, 'prot_p'): 22 | self._client.prot_p() 23 | 24 | def cwd(self, directory): 25 | """Enter directory""" 26 | self._client.cwd(directory) 27 | 28 | def dir(self): 29 | """Returns list of files in current directory. 30 | 31 | Each entry is returned as a tuple of two elements, 32 | first element is the filename, the second are the 33 | properties of that file. 34 | """ 35 | entries = [] 36 | for idx, f in enumerate(self._client.mlsd()): 37 | if idx == 0: 38 | # First entry is current path 39 | continue 40 | if f[0] in ('..', '.'): 41 | continue 42 | entries.append(f) 43 | return entries 44 | 45 | def download(self, remotefile, localfile): 46 | """Download remotefile into localfile""" 47 | with open(localfile, 'wb') as f: 48 | self._client.retrbinary('RETR %s' % remotefile, f.write) 49 | 50 | def upload(self, localfile, remotefile): 51 | """Upload localfile to remotefile""" 52 | with open(localfile, 'rb') as f: 53 | self._client.storbinary('STOR %s' % remotefile, f) 54 | 55 | 56 | 57 | cli = FTPCLient('localhost', username=USERNAME, password=PASSWORD) 58 | print( 59 | cli.dir() 60 | ) 61 | with open('/tmp/hello.txt', 'w+') as f: 62 | f.write('Hello World!') 63 | 64 | cli.upload('/tmp/hello.txt', 'hellofile.txt') 65 | cli.download('hellofile.txt', '/tmp/hello2.txt') 66 | 67 | with open('/tmp/hello2.txt') as f: 68 | print(f.read()) -------------------------------------------------------------------------------- /Chapter12/multimedia_03.py: -------------------------------------------------------------------------------- 1 | import imghdr 2 | import struct 3 | import os 4 | from pathlib import Path 5 | 6 | 7 | class ImageReader: 8 | @classmethod 9 | def get_size(cls, f): 10 | requires_close = False 11 | if isinstance(f, (str, getattr(os, 'PathLike', str))): 12 | f = open(f, 'rb') 13 | requires_close = True 14 | elif isinstance(f, Path): 15 | f = f.expanduser().open('rb') 16 | requires_close = True 17 | 18 | try: 19 | image_type = imghdr.what(f) 20 | if image_type not in ('jpeg', 'png', 'gif'): 21 | raise ValueError('Unsupported image format') 22 | 23 | f.seek(0) 24 | size_reader = getattr(cls, '_size_{}'.format(image_type)) 25 | return size_reader(f) 26 | finally: 27 | if requires_close: f.close() 28 | 29 | @classmethod 30 | def _size_gif(cls, f): 31 | f.read(6) # Skip the Magick Numbers 32 | w, h = struct.unpack('I4s', f.read(8)) 39 | if ctype != b'IHDR': 40 | raise ValueError('Unsupported PNG format') 41 | w, h = struct.unpack('>II', f.read(8)) 42 | return w, h 43 | 44 | @classmethod 45 | def _size_jpeg(cls, f): 46 | start_of_image = f.read(2) 47 | if start_of_image != b'\xff\xd8': 48 | raise ValueError('Unsupported JPEG format') 49 | while True: 50 | marker, segment_size = struct.unpack('>2sH', f.read(4)) 51 | if marker[0] != 0xff: 52 | raise ValueError('Unsupported JPEG format') 53 | data = f.read(segment_size - 2) 54 | if not 0xc0 <= marker[1] <= 0xcf: 55 | continue 56 | _, h, w = struct.unpack('>cHH', data[:5]) 57 | break 58 | return w, h 59 | 60 | 61 | if __name__ == '__main__': 62 | import sys 63 | print( 64 | ImageReader.get_size(sys.argv[1]) 65 | ) 66 | print( 67 | ImageReader.get_size(open(sys.argv[1], 'rb')) 68 | ) 69 | print( 70 | ImageReader.get_size(Path(sys.argv[1])) 71 | ) -------------------------------------------------------------------------------- /Chapter10/networking_04.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import threading 3 | import socketserver 4 | 5 | 6 | class EchoServer: 7 | def __init__(self, host='0.0.0.0', port=9800): 8 | self._host = host 9 | self._port = port 10 | self._server = ThreadedTCPServer((host, port), EchoRequestHandler) 11 | self._thread = threading.Thread(target=self._server.serve_forever) 12 | self._thread.daemon = True 13 | 14 | def start(self): 15 | if self._thread.is_alive(): 16 | # Already serving 17 | return 18 | 19 | print('Serving on %s:%s' % (self._host, self._port)) 20 | self._thread.start() 21 | 22 | def stop(self): 23 | self._server.shutdown() 24 | self._server.server_close() 25 | 26 | 27 | class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): 28 | allow_reuse_address = True 29 | 30 | 31 | class EchoRequestHandler(socketserver.BaseRequestHandler): 32 | MAX_MESSAGE_SIZE = 2**16 # 65k 33 | MESSAGE_HEADER_LEN = len(str(MAX_MESSAGE_SIZE)) 34 | 35 | @classmethod 36 | def recv_message(cls, socket): 37 | data_size = int(socket.recv(cls.MESSAGE_HEADER_LEN)) 38 | data = socket.recv(data_size) 39 | return data 40 | 41 | @classmethod 42 | def prepare_message(cls, message): 43 | if len(message) > cls.MAX_MESSAGE_SIZE: 44 | raise ValueError('Message too big') 45 | 46 | message_size = str(len(message)).encode('ascii') 47 | message_size = message_size.zfill(cls.MESSAGE_HEADER_LEN) 48 | return message_size + message 49 | 50 | def handle(self): 51 | message = self.recv_message(self.request) 52 | self.request.sendall(self.prepare_message(b'ECHO: %s' % message)) 53 | 54 | 55 | 56 | 57 | def send_message_to_server(ip, port, message): 58 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 59 | sock.connect((ip, port)) 60 | try: 61 | message = EchoRequestHandler.prepare_message(message) 62 | sock.sendall(message) 63 | response = EchoRequestHandler.recv_message(sock) 64 | print("ANSWER: {}".format(response)) 65 | finally: 66 | sock.close() 67 | 68 | 69 | server = EchoServer() 70 | server.start() 71 | 72 | send_message_to_server('localhost', server._port, b"Hello World 1") 73 | send_message_to_server('localhost', server._port, b"Hello World 2") 74 | send_message_to_server('localhost', server._port, b"Hello World 3") 75 | 76 | server.stop() -------------------------------------------------------------------------------- /Chapter11/web_04.py: -------------------------------------------------------------------------------- 1 | import io 2 | import mimetypes 3 | import uuid 4 | 5 | 6 | class MultiPartForm: 7 | def __init__(self): 8 | self.fields = {} 9 | self.files = [] 10 | 11 | def __setitem__(self, name, value): 12 | self.fields[name] = value 13 | 14 | def add_file(self, field, filename, data, mimetype=None): 15 | if mimetype is None: 16 | mimetype = (mimetypes.guess_type(filename)[0] or 17 | 'application/octet-stream') 18 | self.files.append((field, filename, mimetype, data)) 19 | 20 | def _generate_bytes(self, boundary): 21 | buffer = io.BytesIO() 22 | for field, value in self.fields.items(): 23 | buffer.write(b'--' + boundary + b'\r\n') 24 | buffer.write('Content-Disposition: form-data; ' 25 | 'name="{}"\r\n'.format(field).encode('utf-8')) 26 | buffer.write(b'\r\n') 27 | buffer.write(value.encode('utf-8')) 28 | buffer.write(b'\r\n') 29 | for field, filename, f_content_type, body in self.files: 30 | buffer.write(b'--' + boundary + b'\r\n') 31 | buffer.write('Content-Disposition: file; ' 32 | 'name="{}"; filename="{}"\r\n'.format( 33 | field, filename 34 | ).encode('utf-8')) 35 | buffer.write('Content-Type: {}\r\n'.format( 36 | f_content_type 37 | ).encode('utf-8')) 38 | buffer.write(b'\r\n') 39 | buffer.write(body) 40 | buffer.write(b'\r\n') 41 | buffer.write(b'--' + boundary + b'--\r\n') 42 | return buffer.getvalue() 43 | 44 | def encode(self): 45 | boundary = uuid.uuid4().hex.encode('ascii') 46 | while boundary in self._generate_bytes(boundary=b'NOBOUNDARY'): 47 | boundary = uuid.uuid4().hex.encode('ascii') 48 | 49 | content_type = 'multipart/form-data; boundary={}'.format( 50 | boundary.decode('ascii') 51 | ) 52 | return content_type, self._generate_bytes(boundary) 53 | 54 | 55 | form = MultiPartForm() 56 | form['name'] = 'value' 57 | form.add_file('file1', 'somefile.txt', b'Some Content', 'text/plain') 58 | content_type, form_body = form.encode() 59 | print(content_type, form_body.decode('ascii')) 60 | 61 | 62 | 63 | from web_03 import http_request 64 | _, resp = http_request('https://httpbin.org/post', method='POST', 65 | data=form_body, headers={'Content-Type': content_type}) 66 | print(resp) 67 | 68 | 69 | -------------------------------------------------------------------------------- /Chapter10/networking_02.py: -------------------------------------------------------------------------------- 1 | import imaplib 2 | import re 3 | from email.parser import BytesParser 4 | 5 | 6 | class IMAPReader: 7 | ENCODING = 'utf-8' 8 | LIST_PATTERN = re.compile( 9 | r'\((?P.*?)\) "(?P.*)" (?P.*)' 10 | ) 11 | 12 | def __init__(self, host, username, password, ssl=True): 13 | if ssl: 14 | self._imap = imaplib.IMAP4_SSL(host) 15 | else: 16 | self._imap = imaplib.IMAP4(host) 17 | self._imap.login(username, password) 18 | 19 | def folders(self): 20 | """Retrieve list of IMAP folders""" 21 | resp, lines = self._imap.list() 22 | if resp != 'OK': 23 | raise Exception(resp) 24 | 25 | entries = [] 26 | for line in lines: 27 | flags, _, name = self.LIST_PATTERN.match( 28 | line.decode(self.ENCODING) 29 | ).groups() 30 | entries.append(dict( 31 | flags=flags, 32 | name=name.strip('"') 33 | )) 34 | return entries 35 | 36 | def messages(self, folder, limit=10, peek=True): 37 | """Return ``limit`` messages from ``folder`` 38 | 39 | peek=False will also fetch message body 40 | """ 41 | resp, count = self._imap.select('"%s"' % folder, readonly=True) 42 | if resp != 'OK': 43 | raise Exception(resp) 44 | 45 | last_message_id = int(count[0]) 46 | msg_ids = range(last_message_id, last_message_id-limit, -1) 47 | 48 | mode = '(BODY.PEEK[HEADER])' if peek else '(RFC822)' 49 | 50 | messages = [] 51 | for msg_id in msg_ids: 52 | resp, msg = self._imap.fetch(str(msg_id), mode) 53 | msg = msg[0][-1] 54 | 55 | messages.append(BytesParser().parsebytes(msg)) 56 | if len(messages) >= limit: 57 | break 58 | return messages 59 | 60 | def get_message_body(self, message): 61 | """Given a message for which the body was fetched, returns it""" 62 | body = [] 63 | if message.is_multipart(): 64 | for payload in message.get_payload(): 65 | body.append(payload.get_payload()) 66 | else: 67 | body.append(message.get_payload()) 68 | return body 69 | 70 | def close(self): 71 | """Close connection to IMAP server""" 72 | self._imap.close() 73 | 74 | mails = IMAPReader('imap.gmail.com', 75 | USER_NAME, 76 | PASSWORD, 77 | ssl=True) 78 | folders = mails.folders() 79 | for msg in mails.messages('INBOX', limit=4, peek=False): 80 | print(msg['Date'], msg['Subject']) 81 | # print(mails.get_message_body(msg)) 82 | -------------------------------------------------------------------------------- /Chapter11/web_10.py: -------------------------------------------------------------------------------- 1 | from web_06 import WSGIApplication 2 | 3 | app = WSGIApplication() 4 | 5 | 6 | 7 | class RestController: 8 | def __call__(self, req, resp): 9 | method = req.environ['REQUEST_METHOD'] 10 | action = getattr(self, method, self._not_found) 11 | return action(req, resp) 12 | 13 | def _not_found(self, environ, resp): 14 | resp.status = '404 Not Found' 15 | return b'{}' # Provide an empty JSON document 16 | 17 | 18 | import json 19 | 20 | 21 | @app.route('/resources/?(?P\\w*)') 22 | class ResourcesRestController(RestController): 23 | RESOURCES = {} 24 | 25 | def GET(self, req, resp): 26 | resource_id = req.urlargs['id'] 27 | if not resource_id: 28 | # Whole catalog requested 29 | return json.dumps(self.RESOURCES).encode('utf-8') 30 | 31 | if resource_id not in self.RESOURCES: 32 | return self._not_found(req, resp) 33 | 34 | return json.dumps(self.RESOURCES[resource_id]).encode('utf-8') 35 | 36 | def POST(self, req, resp): 37 | content_length = int(req.environ['CONTENT_LENGTH']) 38 | data = req.environ['wsgi.input'].read(content_length).decode('utf-8') 39 | 40 | resource = json.loads(data) 41 | resource['id'] = str(len(self.RESOURCES)+1) 42 | self.RESOURCES[resource['id']] = resource 43 | return json.dumps(resource).encode('utf-8') 44 | 45 | def DELETE(self, req, resp): 46 | resource_id = req.urlargs['id'] 47 | if not resource_id: 48 | return self._not_found(req, resp) 49 | self.RESOURCES.pop(resource_id, None) 50 | 51 | req.status = '204 No Content' 52 | return b'' 53 | 54 | 55 | import threading 56 | threading.Thread(target=app.serve, daemon=True).start() 57 | 58 | 59 | from web_03 import http_request 60 | _, resp = http_request('http://localhost:8000/resources', method='POST', 61 | data=json.dumps({'name': 'Mario', 62 | 'surname': 'Mario'}).encode('utf-8')) 63 | print('NEW RESOURCE: ', resp) 64 | 65 | _, resp = http_request('http://localhost:8000/resources') 66 | print('ALL RESOURCES: ', resp) 67 | 68 | http_request('http://localhost:8000/resources', method='POST', 69 | data=json.dumps({'name': 'Luigi', 70 | 'surname': 'Mario'}).encode('utf-8')) 71 | _, resp = http_request('http://localhost:8000/resources') 72 | print('ALL RESOURCES: ', resp) 73 | 74 | _, resp = http_request('http://localhost:8000/resources/1') 75 | print('RESOURCES #1: ', resp) 76 | 77 | http_request('http://localhost:8000/resources/2', method='DELETE') 78 | _, resp = http_request('http://localhost:8000/resources') 79 | print('ALL RESOURCES', resp) 80 | 81 | 82 | -------------------------------------------------------------------------------- /Chapter11/web_06.py: -------------------------------------------------------------------------------- 1 | import re 2 | import inspect 3 | from wsgiref.headers import Headers 4 | from wsgiref.simple_server import make_server 5 | from wsgiref.util import request_uri 6 | from urllib.parse import parse_qs 7 | 8 | class Response: 9 | def __init__(self): 10 | self.status = '200 OK' 11 | self.headers = Headers([ 12 | ('Content-Type', 'text/html; charset=utf-8') 13 | ]) 14 | 15 | def send(self, start_response): 16 | start_response(self.status, self.headers.items()) 17 | 18 | 19 | class Request: 20 | def __init__(self, environ): 21 | self.environ = environ 22 | self.urlargs = {} 23 | 24 | @property 25 | def path(self): 26 | return self.environ['PATH_INFO'] 27 | 28 | @property 29 | def query(self): 30 | return parse_qs(self.environ['QUERY_STRING']) 31 | 32 | 33 | class WSGIApplication: 34 | def __init__(self): 35 | self.routes = [] 36 | 37 | def route(self, path): 38 | def _route_decorator(f): 39 | self.routes.append((re.compile(path), f)) 40 | return f 41 | return _route_decorator 42 | 43 | def serve(self): 44 | httpd = make_server('', 8000, self) 45 | print("Serving on port 8000...") 46 | httpd.serve_forever() 47 | 48 | def _not_found(self, environ, resp): 49 | resp.status = '404 Not Found' 50 | return b"""

Not Found

""" 51 | 52 | def __call__(self, environ, start_response): 53 | request = Request(environ) 54 | 55 | routed_action = self._not_found 56 | for regex, action in self.routes: 57 | match = regex.fullmatch(request.path) 58 | if match: 59 | routed_action = action 60 | request.urlargs = match.groupdict() 61 | break 62 | 63 | resp = Response() 64 | 65 | if inspect.isclass(routed_action): 66 | routed_action = routed_action() 67 | body = routed_action(request, resp) 68 | 69 | resp.send(start_response) 70 | return [body] 71 | 72 | 73 | app = WSGIApplication() 74 | 75 | @app.route('/') 76 | def index(request, resp): 77 | return b'Hello World, Click here' 78 | 79 | @app.route('/link') 80 | def link(request, resp): 81 | return (b'You clicked the link! ' 82 | b'Try Some arguments') 83 | 84 | @app.route('/args') 85 | def args(request, resp): 86 | return (b'You provided %b
' 87 | b'Try URL Arguments' % 88 | repr(request.query).encode('utf-8')) 89 | 90 | @app.route('/name/(?P\\w+)') 91 | def name(request, resp): 92 | return (b'Your name: %b' % request.urlargs['first_name'].encode('utf-8')) 93 | 94 | if __name__ == '__main__': 95 | app.serve() -------------------------------------------------------------------------------- /Chapter03/terminal_09.py: -------------------------------------------------------------------------------- 1 | import curses 2 | import textwrap 3 | import itertools 4 | 5 | 6 | class MessageBox(object): 7 | @classmethod 8 | def show(cls, message, cancel=False, width=40): 9 | """Show a message with an Ok/Cancel dialog. 10 | 11 | Provide ``cancel=True`` argument to show a cancel button too. 12 | Returns the user selected choice: 13 | 14 | - 0 = Ok 15 | - 1 = Cancel 16 | """ 17 | dialog = MessageBox(message, width, cancel) 18 | return curses.wrapper(dialog._show) 19 | 20 | def __init__(self, message, width, cancel): 21 | self._message = self._build_message(width, message) 22 | self._width = width 23 | self._height = max(self._message.count('\n')+1, 3) + 6 24 | self._selected = 0 25 | self._buttons = ['Ok'] 26 | if cancel: 27 | self._buttons.append('Cancel') 28 | 29 | def _build_message(self, width, message): 30 | lines = [] 31 | for line in message.split('\n'): 32 | if line.strip(): 33 | lines.extend(textwrap.wrap(line, width-4, 34 | replace_whitespace=False)) 35 | else: 36 | lines.append('') 37 | return '\n'.join(lines) 38 | 39 | def _show(self, stdscr): 40 | win = curses.newwin(self._height, self._width, 41 | (curses.LINES - self._height) // 2, 42 | (curses.COLS - self._width) // 2) 43 | win.keypad(1) 44 | win.border() 45 | textbox = win.derwin(self._height - 1, self._width - 3, 1, 2) 46 | textbox.addstr(0, 0, self._message) 47 | return self._loop(win) 48 | 49 | def _loop(self, win): 50 | while True: 51 | for idx, btntext in enumerate(self._buttons): 52 | allowedspace = self._width // len(self._buttons) 53 | btn = win.derwin( 54 | 3, 10, 55 | self._height - 4, 56 | (((allowedspace-10)//2*idx) + allowedspace*idx + 2) 57 | ) 58 | btn.border() 59 | flag = 0 60 | if idx == self._selected: 61 | flag = curses.A_BOLD 62 | btn.addstr(1, (10-len(btntext))//2, btntext, flag) 63 | win.refresh() 64 | 65 | key = win.getch() 66 | if key == curses.KEY_RIGHT: 67 | self._selected = 1 68 | elif key == curses.KEY_LEFT: 69 | self._selected = 0 70 | elif key == ord('\n'): 71 | return self._selected 72 | 73 | MessageBox.show('Hello World,\n\npress enter to continue') 74 | if MessageBox.show('Are you sure?\n\npress enter to confirm', cancel=True) == 0: 75 | print("Yeah! Let's continue") 76 | else: 77 | print("That's sad, hope to see you soon") -------------------------------------------------------------------------------- /Chapter11/web_01.py: -------------------------------------------------------------------------------- 1 | import json 2 | import datetime 3 | import decimal 4 | import types 5 | 6 | 7 | class CustomJSONEncoder(json.JSONEncoder): 8 | """JSON Encoder with support for additional types. 9 | 10 | Supports dates, times, decimals, generators and 11 | any custom class that implements __json__ method. 12 | """ 13 | def default(self, obj): 14 | if hasattr(obj, '__json__') and callable(obj.__json__): 15 | return obj.__json__() 16 | elif isinstance(obj, (datetime.datetime, datetime.time)): 17 | return obj.replace(microsecond=0).isoformat() 18 | elif isinstance(obj, datetime.date): 19 | return obj.isoformat() 20 | elif isinstance(obj, decimal.Decimal): 21 | return float(obj) 22 | elif isinstance(obj, types.GeneratorType): 23 | return list(obj) 24 | else: 25 | return super().default(obj) 26 | 27 | 28 | 29 | jsonstr = json.dumps({'s': 'Hello World', 30 | 'dt': datetime.datetime.utcnow(), 31 | 't': datetime.datetime.utcnow().time(), 32 | 'g': (i for i in range(5)), 33 | 'd': datetime.date.today(), 34 | 'dct': { 35 | 's': 'SubDict', 36 | 'dt': datetime.datetime.utcnow() 37 | }}, 38 | cls=CustomJSONEncoder) 39 | print(jsonstr) 40 | 41 | print(json.loads(jsonstr)) 42 | 43 | class CustomJSONDecoder(json.JSONDecoder): 44 | """Custom JSON Decoder that tries to decode additional types. 45 | 46 | Decoder tries to guess dates, times and datetimes in ISO format. 47 | """ 48 | def __init__(self, *args, **kwargs): 49 | super().__init__( 50 | *args, **kwargs, object_hook=self.parse_object 51 | ) 52 | 53 | def parse_object(self, values): 54 | for k, v in values.items(): 55 | if not isinstance(v, str): 56 | continue 57 | 58 | if len(v) == 10 and v.count('-') == 2: 59 | # Probably contains a date 60 | try: 61 | values[k] = datetime.datetime.strptime(v, '%Y-%m-%d').date() 62 | except: 63 | pass 64 | elif len(v) == 8 and v.count(':') == 2: 65 | # Probably contains a time 66 | try: 67 | values[k] = datetime.datetime.strptime(v, '%H:%M:%S').time() 68 | except: 69 | pass 70 | elif (len(v) == 19 and v.count('-') == 2 and 71 | v.count('T') == 1 and v.count(':') == 2): 72 | # Probably contains a datetime 73 | try: 74 | values[k] = datetime.datetime.strptime(v, '%Y-%m-%dT%H:%M:%S') 75 | except: 76 | pass 77 | return values 78 | 79 | 80 | 81 | jsondoc = json.loads(jsonstr, cls=CustomJSONDecoder) 82 | print(jsondoc) -------------------------------------------------------------------------------- /Chapter03/terminal_05.py: -------------------------------------------------------------------------------- 1 | EMAILS = [ 2 | {'sender': 'author1@domain.com', 'subject': 'First email', 3 | 'body': 'This is my first email'}, 4 | {'sender': 'author2@domain.com', 'subject': 'Second email', 5 | 'body': 'This is my second email'}, 6 | ] 7 | 8 | import cmd 9 | import shlex 10 | 11 | class MyMail(cmd.Cmd): 12 | intro = 'Simple interactive email client.' 13 | prompt = 'mymail> ' 14 | 15 | def __init__(self, *args, **kwargs): 16 | super(MyMail, self).__init__(*args, **kwargs) 17 | self.selected_email = None 18 | 19 | def do_list(self, line): 20 | """list 21 | 22 | List emails currently in the Inbox""" 23 | for idx, email in enumerate(EMAILS): 24 | print('[{idx}] From: {e[sender]} - {e[subject]}'.format( 25 | idx=idx, e=email 26 | )) 27 | 28 | def do_read(self, emailnum): 29 | """read [emailnum] 30 | 31 | Reads emailnum nth email from those listed in the Inbox""" 32 | try: 33 | idx = int(emailnum.strip()) 34 | except: 35 | print('Invalid email index {}'.format(emailnum)) 36 | return 37 | 38 | try: 39 | email = EMAILS[idx] 40 | except IndexError: 41 | print('Email {} not found'.format(idx)) 42 | return 43 | 44 | print('From: {e[sender]}\n' 45 | 'Subject: {e[subject]}\n' 46 | '\n{e[body]}'.format(e=email)) 47 | # Track the last read email as the selected one for reply. 48 | self.selected_email = idx 49 | 50 | def do_reply(self, message): 51 | """reply [message] 52 | 53 | Sends back an email to the author of the received email""" 54 | if self.selected_email is None: 55 | print('No email selected for reply.') 56 | return 57 | 58 | email = EMAILS[self.selected_email] 59 | print('Replied to {e[sender]} with: {message}'.format( 60 | e=email, message=message 61 | )) 62 | 63 | def do_send(self, arguments): 64 | """send [recipient] [subject] [message] 65 | 66 | Send a new email with [subject] to [recipient]""" 67 | # Split the arguments with shlex 68 | # so that we allow subject or message with spaces. 69 | args = shlex.split(arguments) 70 | if len(args) < 3: 71 | print('A recipient, a subject and a message are required.') 72 | return 73 | 74 | recipient, subject, message = args[:3] 75 | if len(args) >= 4: 76 | message += ' '.join(args[3:]) 77 | 78 | print('Sending email {} to {}: "{}"'.format( 79 | subject, recipient, message 80 | )) 81 | 82 | def complete_send(self, text, line, begidx, endidx): 83 | # Provide autocompletion of recipients for send command. 84 | return [e['sender'] for e in EMAILS if e['sender'].startswith(text)] 85 | 86 | def do_EOF(self, line): 87 | return True 88 | 89 | if __name__ == '__main__': 90 | MyMail().cmdloop() 91 | -------------------------------------------------------------------------------- /Chapter10/networking_05.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | class EchoServer: 4 | MAX_MESSAGE_SIZE = 2**16 # 65k 5 | MESSAGE_HEADER_LEN = len(str(MAX_MESSAGE_SIZE)) 6 | 7 | def __init__(self, host='0.0.0.0', port=9800): 8 | self._host = host 9 | self._port = port 10 | self._server = None 11 | 12 | def serve(self, loop): 13 | coro = asyncio.start_server(self.handle, self._host, self._port, 14 | loop=loop) 15 | self._server = loop.run_until_complete(coro) 16 | print('Serving on %s:%s' % (self._host, self._port)) 17 | loop.run_until_complete(self._server.wait_closed()) 18 | print('Done') 19 | 20 | @property 21 | def started(self): 22 | return self._server is not None and self._server.sockets 23 | 24 | def stop(self): 25 | print('Stopping...') 26 | self._server.close() 27 | 28 | async def handle(self, reader, writer): 29 | data = await self.recv_message(reader) 30 | await self.send_message(writer, b'ECHO: %s' % data) 31 | # Signal we finished handling this request 32 | # or the server will hang. 33 | writer.close() 34 | 35 | @classmethod 36 | async def recv_message(cls, socket): 37 | data_size = int(await socket.read(cls.MESSAGE_HEADER_LEN)) 38 | data = await socket.read(data_size) 39 | return data 40 | 41 | @classmethod 42 | async def send_message(cls, socket, message): 43 | if len(message) > cls.MAX_MESSAGE_SIZE: 44 | raise ValueError('Message too big') 45 | 46 | message_size = str(len(message)).encode('ascii') 47 | message_size = message_size.zfill(cls.MESSAGE_HEADER_LEN) 48 | data = message_size + message 49 | 50 | socket.write(data) 51 | await socket.drain() 52 | 53 | 54 | import socket 55 | 56 | def send_message_to_server(ip, port, message): 57 | def _recv_message(socket): 58 | data_size = int(socket.recv(EchoServer.MESSAGE_HEADER_LEN)) 59 | data = socket.recv(data_size) 60 | return data 61 | 62 | def _prepare_message(message): 63 | if len(message) > EchoServer.MAX_MESSAGE_SIZE: 64 | raise ValueError('Message too big') 65 | 66 | message_size = str(len(message)).encode('ascii') 67 | message_size = message_size.zfill(EchoServer.MESSAGE_HEADER_LEN) 68 | return message_size + message 69 | 70 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 71 | sock.connect((ip, port)) 72 | try: 73 | sock.sendall(_prepare_message(message)) 74 | response = _recv_message(sock) 75 | print("ANSWER: {}".format(response)) 76 | finally: 77 | sock.close() 78 | 79 | 80 | server = EchoServer() 81 | def serve_for_3_seconds(): 82 | loop = asyncio.new_event_loop() 83 | asyncio.set_event_loop(loop) 84 | loop.call_later(3, server.stop) 85 | server.serve(loop) 86 | loop.close() 87 | 88 | 89 | import threading 90 | server_thread = threading.Thread(target=serve_for_3_seconds) 91 | server_thread.start() 92 | 93 | while not server.started: 94 | pass 95 | 96 | send_message_to_server('localhost', server._port, b"Hello World 1") 97 | send_message_to_server('localhost', server._port, b"Hello World 2") 98 | send_message_to_server('localhost', server._port, b"Hello World 3") 99 | 100 | server_thread.join() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Modern Python Standard Library Cookbook 5 | 6 | Modern Python Standard Library Cookbook 7 | 8 | This is the code repository for [Modern Python Standard Library Cookbook](https://www.packtpub.com/application-development/modern-python-standard-library-cookbook?utm_source=github&utm_medium=repository&utm_campaign=9781788830829 ), published by Packt. 9 | 10 | **Build optimized applications in Python by smartly implementing the standard library** 11 | 12 | ## What is this book about? 13 | The Python 3 Standard Library is a vast array of modules that you can use for developing various kinds of applications. It contains an exhaustive list of libraries, and this book will help you choose the best one to address specific programming problems in Python. 14 | 15 | This book covers the following exciting features: 16 | Store multiple values per key in associative containers 17 | Create interactive character-based user interfaces 18 | Work with native time and display data for your time zone 19 | Read/write SGML family languages, both as a SAX and DOM parser to meet file sizes and other requirements 20 | Group equivalent items using itertools and sorted features together 21 | Use partials to create unary functions out of multi-argument functions 22 | Implement hashing algorithms to store passwords in a safe way 23 | 24 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1788830822) today! 25 | 26 | https://www.packtpub.com/ 28 | 29 | ## Instructions and Navigations 30 | All of the code is organized into folders. For example, Chapter02. 31 | 32 | The code will look like the following: 33 | ``` 34 | for word in 'hello world this is a very nice day'.split(): 35 | if word in counts: 36 | counts[word] += 1 37 | ``` 38 | 39 | **Following is what you need for this book:** 40 | This book is well suited for developers who want to write expressive, highly responsive, manageable, scalable, and resilient code in Python. Prior programming knowledge of Python is expected. 41 | 42 | With the following software and hardware list you can run all code files present in the book (Chapter 1-14). 43 | ### Software and Hardware List 44 | | Chapter | Software required | OS required | 45 | | -------- | ------------------------------------ | ----------------------------------- | 46 | | 1-14 | Python 3.5 or 3.6 | Linux, BSD, Windows Vista or newer or macOS | 47 | 48 | 49 | ### Related products 50 | * Python Programming Blueprints [[Packt]](https://www.packtpub.com/application-development/python-programming-blueprints?utm_source=github&utm_medium=repository&utm_campaign=9781786468161 ) [[Amazon]](https://www.amazon.com/dp/1786468166) 51 | 52 | * Secret Recipes of the Python Ninja [[Packt]](https://www.packtpub.com/application-development/secret-recipes-python-ninja?utm_source=github&utm_medium=repository&utm_campaign=9781788294874 ) [[Amazon]](https://www.amazon.com/dp/B07BYBMGQT) 53 | 54 | ## Get to Know the Author 55 | **Alessandro Molina** 56 | has been a Python developer since 2001, and has always been interested in Python as a web development platform. He has worked as a CTO and a team leader of Python teams for the past 10 years and is currently the core developer of the TurboGears2 web framework and maintainer of Beaker Caching/Session framework. He authored the DEPOT file storage framework and the DukPy JavaScript interpreter for Python and has collaborated with various Python projects related to web development, such as FormEncode, ToscaWidgets, and the Ming MongoDB ORM. 57 | 58 | ### Suggestions and Feedback 59 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 60 | ### Download a free PDF 61 | 62 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
63 |

https://packt.link/free-ebook/9781788830829

--------------------------------------------------------------------------------