Skip to content

Commit 2f8bf06

Browse files
committed
update from O'Reilly repo
1 parent e1cd63a commit 2f8bf06

File tree

28 files changed

+470
-125
lines changed

28 files changed

+470
-125
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from typing import Protocol, Any
22

3-
class Comparable(Protocol): # <1>
3+
class SupportsLessThan(Protocol): # <1>
44
def __lt__(self, other: Any) -> bool: ... # <2>

08-def-type-hints/comparable/mymax.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
# tag::MYMAX_TYPES[]
22
from typing import Protocol, Any, TypeVar, overload, Callable, Iterable, Union
33

4-
class _Comparable(Protocol):
4+
class SupportsLessThan(Protocol):
55
def __lt__(self, other: Any) -> bool: ...
66

7-
_T = TypeVar('_T')
8-
_CT = TypeVar('_CT', bound=_Comparable)
9-
_DT = TypeVar('_DT')
7+
T = TypeVar('T')
8+
LT = TypeVar('LT', bound=SupportsLessThan)
9+
DT = TypeVar('DT')
1010

1111
MISSING = object()
1212
EMPTY_MSG = 'max() arg is an empty sequence'
1313

1414
@overload
15-
def max(__arg1: _CT, __arg2: _CT, *_args: _CT, key: None = ...) -> _CT:
15+
def max(__arg1: LT, __arg2: LT, *_args: LT, key: None = ...) -> LT:
1616
...
1717
@overload
18-
def max(__arg1: _T, __arg2: _T, *_args: _T, key: Callable[[_T], _CT]) -> _T:
18+
def max(__arg1: T, __arg2: T, *_args: T, key: Callable[[T], LT]) -> T:
1919
...
2020
@overload
21-
def max(__iterable: Iterable[_CT], *, key: None = ...) -> _CT:
21+
def max(__iterable: Iterable[LT], *, key: None = ...) -> LT:
2222
...
2323
@overload
24-
def max(__iterable: Iterable[_T], *, key: Callable[[_T], _CT]) -> _T:
24+
def max(__iterable: Iterable[T], *, key: Callable[[T], LT]) -> T:
2525
...
2626
@overload
27-
def max(__iterable: Iterable[_CT], *, key: None = ...,
28-
default: _DT) -> Union[_CT, _DT]:
27+
def max(__iterable: Iterable[LT], *, key: None = ...,
28+
default: DT) -> Union[LT, DT]:
2929
...
3030
@overload
31-
def max(__iterable: Iterable[_T], *, key: Callable[[_T], _CT],
32-
default: _DT) -> Union[_T, _DT]:
31+
def max(__iterable: Iterable[T], *, key: Callable[[T], LT],
32+
default: DT) -> Union[T, DT]:
3333
...
3434
# end::MYMAX_TYPES[]
3535
# tag::MYMAX[]
@@ -57,4 +57,4 @@ def max(first, *args, key=None, default=MISSING):
5757
candidate = current
5858
candidate_key = current_key
5959
return candidate
60-
# end::MYMAX[]
60+
# end::MYMAX[]

08-def-type-hints/comparable/mymax_demo.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ def error_single_arg_not_iterable() -> None:
116116
except TypeError as exc:
117117
print(exc)
118118

119+
###################################### run demo and error functions
119120

120121
def main():
121122
for name, val in globals().items():

08-def-type-hints/comparable/top.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121

2222
# tag::TOP[]
2323
from typing import TypeVar, Iterable, List
24-
from comparable import Comparable
24+
from comparable import SupportsLessThan
2525

26-
CT = TypeVar('CT', bound=Comparable)
26+
LT = TypeVar('LT', bound=SupportsLessThan)
2727

28-
def top(series: Iterable[CT], length: int) -> List[CT]:
28+
def top(series: Iterable[LT], length: int) -> List[LT]:
2929
return sorted(series, reverse=True)[:length]
3030
# end::TOP[]

08-def-type-hints/comparable/top_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def test_top_tuples() -> None:
2929
reveal_type(result)
3030
assert result == expected
3131

32+
# intentional type error
3233
def test_top_objects_error() -> None:
3334
series = [object() for _ in range(4)]
3435
if TYPE_CHECKING:

08-def-type-hints/passdrill.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ def load_hash() -> Tuple[bytes, bytes]:
5454
salted_hash = fp.read()
5555
except FileNotFoundError:
5656
print('ERROR: passphrase hash file not found.', HELP)
57-
sys.exit(2)
57+
# "standard" exit status codes:
58+
# https://stackoverflow.com/questions/1101957/are-there-any-standard-exit-status-codes-in-linux/40484670#40484670
59+
sys.exit(74) # input/output error
5860

5961
salt, stored_hash = salted_hash.split(b':')
6062
return b64decode(salt), b64decode(stored_hash)
@@ -93,7 +95,7 @@ def main(argv: Sequence[str]) -> None:
9395
save_hash()
9496
else:
9597
print('ERROR: invalid argument.', HELP)
96-
sys.exit(1)
98+
sys.exit(2) # command line usage error
9799

98100

99101
if __name__ == '__main__':

11-pythonic-obj/mem_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
module = importlib.import_module(module_name)
1010
else:
1111
print(f'Usage: {sys.argv[0]} <vector-module-to-test>')
12-
sys.exit(1)
12+
sys.exit(2) # command line usage error
1313

1414
fmt = 'Selected Vector2d type: {.__name__}.{.__name__}'
1515
print(fmt.format(module, module.Vector2d))

15-type-hints/erp.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import random
2+
from typing import TypeVar, Generic, List, Iterable
3+
4+
5+
T = TypeVar('T')
6+
7+
8+
class EnterpriserRandomPopper(Generic[T]):
9+
def __init__(self, items: Iterable[T]) -> None:
10+
self._items: List[T] = list(items)
11+
random.shuffle(self._items)
12+
13+
def pop_random(self) -> T:
14+
return self._items.pop()

15-type-hints/erp_test.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import random
2+
from typing import Iterable, TYPE_CHECKING, List
3+
4+
from erp import EnterpriserRandomPopper
5+
import randompop
6+
7+
8+
def test_issubclass() -> None:
9+
assert issubclass(EnterpriserRandomPopper, randompop.RandomPopper)
10+
11+
12+
13+
def test_isinstance_untyped_items_argument() -> None:
14+
items = [1, 2, 3]
15+
popper = EnterpriserRandomPopper(items) # [int] is not required
16+
if TYPE_CHECKING:
17+
reveal_type(popper)
18+
# Revealed type is 'erp.EnterpriserRandomPopper[builtins.int*]'
19+
assert isinstance(popper, randompop.RandomPopper)
20+
21+
22+
def test_isinstance_untyped_items_in_var_type() -> None:
23+
items = [1, 2, 3]
24+
popper: EnterpriserRandomPopper = EnterpriserRandomPopper[int](items)
25+
if TYPE_CHECKING:
26+
reveal_type(popper)
27+
# Revealed type is 'erp.EnterpriserRandomPopper[Any]'
28+
assert isinstance(popper, randompop.RandomPopper)
29+
30+
31+
def test_isinstance_item() -> None:
32+
items = [1, 2, 3]
33+
popper = EnterpriserRandomPopper[int](items) # [int] is not required
34+
popped = popper.pop_random()
35+
if TYPE_CHECKING:
36+
reveal_type(popped)
37+
# Revealed type is 'builtins.int*'
38+
assert isinstance(popped, int)

15-type-hints/randompick_generic.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from typing import Protocol, runtime_checkable, TypeVar
2+
3+
T_co = TypeVar('T_co', covariant=True)
4+
5+
@runtime_checkable
6+
class GenericRandomPicker(Protocol[T_co]):
7+
def pick(self) -> T_co: ...

0 commit comments

Comments
 (0)