From aa868e8f75da7d41b6ea016851d52648c45da346 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Tue, 18 Feb 2020 23:45:05 -0300 Subject: [PATCH] ch17: update from book draft --- 17-it-generator/aritprog_v0.py | 3 +- 17-it-generator/aritprog_v1.py | 21 ++-- 17-it-generator/aritprog_v2.py | 4 +- 17-it-generator/aritprog_v3.py | 4 +- 17-it-generator/fibo_by_hand.py | 4 +- 17-it-generator/isis2json/isis2json.py | 10 +- 17-it-generator/sentence.py | 15 ++- 17-it-generator/sentence_gen.py | 3 + 17-it-generator/sentence_gen2.py | 5 +- 17-it-generator/sentence_genexp.py | 4 +- 17-it-generator/sentence_iter.py | 4 +- 17-it-generator/tree/4steps/tree_step0.py | 7 ++ 17-it-generator/tree/4steps/tree_step1.py | 10 ++ 17-it-generator/tree/4steps/tree_step2.py | 12 ++ 17-it-generator/tree/4steps/tree_step3.py | 10 ++ 17-it-generator/tree/extra/pretty_tree.py | 31 ++++++ .../tree/extra/test_pretty_tree.py | 105 ++++++++++++++++++ 17-it-generator/tree/extra/test_tree.py | 94 ++++++++++++++++ 17-it-generator/tree/extra/tree.py | 13 +++ 17-it-generator/tree/step0/test_tree.py | 8 ++ 17-it-generator/tree/step0/tree.py | 7 ++ 17-it-generator/tree/step1/test_tree.py | 25 +++++ 17-it-generator/tree/step1/tree.py | 10 ++ 17-it-generator/tree/step2/test_tree.py | 25 +++++ 17-it-generator/tree/step2/tree.py | 14 +++ 17-it-generator/tree/step3/test_tree.py | 38 +++++++ 17-it-generator/tree/step3/tree.py | 16 +++ 17-it-generator/tree/step4/test_tree.py | 76 +++++++++++++ 17-it-generator/tree/step4/tree.py | 20 ++++ 17-it-generator/tree/step5/test_tree.py | 94 ++++++++++++++++ 17-it-generator/tree/step5/tree.py | 15 +++ 17-it-generator/tree/step6/test_tree.py | 94 ++++++++++++++++ 17-it-generator/tree/step6/tree.py | 10 ++ 17-it-generator/yield_delegate_fail.py | 4 +- 17-it-generator/yield_delegate_fix.py | 4 +- 35 files changed, 788 insertions(+), 31 deletions(-) create mode 100644 17-it-generator/tree/4steps/tree_step0.py create mode 100644 17-it-generator/tree/4steps/tree_step1.py create mode 100644 17-it-generator/tree/4steps/tree_step2.py create mode 100644 17-it-generator/tree/4steps/tree_step3.py create mode 100644 17-it-generator/tree/extra/pretty_tree.py create mode 100644 17-it-generator/tree/extra/test_pretty_tree.py create mode 100644 17-it-generator/tree/extra/test_tree.py create mode 100644 17-it-generator/tree/extra/tree.py create mode 100644 17-it-generator/tree/step0/test_tree.py create mode 100644 17-it-generator/tree/step0/tree.py create mode 100644 17-it-generator/tree/step1/test_tree.py create mode 100644 17-it-generator/tree/step1/tree.py create mode 100644 17-it-generator/tree/step2/test_tree.py create mode 100644 17-it-generator/tree/step2/tree.py create mode 100644 17-it-generator/tree/step3/test_tree.py create mode 100644 17-it-generator/tree/step3/tree.py create mode 100644 17-it-generator/tree/step4/test_tree.py create mode 100644 17-it-generator/tree/step4/tree.py create mode 100644 17-it-generator/tree/step5/test_tree.py create mode 100644 17-it-generator/tree/step5/tree.py create mode 100644 17-it-generator/tree/step6/test_tree.py create mode 100644 17-it-generator/tree/step6/tree.py diff --git a/17-it-generator/aritprog_v0.py b/17-it-generator/aritprog_v0.py index ab26176..dbbaa20 100644 --- a/17-it-generator/aritprog_v0.py +++ b/17-it-generator/aritprog_v0.py @@ -17,7 +17,8 @@ class ArithmeticProgression: self.end = end # None -> "infinite" series def __iter__(self): - result = type(self.begin + self.step)(self.begin) + result_type = type(self.begin + self.step) + result = result_type(self.begin) forever = self.end is None while forever or result < self.end: yield result diff --git a/17-it-generator/aritprog_v1.py b/17-it-generator/aritprog_v1.py index 6019d14..2ae66c4 100644 --- a/17-it-generator/aritprog_v1.py +++ b/17-it-generator/aritprog_v1.py @@ -1,7 +1,7 @@ """ Arithmetic progression class -# BEGIN ARITPROG_CLASS_DEMO +# tag::ARITPROG_CLASS_DEMO[] >>> ap = ArithmeticProgression(0, 1, 3) >>> list(ap) @@ -21,24 +21,25 @@ Arithmetic progression class >>> list(ap) [Decimal('0.0'), Decimal('0.1'), Decimal('0.2')] -# END ARITPROG_CLASS_DEMO +# end::ARITPROG_CLASS_DEMO[] """ -# BEGIN ARITPROG_CLASS +# tag::ARITPROG_CLASS[] class ArithmeticProgression: - def __init__(self, begin, step, end=None): # <1> + def __init__(self, begin, step, end=None): # <1> self.begin = begin self.step = step self.end = end # None -> "infinite" series def __iter__(self): - result = type(self.begin + self.step)(self.begin) # <2> - forever = self.end is None # <3> + result_type = type(self.begin + self.step) # <2> + result = result_type(self.begin) # <3> + forever = self.end is None # <4> index = 0 - while forever or result < self.end: # <4> - yield result # <5> + while forever or result < self.end: # <5> + yield result # <6> index += 1 - result = self.begin + self.step * index # <6> -# END ARITPROG_CLASS + result = self.begin + self.step * index # <7> +# end::ARITPROG_CLASS[] diff --git a/17-it-generator/aritprog_v2.py b/17-it-generator/aritprog_v2.py index 03e42a4..be211d8 100644 --- a/17-it-generator/aritprog_v2.py +++ b/17-it-generator/aritprog_v2.py @@ -19,7 +19,7 @@ Arithmetic progression generator function:: """ -# BEGIN ARITPROG_GENFUNC +# tag::ARITPROG_GENFUNC[] def aritprog_gen(begin, step, end=None): result = type(begin + step)(begin) forever = end is None @@ -28,4 +28,4 @@ def aritprog_gen(begin, step, end=None): yield result index += 1 result = begin + step * index -# END ARITPROG_GENFUNC +# end::ARITPROG_GENFUNC[] diff --git a/17-it-generator/aritprog_v3.py b/17-it-generator/aritprog_v3.py index ad83add..3dd8e18 100644 --- a/17-it-generator/aritprog_v3.py +++ b/17-it-generator/aritprog_v3.py @@ -1,4 +1,4 @@ -# BEGIN ARITPROG_ITERTOOLS +# tag::ARITPROG_ITERTOOLS[] import itertools @@ -8,4 +8,4 @@ def aritprog_gen(begin, step, end=None): if end is not None: ap_gen = itertools.takewhile(lambda n: n < end, ap_gen) return ap_gen -# END ARITPROG_ITERTOOLS +# end::ARITPROG_ITERTOOLS[] diff --git a/17-it-generator/fibo_by_hand.py b/17-it-generator/fibo_by_hand.py index 9d35ed3..9bf8ab4 100644 --- a/17-it-generator/fibo_by_hand.py +++ b/17-it-generator/fibo_by_hand.py @@ -8,7 +8,7 @@ Fibonacci generator implemented "by hand" without generator objects """ -# BEGIN FIBO_BY_HAND +# tag::FIBO_BY_HAND[] class Fibonacci: def __iter__(self): @@ -28,7 +28,7 @@ class FibonacciGenerator: def __iter__(self): return self -# END FIBO_BY_HAND +# end::FIBO_BY_HAND[] # for comparison, this is the usual implementation of a Fibonacci # generator in Python: diff --git a/17-it-generator/isis2json/isis2json.py b/17-it-generator/isis2json/isis2json.py index a488125..065bccd 100755 --- a/17-it-generator/isis2json/isis2json.py +++ b/17-it-generator/isis2json/isis2json.py @@ -200,11 +200,12 @@ def main(): # <4> ' for bulk insert to CouchDB via POST to db/_bulk_docs') parser.add_argument( '-m', '--mongo', action='store_true', - help='output individual records as separate JSON dictionaries,' - ' one per line for bulk insert to MongoDB via mongoimport utility') + help='output individual records as separate JSON dictionaries, one' + ' per line for bulk insert to MongoDB via mongoimport utility') parser.add_argument( '-t', '--type', type=int, metavar='ISIS_JSON_TYPE', default=1, - help='ISIS-JSON type, sets field structure: 1=string, 2=alist, 3=dict (default=1)') + help='ISIS-JSON type, sets field structure: 1=string, 2=alist,' + ' 3=dict (default=1)') parser.add_argument( '-q', '--qty', type=int, default=DEFAULT_QTY, help='maximum quantity of records to read (default=ALL)') @@ -220,7 +221,8 @@ def main(): # <4> help='generate an "_id" with a random UUID for each record') parser.add_argument( '-p', '--prefix', type=str, metavar='PREFIX', default='', - help='concatenate prefix to every numeric field tag (ex. 99 becomes "v99")') + help='concatenate prefix to every numeric field tag' + ' (ex. 99 becomes "v99")') parser.add_argument( '-n', '--mfn', action='store_true', help='generate an "_id" from the MFN of each record' diff --git a/17-it-generator/sentence.py b/17-it-generator/sentence.py index 6a20c15..5c0c5d4 100644 --- a/17-it-generator/sentence.py +++ b/17-it-generator/sentence.py @@ -1,7 +1,18 @@ """ Sentence: access words by index + + >>> text = 'To be, or not to be, that is the question' + >>> s = Sentence(text) + >>> len(s) + 10 + >>> s[1], s[5] + ('be', 'be') + >>> s + Sentence('To be, or no... the question') + """ +# tag::SENTENCE_SEQ[] import re import reprlib @@ -17,8 +28,10 @@ class Sentence: def __getitem__(self, index): return self.words[index] # <2> - def __len__(self, index): # <3> + def __len__(self): # <3> return len(self.words) def __repr__(self): return 'Sentence(%s)' % reprlib.repr(self.text) # <4> + +# end::SENTENCE_SEQ[] diff --git a/17-it-generator/sentence_gen.py b/17-it-generator/sentence_gen.py index 32a8225..3dbc606 100644 --- a/17-it-generator/sentence_gen.py +++ b/17-it-generator/sentence_gen.py @@ -2,6 +2,7 @@ Sentence: iterate over words using a generator function """ +# tag::SENTENCE_GEN[] import re import reprlib @@ -23,3 +24,5 @@ class Sentence: return # <3> # done! <4> + +# end::SENTENCE_GEN[] diff --git a/17-it-generator/sentence_gen2.py b/17-it-generator/sentence_gen2.py index b308100..503fecd 100644 --- a/17-it-generator/sentence_gen2.py +++ b/17-it-generator/sentence_gen2.py @@ -2,10 +2,11 @@ Sentence: iterate over words using a generator function """ +# tag::SENTENCE_GEN2[] import re import reprlib -RE_WORD = re.compile(r'\w+') +RE_WORD = re.compile('r\w+') class Sentence: @@ -19,3 +20,5 @@ class Sentence: def __iter__(self): for match in RE_WORD.finditer(self.text): # <2> yield match.group() # <3> + +# end::SENTENCE_GEN2[] diff --git a/17-it-generator/sentence_genexp.py b/17-it-generator/sentence_genexp.py index 52228de..a5ff584 100644 --- a/17-it-generator/sentence_genexp.py +++ b/17-it-generator/sentence_genexp.py @@ -2,7 +2,7 @@ Sentence: iterate over words using a generator expression """ -# BEGIN SENTENCE_GENEXP +# tag::SENTENCE_GENEXP[] import re import reprlib @@ -19,7 +19,7 @@ class Sentence: def __iter__(self): return (match.group() for match in RE_WORD.finditer(self.text)) -# END SENTENCE_GENEXP +# end::SENTENCE_GENEXP[] def main(): diff --git a/17-it-generator/sentence_iter.py b/17-it-generator/sentence_iter.py index 11b8179..71ee3f3 100644 --- a/17-it-generator/sentence_iter.py +++ b/17-it-generator/sentence_iter.py @@ -5,7 +5,7 @@ WARNING: the Iterator Pattern is much simpler in idiomatic Python; see: sentence_gen*.py. """ -# BEGIN SENTENCE_ITER +# tag::SENTENCE_ITER[] import re import reprlib @@ -41,7 +41,7 @@ class SentenceIterator: def __iter__(self): # <9> return self -# END SENTENCE_ITER +# end::SENTENCE_ITER[] def main(): import sys diff --git a/17-it-generator/tree/4steps/tree_step0.py b/17-it-generator/tree/4steps/tree_step0.py new file mode 100644 index 0000000..4236a2e --- /dev/null +++ b/17-it-generator/tree/4steps/tree_step0.py @@ -0,0 +1,7 @@ +def tree(cls): + yield cls.__name__ + + +if __name__ == '__main__': + for cls_name in tree(BaseException): + print(cls_name) diff --git a/17-it-generator/tree/4steps/tree_step1.py b/17-it-generator/tree/4steps/tree_step1.py new file mode 100644 index 0000000..54f6ae8 --- /dev/null +++ b/17-it-generator/tree/4steps/tree_step1.py @@ -0,0 +1,10 @@ +def tree(cls): + yield cls.__name__, 0 + for sub_cls in cls.__subclasses__(): + yield sub_cls.__name__, 1 + + +if __name__ == '__main__': + for cls_name, level in tree(BaseException): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') diff --git a/17-it-generator/tree/4steps/tree_step2.py b/17-it-generator/tree/4steps/tree_step2.py new file mode 100644 index 0000000..10c3e9b --- /dev/null +++ b/17-it-generator/tree/4steps/tree_step2.py @@ -0,0 +1,12 @@ +def tree(cls): + yield cls.__name__, 0 + for sub_cls in cls.__subclasses__(): + yield sub_cls.__name__, 1 + for sub_sub_cls in sub_cls.__subclasses__(): + yield sub_sub_cls.__name__, 2 + + +if __name__ == '__main__': + for cls_name, level in tree(BaseException): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') diff --git a/17-it-generator/tree/4steps/tree_step3.py b/17-it-generator/tree/4steps/tree_step3.py new file mode 100644 index 0000000..7dfea68 --- /dev/null +++ b/17-it-generator/tree/4steps/tree_step3.py @@ -0,0 +1,10 @@ +def tree(cls, level=0): + yield cls.__name__, level + for sub_cls in cls.__subclasses__(): + yield from tree(sub_cls, level + 1) + + +if __name__ == '__main__': + for cls_name, level in tree(BaseException): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') diff --git a/17-it-generator/tree/extra/pretty_tree.py b/17-it-generator/tree/extra/pretty_tree.py new file mode 100644 index 0000000..1700194 --- /dev/null +++ b/17-it-generator/tree/extra/pretty_tree.py @@ -0,0 +1,31 @@ +from tree import tree + +SPACES = ' ' * 4 +HLINE = '\u2500' # ─ BOX DRAWINGS LIGHT HORIZONTAL +HLINE2 = HLINE * 2 +ELBOW = f'\u2514{HLINE2} ' # └ BOX DRAWINGS LIGHT UP AND RIGHT +TEE = f'\u251C{HLINE2} ' # ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT +PIPE = f'\u2502 ' # │ BOX DRAWINGS LIGHT VERTICAL + + +def render_lines(tree_iter): + name, _, _ = next(tree_iter) + yield name + prefix = '' + + for name, level, last in tree_iter: + if last: + connector = ELBOW + else: + connector = TEE + + prefix = prefix[:4 * (level-1)] + prefix = prefix.replace(TEE, PIPE).replace(ELBOW, SPACES) + prefix += connector + + yield prefix + name + + +if __name__ == '__main__': + for line in render_lines(tree(BaseException)): + print(line) \ No newline at end of file diff --git a/17-it-generator/tree/extra/test_pretty_tree.py b/17-it-generator/tree/extra/test_pretty_tree.py new file mode 100644 index 0000000..212dbd9 --- /dev/null +++ b/17-it-generator/tree/extra/test_pretty_tree.py @@ -0,0 +1,105 @@ +import pytest + +from pretty_tree import tree, render_lines + +def test_1_level(): + result = list(render_lines(tree(BrokenPipeError))) + expected = [ + 'BrokenPipeError', + ] + assert expected == result + + +def test_2_levels_1_leaf(): + result = list(render_lines(tree(IndentationError))) + expected = [ + 'IndentationError', + '└── TabError', + ] + assert expected == result + + +def test_3_levels_1_leaf(): + class X: pass + class Y(X): pass + class Z(Y): pass + result = list(render_lines(tree(X))) + expected = [ + 'X', + '└── Y', + ' └── Z', + ] + assert expected == result + + +def test_4_levels_1_leaf(): + class Level0: pass + class Level1(Level0): pass + class Level2(Level1): pass + class Level3(Level2): pass + + result = list(render_lines(tree(Level0))) + expected = [ + 'Level0', + '└── Level1', + ' └── Level2', + ' └── Level3', + ] + assert expected == result + + +def test_2_levels_4_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + class Leaf3(Branch): pass + class Leaf4(Branch): pass + result = list(render_lines(tree(Branch))) + expected = [ + 'Branch', + '├── Leaf1', + '├── Leaf2', + '├── Leaf3', + '└── Leaf4', + ] + assert expected == result + + +def test_3_levels_2_leaves(): + class A: pass + class B(A): pass + class C(B): pass + class D(A): pass + class E(D): pass + + result = list(render_lines(tree(A))) + expected = [ + 'A', + '├── B', + '│ └── C', + '└── D', + ' └── E', + ] + assert expected == result + + +def test_4_levels_4_leaves(): + class A: pass + class B1(A): pass + class C1(B1): pass + class D1(C1): pass + class D2(C1): pass + class C2(B1): pass + class B2(A): pass + expected = [ + 'A', + '├── B1', + '│ ├── C1', + '│ │ ├── D1', + '│ │ └── D2', + '│ └── C2', + '└── B2', + ] + + result = list(render_lines(tree(A))) + assert expected == result diff --git a/17-it-generator/tree/extra/test_tree.py b/17-it-generator/tree/extra/test_tree.py new file mode 100644 index 0000000..ed35226 --- /dev/null +++ b/17-it-generator/tree/extra/test_tree.py @@ -0,0 +1,94 @@ +from tree import tree + + +def test_1_level(): + class One: pass + expected = [('One', 0, True)] + result = list(tree(One)) + assert expected == result + + +def test_2_levels_4_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + class Leaf3(Branch): pass + class Leaf4(Branch): pass + expected = [ + ('Branch', 0, True), + ('Leaf1', 1, False), + ('Leaf2', 1, False), + ('Leaf3', 1, False), + ('Leaf4', 1, True), + ] + result = list(tree(Branch)) + assert expected == result + + +def test_3_levels_1_leaf(): + class X: pass + class Y(X): pass + class Z(Y): pass + expected = [ + ('X', 0, True), + ('Y', 1, True), + ('Z', 2, True), + ] + result = list(tree(X)) + assert expected == result + + +def test_4_levels_1_leaf(): + class Level0: pass + class Level1(Level0): pass + class Level2(Level1): pass + class Level3(Level2): pass + expected = [ + ('Level0', 0, True), + ('Level1', 1, True), + ('Level2', 2, True), + ('Level3', 3, True), + ] + + result = list(tree(Level0)) + assert expected == result + + +def test_4_levels_3_leaves(): + class A: pass + class B1(A): pass + class C1(B1): pass + class D1(C1): pass + class B2(A): pass + class D2(C1): pass + class C2(B2): pass + expected = [ + ('A', 0, True), + ('B1', 1, False), + ('C1', 2, True), + ('D1', 3, False), + ('D2', 3, True), + ('B2', 1, True), + ('C2', 2, True), + ] + + result = list(tree(A)) + assert expected == result + + +def test_many_levels_1_leaf(): + class Root: pass + level_count = 100 + expected = [('Root', 0, True)] + parent = Root + for level in range(1, level_count): + name = f'Sub{level}' + cls = type(name, (parent,), {}) + expected.append((name, level, True)) + parent = cls + + result = list(tree(Root)) + assert len(result) == level_count + assert result[0] == ('Root', 0, True) + assert result[-1] == ('Sub99', 99, True) + assert expected == result diff --git a/17-it-generator/tree/extra/tree.py b/17-it-generator/tree/extra/tree.py new file mode 100644 index 0000000..5eeed9d --- /dev/null +++ b/17-it-generator/tree/extra/tree.py @@ -0,0 +1,13 @@ +def tree(cls, level=0, last_in_level=True): + yield cls.__name__, level, last_in_level + subclasses = cls.__subclasses__() + if subclasses: + last = subclasses[-1] + for sub_cls in subclasses: + yield from tree(sub_cls, level+1, sub_cls is last) + + +if __name__ == '__main__': + for cls_name, level, _ in tree(BaseException): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') diff --git a/17-it-generator/tree/step0/test_tree.py b/17-it-generator/tree/step0/test_tree.py new file mode 100644 index 0000000..1ab22ae --- /dev/null +++ b/17-it-generator/tree/step0/test_tree.py @@ -0,0 +1,8 @@ +from tree import tree + + +def test_1_level(): + class One: pass + expected = ['One'] + result = list(tree(One)) + assert expected == result diff --git a/17-it-generator/tree/step0/tree.py b/17-it-generator/tree/step0/tree.py new file mode 100644 index 0000000..4236a2e --- /dev/null +++ b/17-it-generator/tree/step0/tree.py @@ -0,0 +1,7 @@ +def tree(cls): + yield cls.__name__ + + +if __name__ == '__main__': + for cls_name in tree(BaseException): + print(cls_name) diff --git a/17-it-generator/tree/step1/test_tree.py b/17-it-generator/tree/step1/test_tree.py new file mode 100644 index 0000000..1f6bb5b --- /dev/null +++ b/17-it-generator/tree/step1/test_tree.py @@ -0,0 +1,25 @@ +from tree import tree + + +def test_1_level(): + class One: pass + expected = [('One', 0)] + result = list(tree(One)) + assert expected == result + + +def test_2_levels_4_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + class Leaf3(Branch): pass + class Leaf4(Branch): pass + expected = [ + ('Branch', 0), + ('Leaf1', 1), + ('Leaf2', 1), + ('Leaf3', 1), + ('Leaf4', 1), + ] + result = list(tree(Branch)) + assert expected == result diff --git a/17-it-generator/tree/step1/tree.py b/17-it-generator/tree/step1/tree.py new file mode 100644 index 0000000..74a7489 --- /dev/null +++ b/17-it-generator/tree/step1/tree.py @@ -0,0 +1,10 @@ +def tree(cls): + yield cls.__name__, 0 # <1> + for sub_cls in cls.__subclasses__(): # <2> + yield sub_cls.__name__, 1 # <3> + + +if __name__ == '__main__': + for cls_name, level in tree(BaseException): + indent = ' ' * 4 * level # <4> + print(f'{indent}{cls_name}') diff --git a/17-it-generator/tree/step2/test_tree.py b/17-it-generator/tree/step2/test_tree.py new file mode 100644 index 0000000..1f6bb5b --- /dev/null +++ b/17-it-generator/tree/step2/test_tree.py @@ -0,0 +1,25 @@ +from tree import tree + + +def test_1_level(): + class One: pass + expected = [('One', 0)] + result = list(tree(One)) + assert expected == result + + +def test_2_levels_4_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + class Leaf3(Branch): pass + class Leaf4(Branch): pass + expected = [ + ('Branch', 0), + ('Leaf1', 1), + ('Leaf2', 1), + ('Leaf3', 1), + ('Leaf4', 1), + ] + result = list(tree(Branch)) + assert expected == result diff --git a/17-it-generator/tree/step2/tree.py b/17-it-generator/tree/step2/tree.py new file mode 100644 index 0000000..9807b0d --- /dev/null +++ b/17-it-generator/tree/step2/tree.py @@ -0,0 +1,14 @@ +def tree(cls): + yield cls.__name__, 0 + yield from sub_tree(cls) # <1> + + +def sub_tree(cls): + for sub_cls in cls.__subclasses__(): + yield sub_cls.__name__, 1 # <2> + + +if __name__ == '__main__': + for cls_name, level in tree(BaseException): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') diff --git a/17-it-generator/tree/step3/test_tree.py b/17-it-generator/tree/step3/test_tree.py new file mode 100644 index 0000000..c7f6ee6 --- /dev/null +++ b/17-it-generator/tree/step3/test_tree.py @@ -0,0 +1,38 @@ +from tree import tree + + +def test_1_level(): + class One: pass + expected = [('One', 0)] + result = list(tree(One)) + assert expected == result + + +def test_2_levels_4_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + class Leaf3(Branch): pass + class Leaf4(Branch): pass + expected = [ + ('Branch', 0), + ('Leaf1', 1), + ('Leaf2', 1), + ('Leaf3', 1), + ('Leaf4', 1), + ] + result = list(tree(Branch)) + assert expected == result + + +def test_3_levels_1_leaf(): + class X: pass + class Y(X): pass + class Z(Y): pass + expected = [ + ('X', 0), + ('Y', 1), + ('Z', 2), + ] + result = list(tree(X)) + assert expected == result diff --git a/17-it-generator/tree/step3/tree.py b/17-it-generator/tree/step3/tree.py new file mode 100644 index 0000000..880b400 --- /dev/null +++ b/17-it-generator/tree/step3/tree.py @@ -0,0 +1,16 @@ +def tree(cls): + yield cls.__name__, 0 + yield from sub_tree(cls) + + +def sub_tree(cls): + for sub_cls in cls.__subclasses__(): + yield sub_cls.__name__, 1 + for sub_sub_cls in sub_cls.__subclasses__(): + yield sub_sub_cls.__name__, 2 + + +if __name__ == '__main__': + for cls_name, level in tree(BaseException): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') diff --git a/17-it-generator/tree/step4/test_tree.py b/17-it-generator/tree/step4/test_tree.py new file mode 100644 index 0000000..db34d29 --- /dev/null +++ b/17-it-generator/tree/step4/test_tree.py @@ -0,0 +1,76 @@ +from tree import tree + + +def test_1_level(): + class One: pass + expected = [('One', 0)] + result = list(tree(One)) + assert expected == result + + +def test_2_levels_4_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + class Leaf3(Branch): pass + class Leaf4(Branch): pass + expected = [ + ('Branch', 0), + ('Leaf1', 1), + ('Leaf2', 1), + ('Leaf3', 1), + ('Leaf4', 1), + ] + result = list(tree(Branch)) + assert expected == result + + +def test_3_levels_1_leaf(): + class X: pass + class Y(X): pass + class Z(Y): pass + expected = [ + ('X', 0), + ('Y', 1), + ('Z', 2), + ] + result = list(tree(X)) + assert expected == result + + +def test_4_levels_1_leaf(): + class Level0: pass + class Level1(Level0): pass + class Level2(Level1): pass + class Level3(Level2): pass + expected = [ + ('Level0', 0), + ('Level1', 1), + ('Level2', 2), + ('Level3', 3), + ] + + result = list(tree(Level0)) + assert expected == result + + +def test_4_levels_3_leaves(): + class A: pass + class B1(A): pass + class C1(B1): pass + class D1(C1): pass + class B2(A): pass + class D2(C1): pass + class C2(B2): pass + expected = [ + ('A', 0), + ('B1', 1), + ('C1', 2), + ('D1', 3), + ('D2', 3), + ('B2', 1), + ('C2', 2), + ] + + result = list(tree(A)) + assert expected == result diff --git a/17-it-generator/tree/step4/tree.py b/17-it-generator/tree/step4/tree.py new file mode 100644 index 0000000..53103c8 --- /dev/null +++ b/17-it-generator/tree/step4/tree.py @@ -0,0 +1,20 @@ +def tree(cls): + yield cls.__name__, 0 + yield from sub_tree(cls) + + +# tag::SUB_TREE[] +def sub_tree(cls): + for sub_cls in cls.__subclasses__(): + yield sub_cls.__name__, 1 + for sub_sub_cls in sub_cls.__subclasses__(): + yield sub_sub_cls.__name__, 2 + for sub_sub_sub_cls in sub_sub_cls.__subclasses__(): + yield sub_sub_sub_cls.__name__, 3 +# end::SUB_TREE[] + + +if __name__ == '__main__': + for cls_name, level in tree(BaseException): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') diff --git a/17-it-generator/tree/step5/test_tree.py b/17-it-generator/tree/step5/test_tree.py new file mode 100644 index 0000000..aa23fa9 --- /dev/null +++ b/17-it-generator/tree/step5/test_tree.py @@ -0,0 +1,94 @@ +from tree import tree + + +def test_1_level(): + class One: pass + expected = [('One', 0)] + result = list(tree(One)) + assert expected == result + + +def test_2_levels_4_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + class Leaf3(Branch): pass + class Leaf4(Branch): pass + expected = [ + ('Branch', 0), + ('Leaf1', 1), + ('Leaf2', 1), + ('Leaf3', 1), + ('Leaf4', 1), + ] + result = list(tree(Branch)) + assert expected == result + + +def test_3_levels_1_leaf(): + class X: pass + class Y(X): pass + class Z(Y): pass + expected = [ + ('X', 0), + ('Y', 1), + ('Z', 2), + ] + result = list(tree(X)) + assert expected == result + + +def test_4_levels_1_leaf(): + class Level0: pass + class Level1(Level0): pass + class Level2(Level1): pass + class Level3(Level2): pass + expected = [ + ('Level0', 0), + ('Level1', 1), + ('Level2', 2), + ('Level3', 3), + ] + + result = list(tree(Level0)) + assert expected == result + + +def test_4_levels_3_leaves(): + class A: pass + class B1(A): pass + class C1(B1): pass + class D1(C1): pass + class B2(A): pass + class D2(C1): pass + class C2(B2): pass + expected = [ + ('A', 0), + ('B1', 1), + ('C1', 2), + ('D1', 3), + ('D2', 3), + ('B2', 1), + ('C2', 2), + ] + + result = list(tree(A)) + assert expected == result + + +def test_many_levels_1_leaf(): + class Root: pass + level_count = 100 + expected = [('Root', 0)] + parent = Root + for level in range(1, level_count): + name = f'Sub{level}' + cls = type(name, (parent,), {}) + expected.append((name, level)) + parent = cls + + result = list(tree(Root)) + assert len(result) == level_count + assert result[0] == ('Root', 0) + assert result[-1] == ('Sub99', 99) + assert expected == result diff --git a/17-it-generator/tree/step5/tree.py b/17-it-generator/tree/step5/tree.py new file mode 100644 index 0000000..a40e2a4 --- /dev/null +++ b/17-it-generator/tree/step5/tree.py @@ -0,0 +1,15 @@ +def tree(cls): + yield cls.__name__, 0 + yield from sub_tree(cls, 1) + + +def sub_tree(cls, level): + for sub_cls in cls.__subclasses__(): + yield sub_cls.__name__, level + yield from sub_tree(sub_cls, level+1) + + +if __name__ == '__main__': + for cls_name, level in tree(BaseException): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') diff --git a/17-it-generator/tree/step6/test_tree.py b/17-it-generator/tree/step6/test_tree.py new file mode 100644 index 0000000..aa23fa9 --- /dev/null +++ b/17-it-generator/tree/step6/test_tree.py @@ -0,0 +1,94 @@ +from tree import tree + + +def test_1_level(): + class One: pass + expected = [('One', 0)] + result = list(tree(One)) + assert expected == result + + +def test_2_levels_4_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + class Leaf3(Branch): pass + class Leaf4(Branch): pass + expected = [ + ('Branch', 0), + ('Leaf1', 1), + ('Leaf2', 1), + ('Leaf3', 1), + ('Leaf4', 1), + ] + result = list(tree(Branch)) + assert expected == result + + +def test_3_levels_1_leaf(): + class X: pass + class Y(X): pass + class Z(Y): pass + expected = [ + ('X', 0), + ('Y', 1), + ('Z', 2), + ] + result = list(tree(X)) + assert expected == result + + +def test_4_levels_1_leaf(): + class Level0: pass + class Level1(Level0): pass + class Level2(Level1): pass + class Level3(Level2): pass + expected = [ + ('Level0', 0), + ('Level1', 1), + ('Level2', 2), + ('Level3', 3), + ] + + result = list(tree(Level0)) + assert expected == result + + +def test_4_levels_3_leaves(): + class A: pass + class B1(A): pass + class C1(B1): pass + class D1(C1): pass + class B2(A): pass + class D2(C1): pass + class C2(B2): pass + expected = [ + ('A', 0), + ('B1', 1), + ('C1', 2), + ('D1', 3), + ('D2', 3), + ('B2', 1), + ('C2', 2), + ] + + result = list(tree(A)) + assert expected == result + + +def test_many_levels_1_leaf(): + class Root: pass + level_count = 100 + expected = [('Root', 0)] + parent = Root + for level in range(1, level_count): + name = f'Sub{level}' + cls = type(name, (parent,), {}) + expected.append((name, level)) + parent = cls + + result = list(tree(Root)) + assert len(result) == level_count + assert result[0] == ('Root', 0) + assert result[-1] == ('Sub99', 99) + assert expected == result diff --git a/17-it-generator/tree/step6/tree.py b/17-it-generator/tree/step6/tree.py new file mode 100644 index 0000000..41f36cb --- /dev/null +++ b/17-it-generator/tree/step6/tree.py @@ -0,0 +1,10 @@ +def tree(cls, level=0): + yield cls.__name__, level + for sub_cls in cls.__subclasses__(): + yield from tree(sub_cls, level+1) + + +if __name__ == '__main__': + for cls_name, level in tree(BaseException): + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') diff --git a/17-it-generator/yield_delegate_fail.py b/17-it-generator/yield_delegate_fail.py index 5b9a76f..3fd110a 100644 --- a/17-it-generator/yield_delegate_fail.py +++ b/17-it-generator/yield_delegate_fail.py @@ -14,7 +14,7 @@ Daniel Patterson, Junsong Li, Anand Chitipothu, and Shriram Krishnamurthi. DOI=10.1145/2544173.2509536 http://doi.acm.org/10.1145/2544173.2509536 """ -# BEGIN YIELD_DELEGATE_FAIL +# tag::YIELD_DELEGATE_FAIL[] def f(): def do_yield(n): yield n @@ -22,7 +22,7 @@ def f(): while True: x += 1 do_yield(x) -# END YIELD_DELEGATE_FAIL +# end::YIELD_DELEGATE_FAIL[] if __name__ == '__main__': print('Invoking f() results in an infinite loop') diff --git a/17-it-generator/yield_delegate_fix.py b/17-it-generator/yield_delegate_fix.py index abf06ab..9c5ce71 100644 --- a/17-it-generator/yield_delegate_fix.py +++ b/17-it-generator/yield_delegate_fix.py @@ -5,7 +5,7 @@ yielding. """ -# BEGIN YIELD_DELEGATE_FIX +# tag::YIELD_DELEGATE_FIX[] def f(): def do_yield(n): yield n @@ -13,7 +13,7 @@ def f(): while True: x += 1 yield from do_yield(x) -# END YIELD_DELEGATE_FIX +# end::YIELD_DELEGATE_FIX[] if __name__ == '__main__': print('Invoking f() now produces a generator')