Skip to content

Commit bf4a2be

Browse files
committed
ch08, 09, 10: example files
1 parent 42861b6 commit bf4a2be

File tree

111 files changed

+4707
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+4707
-0
lines changed

08-def-type-hints/README.asciidoc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
== Type Hints in Function Definitions

08-def-type-hints/RPN_calc/calc.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
from array import array
5+
from typing import Mapping, MutableSequence, Callable, Iterable, Sequence, Union, Any
6+
7+
8+
OPERATORS: Mapping[str, Callable[[float, float], float]] = {
9+
'+': lambda a, b: a + b,
10+
'-': lambda a, b: a - b,
11+
'*': lambda a, b: a * b,
12+
'/': lambda a, b: a / b,
13+
'^': lambda a, b: a ** b,
14+
}
15+
16+
17+
Stack = MutableSequence[float]
18+
19+
20+
def parse_token(token: str) -> Union[str, float]:
21+
try:
22+
return float(token)
23+
except ValueError:
24+
return token
25+
26+
27+
def evaluate(tokens: Iterable[str], stack: Stack) -> None:
28+
for token in tokens:
29+
atom = parse_token(token)
30+
if isinstance(atom, float):
31+
stack.append(atom)
32+
else: # not float, must be operator
33+
op = OPERATORS[atom]
34+
x, y = stack.pop(), stack.pop()
35+
result = op(y, x)
36+
stack.append(result)
37+
38+
39+
def display(s: Stack) -> str:
40+
items = (repr(n) for n in s)
41+
return ' │ '.join(items) + ' →'
42+
43+
44+
def repl(input_fn: Callable[[Any], str] = input) -> None:
45+
"""Read-Eval-Print-Loop"""
46+
47+
print('Use CTRL+C to quit.', file=sys.stderr)
48+
stack: Stack = array('d')
49+
50+
while True:
51+
try:
52+
line = input_fn('> ') # Read
53+
except (EOFError, KeyboardInterrupt):
54+
break
55+
try:
56+
evaluate(line.split(), stack) # Eval
57+
except IndexError:
58+
print('*** Not enough arguments.', file=sys.stderr)
59+
except KeyError as exc:
60+
print('*** Unknown operator:', exc.args[0], file=sys.stderr)
61+
print(display(stack)) # Print
62+
63+
print()
64+
65+
66+
if __name__ == '__main__':
67+
repl()
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from pytest import mark, approx # type: ignore
2+
3+
from dialogue import Dialogue # type: ignore
4+
5+
from calc import evaluate, repl, display, Stack
6+
7+
TOLERANCE = .0001
8+
9+
@mark.parametrize("source, want", [
10+
('2', 2),
11+
('2 3 +', 5),
12+
('5 3 -', 2),
13+
('3 5 * 2 +', 17),
14+
('2 3 4 5 * * *', 120),
15+
('1.1 1.1 1.1 + +', approx(3.3, TOLERANCE)),
16+
('100 32 - 5 * 9 /', approx(37.78, TOLERANCE)),
17+
])
18+
def test_evaluate(source, want) -> None:
19+
stack: Stack = []
20+
evaluate(source.split(), stack)
21+
assert want == stack[-1]
22+
23+
24+
@mark.parametrize("value, want", [
25+
([], ' →'),
26+
([3.], '3.0 →'),
27+
([3., 4., 5.], '3.0 │ 4.0 │ 5.0 →'),
28+
])
29+
def test_display(value, want) -> None:
30+
assert want == display(value)
31+
32+
33+
@mark.parametrize("session", [
34+
"""
35+
> 3
36+
3.0 →
37+
""",
38+
"""
39+
> 3 5 6
40+
3.0 │ 5.0 │ 6.0 →
41+
> *
42+
3.0 │ 30.0 →
43+
> -
44+
-27.0 →
45+
""",
46+
])
47+
def test_repl(capsys, session) -> None:
48+
dlg = Dialogue(session)
49+
repl(dlg.fake_input)
50+
captured = capsys.readouterr()
51+
assert dlg.session.strip() == captured.out.strip()

08-def-type-hints/arg_lab.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import typing
2+
from typing import Optional
3+
4+
5+
def f(a: str, *b: int, **c: float) -> None:
6+
if typing.TYPE_CHECKING:
7+
# reveal_type(b)
8+
reveal_type(c)
9+
print(a, b, c)
10+
11+
12+
def g(__a: int) -> None:
13+
print(__a)
14+
15+
16+
def h(a: int, /) -> None:
17+
print(a)
18+
19+
20+
def tag(
21+
name: str,
22+
/,
23+
*content: str,
24+
class_: Optional[str] = None,
25+
foo: Optional[str] = None,
26+
**attrs: str,
27+
) -> str:
28+
return repr((name, content, class_, attrs))
29+
30+
31+
f(a='1')
32+
f('1', 2, 3, x=4, y=5)
33+
g(__a=1)
34+
# h(a=1)
35+
print(tag('li', 'first', 'second', id='#123'))
36+
print(tag('li', 'first', 'second', class_='menu', id='#123'))

08-def-type-hints/birds/birds.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class Bird:
2+
pass
3+
4+
class Duck(Bird): # <1>
5+
def quack(self):
6+
print('Quack!')
7+
8+
def alert(birdie): # <2>
9+
birdie.quack()
10+
11+
def alert_duck(birdie: Duck) -> None: # <3>
12+
birdie.quack()
13+
14+
def alert_bird(birdie: Bird) -> None: # <4>
15+
birdie.quack()

08-def-type-hints/birds/daffy.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from birds import *
2+
3+
daffy = Duck()
4+
alert(daffy) # <1>
5+
alert_duck(daffy) # <2>
6+
alert_bird(daffy) # <3>
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from typing import Protocol # <1>
2+
3+
class GooseLike(Protocol):
4+
def honk(self, times: int) -> None: ... # <2>
5+
def swim(self) -> None: ...
6+
7+
8+
def alert(waterfowl: GooseLike) -> None: # <3>
9+
waterfowl.honk(2)
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from lake import alert
2+
3+
class Parrot:
4+
def honk(self, times: int) -> None: # <1>
5+
print('Honk! ' * times * 2)
6+
7+
8+
ze_carioca = Parrot()
9+
10+
alert(ze_carioca) # <2>
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from lake import alert # <1>
2+
3+
class Swan: # <2>
4+
def honk(self, repetitions: int) -> None: # <3>
5+
print('Honk! ' * repetitions)
6+
7+
def swim(self) -> None: # <4>
8+
pass
9+
10+
11+
bella = Swan()
12+
13+
alert(bella) # <5>

08-def-type-hints/birds/woody.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from birds import *
2+
3+
woody = Bird()
4+
alert(woody)
5+
alert_duck(woody)
6+
alert_bird(woody)

08-def-type-hints/bus.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
"""
3+
>>> import copy
4+
>>> bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
5+
>>> bus2 = copy.copy(bus1)
6+
>>> bus3 = copy.deepcopy(bus1)
7+
>>> bus1.drop('Bill')
8+
>>> bus2.passengers
9+
['Alice', 'Claire', 'David']
10+
>>> bus3.passengers
11+
['Alice', 'Bill', 'Claire', 'David']
12+
13+
"""
14+
15+
# tag::BUS_CLASS[]
16+
class Bus:
17+
18+
def __init__(self, passengers=None):
19+
if passengers is None:
20+
self.passengers = []
21+
else:
22+
self.passengers = list(passengers)
23+
24+
def pick(self, name):
25+
self.passengers.append(name)
26+
27+
def drop(self, name):
28+
self.passengers.remove(name)
29+
# end::BUS_CLASS[]

08-def-type-hints/charindex.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
``name_index`` builds an inverted index mapping words to sets of Unicode
3+
characters which contain that word in their names. For example::
4+
5+
>>> index = name_index(32, 65)
6+
>>> sorted(index['SIGN'])
7+
['#', '$', '%', '+', '<', '=', '>']
8+
>>> sorted(index['DIGIT'])
9+
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
10+
>>> index['DIGIT'] & index['EIGHT']
11+
{'8'}
12+
"""
13+
14+
# tag::CHARINDEX[]
15+
import sys
16+
import re
17+
import unicodedata
18+
from typing import Dict, Set, Iterator
19+
20+
RE_WORD = re.compile('\w+')
21+
STOP_CODE = sys.maxunicode + 1
22+
23+
def tokenize(text: str) -> Iterator[str]: # <1>
24+
"""return iterable of uppercased words"""
25+
for match in RE_WORD.finditer(text):
26+
yield match.group().upper()
27+
28+
def name_index(start: int = 32, end: int = STOP_CODE) -> Dict[str, Set[str]]:
29+
index: Dict[str, Set[str]] = {} # <2>
30+
for char in (chr(i) for i in range(start, end)):
31+
if name := unicodedata.name(char, ''): # <3>
32+
for word in tokenize(name):
33+
index.setdefault(word, set()).add(char)
34+
return index
35+
# end::CHARINDEX[]

08-def-type-hints/clip_annot.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
>>> clip('banana ', 6)
3+
'banana'
4+
>>> clip('banana ', 7)
5+
'banana'
6+
>>> clip('banana ', 5)
7+
'banana'
8+
>>> clip('banana split', 6)
9+
'banana'
10+
>>> clip('banana split', 7)
11+
'banana'
12+
>>> clip('banana split', 10)
13+
'banana'
14+
>>> clip('banana split', 11)
15+
'banana'
16+
>>> clip('banana split', 12)
17+
'banana split'
18+
"""
19+
20+
# tag::CLIP_ANNOT[]
21+
def clip(text: str, max_len: int = 80) -> str:
22+
"""Return new ``str`` clipped at last space before or after ``max_len``.
23+
Return full ``text`` if no space found.
24+
"""
25+
end = None
26+
if len(text) > max_len:
27+
space_before = text.rfind(' ', 0, max_len)
28+
if space_before >= 0:
29+
end = space_before
30+
else:
31+
space_after = text.rfind(' ', max_len)
32+
if space_after >= 0:
33+
end = space_after
34+
if end is None:
35+
end = len(text)
36+
return text[:end].rstrip()
37+
38+
# end::CLIP_ANNOT[]

08-def-type-hints/clip_annot_1ed.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
>>> clip('banana ', 6)
3+
'banana'
4+
>>> clip('banana ', 7)
5+
'banana'
6+
>>> clip('banana ', 5)
7+
'banana'
8+
>>> clip('banana split', 6)
9+
'banana'
10+
>>> clip('banana split', 7)
11+
'banana'
12+
>>> clip('banana split', 10)
13+
'banana'
14+
>>> clip('banana split', 11)
15+
'banana'
16+
>>> clip('banana split', 12)
17+
'banana split'
18+
"""
19+
20+
# tag::CLIP_ANNOT[]
21+
22+
def clip(text:str, max_len:'int > 0'=80) -> str: # <1>
23+
"""Return text clipped at the last space before or after max_len
24+
"""
25+
end = None
26+
if len(text) > max_len:
27+
space_before = text.rfind(' ', 0, max_len)
28+
if space_before >= 0:
29+
end = space_before
30+
else:
31+
space_after = text.rfind(' ', max_len)
32+
if space_after >= 0:
33+
end = space_after
34+
if end is None: # no spaces were found
35+
end = len(text)
36+
return text[:end].rstrip()
37+
38+
# end::CLIP_ANNOT[]
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
>>> from clip_annot import clip
2+
>>> from inspect import signature
3+
>>> sig = signature(clip)
4+
>>> sig.return_annotation
5+
<class 'str'>
6+
>>> for param in sig.parameters.values():
7+
... note = repr(param.annotation).ljust(13)
8+
... print(note, ':', param.name, '=', param.default)
9+
<class 'str'> : text = <class 'inspect._empty'>
10+
'int > 0' : max_len = 80

0 commit comments

Comments
 (0)