removed 1st edition code

This commit is contained in:
Luciano Ramalho
2020-03-11 18:03:55 -03:00
parent 1be97eea3e
commit 506d343843
460 changed files with 0 additions and 108428 deletions

View File

@@ -1,4 +0,0 @@
Sample code for Chapter 14 - "Iterables, iterators and generators"
From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
http://shop.oreilly.com/product/0636920032519.do

View File

@@ -1,31 +0,0 @@
===========================================
Tests for arithmetic progression generators
===========================================
Tests with built-in numeric types::
>>> ap = aritprog_gen(1, .5, 3)
>>> list(ap)
[1.0, 1.5, 2.0, 2.5]
>>> ap = aritprog_gen(0, 1/3, 1)
>>> list(ap)
[0.0, 0.3333333333333333, 0.6666666666666666]
Tests with standard library numeric types::
>>> from fractions import Fraction
>>> ap = aritprog_gen(0, Fraction(1, 3), 1)
>>> list(ap)
[Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
>>> from decimal import Decimal
>>> ap = aritprog_gen(0, Decimal('.1'), .3)
>>> list(ap)
[Decimal('0'), Decimal('0.1'), Decimal('0.2')]
Test producing an empty series::
>>> ap = aritprog_gen(0, 1, 0)
>>> list(ap)
[]

View File

@@ -1,26 +0,0 @@
"""
Demonstrate difference between Arithmetic Progression calculated
as a series of increments accumulating errors versus one addition
and one multiplication.
"""
from fractions import Fraction
from aritprog_v0 import ArithmeticProgression as APv0
from aritprog_v1 import ArithmeticProgression as APv1
if __name__ == '__main__':
ap0 = iter(APv0(1, .1))
ap1 = iter(APv1(1, .1))
ap_frac = iter(APv1(Fraction(1, 1), Fraction(1, 10)))
epsilon = 10**-10
iteration = 0
delta = next(ap0) - next(ap1)
frac = next(ap_frac)
while abs(delta) <= epsilon:
delta = next(ap0) - next(ap1)
frac = next(ap_frac)
iteration +=1
print('iteration: {}\tfraction: {}\tepsilon: {}\tdelta: {}'.
format(iteration, frac, epsilon, delta))

View File

@@ -1,37 +0,0 @@
import doctest
import importlib
import glob
TARGET_GLOB = 'aritprog*.py'
TEST_FILE = 'aritprog.rst'
TEST_MSG = '{0:16} {1.attempted:2} tests, {1.failed:2} failed - {2}'
def main(argv):
verbose = '-v' in argv
for module_file_name in sorted(glob.glob(TARGET_GLOB)):
module_name = module_file_name.replace('.py', '')
module = importlib.import_module(module_name)
gen_factory = getattr(module, 'ArithmeticProgression', None)
if gen_factory is None:
gen_factory = getattr(module, 'aritprog_gen', None)
if gen_factory is None:
continue
test(gen_factory, verbose)
def test(gen_factory, verbose=False):
res = doctest.testfile(
TEST_FILE,
globs={'aritprog_gen': gen_factory},
verbose=verbose,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
tag = 'FAIL' if res.failed else 'OK'
print(TEST_MSG.format(gen_factory.__module__, res, tag))
if __name__ == '__main__':
import sys
main(sys.argv)

View File

@@ -1,25 +0,0 @@
"""
Arithmetic progression class
>>> ap = ArithmeticProgression(1, .5, 3)
>>> list(ap)
[1.0, 1.5, 2.0, 2.5]
"""
class ArithmeticProgression:
def __init__(self, begin, step, end=None):
self.begin = begin
self.step = step
self.end = end # None -> "infinite" series
def __iter__(self):
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
result += self.step

View File

@@ -1,45 +0,0 @@
"""
Arithmetic progression class
# tag::ARITPROG_CLASS_DEMO[]
>>> ap = ArithmeticProgression(0, 1, 3)
>>> list(ap)
[0, 1, 2]
>>> ap = ArithmeticProgression(1, .5, 3)
>>> list(ap)
[1.0, 1.5, 2.0, 2.5]
>>> ap = ArithmeticProgression(0, 1/3, 1)
>>> list(ap)
[0.0, 0.3333333333333333, 0.6666666666666666]
>>> from fractions import Fraction
>>> ap = ArithmeticProgression(0, Fraction(1, 3), 1)
>>> list(ap)
[Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
>>> from decimal import Decimal
>>> ap = ArithmeticProgression(0, Decimal('.1'), .3)
>>> list(ap)
[Decimal('0.0'), Decimal('0.1'), Decimal('0.2')]
# end::ARITPROG_CLASS_DEMO[]
"""
# tag::ARITPROG_CLASS[]
class ArithmeticProgression:
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 = 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: # <5>
yield result # <6>
index += 1
result = self.begin + self.step * index # <7>
# end::ARITPROG_CLASS[]

View File

@@ -1,31 +0,0 @@
"""
Arithmetic progression generator function::
>>> ap = aritprog_gen(1, .5, 3)
>>> list(ap)
[1.0, 1.5, 2.0, 2.5]
>>> ap = aritprog_gen(0, 1/3, 1)
>>> list(ap)
[0.0, 0.3333333333333333, 0.6666666666666666]
>>> from fractions import Fraction
>>> ap = aritprog_gen(0, Fraction(1, 3), 1)
>>> list(ap)
[Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
>>> from decimal import Decimal
>>> ap = aritprog_gen(0, Decimal('.1'), .3)
>>> list(ap)
[Decimal('0.0'), Decimal('0.1'), Decimal('0.2')]
"""
# tag::ARITPROG_GENFUNC[]
def aritprog_gen(begin, step, end=None):
result = type(begin + step)(begin)
forever = end is None
index = 0
while forever or result < end:
yield result
index += 1
result = begin + step * index
# end::ARITPROG_GENFUNC[]

View File

@@ -1,11 +0,0 @@
# tag::ARITPROG_ITERTOOLS[]
import itertools
def aritprog_gen(begin, step, end=None):
first = type(begin + step)(begin)
ap_gen = itertools.count(first, step)
if end is not None:
ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
return ap_gen
# end::ARITPROG_ITERTOOLS[]

View File

@@ -1,51 +0,0 @@
"""
Fibonacci generator implemented "by hand" without generator objects
>>> from itertools import islice
>>> list(islice(Fibonacci(), 15))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
"""
# tag::FIBO_BY_HAND[]
class Fibonacci:
def __iter__(self):
return FibonacciGenerator()
class FibonacciGenerator:
def __init__(self):
self.a = 0
self.b = 1
def __next__(self):
result = self.a
self.a, self.b = self.b, self.a + self.b
return result
def __iter__(self):
return self
# end::FIBO_BY_HAND[]
# for comparison, this is the usual implementation of a Fibonacci
# generator in Python:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
if __name__ == '__main__':
for x, y in zip(Fibonacci(), fibonacci()):
assert x == y, '%s != %s' % (x, y)
print(x)
if x > 10**10:
break
print('etc...')

View File

@@ -1,12 +0,0 @@
isis2json.py
============
This directory contains a copy of the ``isis2json.py`` script, with
minimal dependencies, just to allow the O'Reilly Atlas toolchain to
render the listing of the script in appendix A of the book.
If you want to use or contribute to this script, please get the full
source code with all dependencies from the main ``isis2json``
repository:
https://github.com/fluentpython/isis2json

View File

@@ -1,263 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# isis2json.py: convert ISIS and ISO-2709 files to JSON
#
# Copyright (C) 2010 BIREME/PAHO/WHO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
############################
# BEGIN ISIS2JSON
# this script works with Python or Jython (versions >=2.5 and <3)
import sys
import argparse
from uuid import uuid4
import os
try:
import json
except ImportError:
if os.name == 'java': # running Jython
from com.xhaus.jyson import JysonCodec as json
else:
import simplejson as json
SKIP_INACTIVE = True
DEFAULT_QTY = 2**31
ISIS_MFN_KEY = 'mfn'
ISIS_ACTIVE_KEY = 'active'
SUBFIELD_DELIMITER = '^'
INPUT_ENCODING = 'cp1252'
def iter_iso_records(iso_file_name, isis_json_type): # <1>
from iso2709 import IsoFile
from subfield import expand
iso = IsoFile(iso_file_name)
for record in iso:
fields = {}
for field in record.directory:
field_key = str(int(field.tag)) # remove leading zeroes
field_occurrences = fields.setdefault(field_key, [])
content = field.value.decode(INPUT_ENCODING, 'replace')
if isis_json_type == 1:
field_occurrences.append(content)
elif isis_json_type == 2:
field_occurrences.append(expand(content))
elif isis_json_type == 3:
field_occurrences.append(dict(expand(content)))
else:
raise NotImplementedError('ISIS-JSON type %s conversion '
'not yet implemented for .iso input' % isis_json_type)
yield fields
iso.close()
def iter_mst_records(master_file_name, isis_json_type): # <2>
try:
from bruma.master import MasterFactory, Record
except ImportError:
print('IMPORT ERROR: Jython 2.5 and Bruma.jar '
'are required to read .mst files')
raise SystemExit
mst = MasterFactory.getInstance(master_file_name).open()
for record in mst:
fields = {}
if SKIP_INACTIVE:
if record.getStatus() != Record.Status.ACTIVE:
continue
else: # save status only there are non-active records
fields[ISIS_ACTIVE_KEY] = (record.getStatus() ==
Record.Status.ACTIVE)
fields[ISIS_MFN_KEY] = record.getMfn()
for field in record.getFields():
field_key = str(field.getId())
field_occurrences = fields.setdefault(field_key, [])
if isis_json_type == 3:
content = {}
for subfield in field.getSubfields():
subfield_key = subfield.getId()
if subfield_key == '*':
content['_'] = subfield.getContent()
else:
subfield_occurrences = content.setdefault(subfield_key, [])
subfield_occurrences.append(subfield.getContent())
field_occurrences.append(content)
elif isis_json_type == 1:
content = []
for subfield in field.getSubfields():
subfield_key = subfield.getId()
if subfield_key == '*':
content.insert(0, subfield.getContent())
else:
content.append(SUBFIELD_DELIMITER + subfield_key +
subfield.getContent())
field_occurrences.append(''.join(content))
else:
raise NotImplementedError('ISIS-JSON type %s conversion '
'not yet implemented for .mst input' % isis_json_type)
yield fields
mst.close()
def write_json(input_gen, file_name, output, qty, skip, id_tag, # <3>
gen_uuid, mongo, mfn, isis_json_type, prefix,
constant):
start = skip
end = start + qty
if id_tag:
id_tag = str(id_tag)
ids = set()
else:
id_tag = ''
for i, record in enumerate(input_gen):
if i >= end:
break
if not mongo:
if i == 0:
output.write('[')
elif i > start:
output.write(',')
if start <= i < end:
if id_tag:
occurrences = record.get(id_tag, None)
if occurrences is None:
msg = 'id tag #%s not found in record %s'
if ISIS_MFN_KEY in record:
msg = msg + (' (mfn=%s)' % record[ISIS_MFN_KEY])
raise KeyError(msg % (id_tag, i))
if len(occurrences) > 1:
msg = 'multiple id tags #%s found in record %s'
if ISIS_MFN_KEY in record:
msg = msg + (' (mfn=%s)' % record[ISIS_MFN_KEY])
raise TypeError(msg % (id_tag, i))
else: # ok, we have one and only one id field
if isis_json_type == 1:
id = occurrences[0]
elif isis_json_type == 2:
id = occurrences[0][0][1]
elif isis_json_type == 3:
id = occurrences[0]['_']
if id in ids:
msg = 'duplicate id %s in tag #%s, record %s'
if ISIS_MFN_KEY in record:
msg = msg + (' (mfn=%s)' % record[ISIS_MFN_KEY])
raise TypeError(msg % (id, id_tag, i))
record['_id'] = id
ids.add(id)
elif gen_uuid:
record['_id'] = unicode(uuid4())
elif mfn:
record['_id'] = record[ISIS_MFN_KEY]
if prefix:
# iterate over a fixed sequence of tags
for tag in tuple(record):
if str(tag).isdigit():
record[prefix+tag] = record[tag]
del record[tag] # this is why we iterate over a tuple
# with the tags, and not directly on the record dict
if constant:
constant_key, constant_value = constant.split(':')
record[constant_key] = constant_value
output.write(json.dumps(record).encode('utf-8'))
output.write('\n')
if not mongo:
output.write(']\n')
def main(): # <4>
# create the parser
parser = argparse.ArgumentParser(
description='Convert an ISIS .mst or .iso file to a JSON array')
# add the arguments
parser.add_argument(
'file_name', metavar='INPUT.(mst|iso)',
help='.mst or .iso file to read')
parser.add_argument(
'-o', '--out', type=argparse.FileType('w'), default=sys.stdout,
metavar='OUTPUT.json',
help='the file where the JSON output should be written'
' (default: write to stdout)')
parser.add_argument(
'-c', '--couch', action='store_true',
help='output array within a "docs" item in a JSON document'
' 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')
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)')
parser.add_argument(
'-q', '--qty', type=int, default=DEFAULT_QTY,
help='maximum quantity of records to read (default=ALL)')
parser.add_argument(
'-s', '--skip', type=int, default=0,
help='records to skip from start of .mst (default=0)')
parser.add_argument(
'-i', '--id', type=int, metavar='TAG_NUMBER', default=0,
help='generate an "_id" from the given unique TAG field number'
' for each record')
parser.add_argument(
'-u', '--uuid', action='store_true',
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")')
parser.add_argument(
'-n', '--mfn', action='store_true',
help='generate an "_id" from the MFN of each record'
' (available only for .mst input)')
parser.add_argument(
'-k', '--constant', type=str, metavar='TAG:VALUE', default='',
help='Include a constant tag:value in every record (ex. -k type:AS)')
'''
# TODO: implement this to export large quantities of records to CouchDB
parser.add_argument(
'-r', '--repeat', type=int, default=1,
help='repeat operation, saving multiple JSON files'
' (default=1, use -r 0 to repeat until end of input)')
'''
# parse the command line
args = parser.parse_args()
if args.file_name.lower().endswith('.mst'):
input_gen_func = iter_mst_records # <5>
else:
if args.mfn:
print('UNSUPORTED: -n/--mfn option only available for .mst input.')
raise SystemExit
input_gen_func = iter_iso_records # <6>
input_gen = input_gen_func(args.file_name, args.type) # <7>
if args.couch:
args.out.write('{ "docs" : ')
write_json(input_gen, args.file_name, args.out, args.qty, # <8>
args.skip, args.id, args.uuid, args.mongo, args.mfn,
args.type, args.prefix, args.constant)
if args.couch:
args.out.write('}\n')
args.out.close()
if __name__ == '__main__':
main()
# END ISIS2JSON

View File

@@ -1,167 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# ISO-2709 file reader
#
# Copyright (C) 2010 BIREME/PAHO/WHO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from struct import unpack
CR = '\x0D' # \r
LF = '\x0A' # \n
IS1 = '\x1F' # ECMA-48 Unit Separator
IS2 = '\x1E' # ECMA-48 Record Separator / ISO-2709 field separator
IS3 = '\x1D' # ECMA-48 Group Separator / ISO-2709 record separator
LABEL_LEN = 24
LABEL_FORMAT = '5s c 4s c c 5s 3s c c c c'
TAG_LEN = 3
DEFAULT_ENCODING = 'ASCII'
SUBFIELD_DELIMITER = '^'
class IsoFile(object):
def __init__(self, filename, encoding = DEFAULT_ENCODING):
self.file = open(filename, 'rb')
self.encoding = encoding
def __iter__(self):
return self
def next(self):
return IsoRecord(self)
__next__ = next # Python 3 compatibility
def read(self, size):
''' read and drop all CR and LF characters '''
# TODO: this is inneficient but works, patches accepted!
# NOTE: our fixtures include files which have no linebreaks,
# files with CR-LF linebreaks and files with LF linebreaks
chunks = []
count = 0
while count < size:
chunk = self.file.read(size-count)
if len(chunk) == 0:
break
chunk = chunk.replace(CR+LF,'')
if CR in chunk:
chunk = chunk.replace(CR,'')
if LF in chunk:
chunk = chunk.replace(LF,'')
count += len(chunk)
chunks.append(chunk)
return ''.join(chunks)
def close(self):
self.file.close()
class IsoRecord(object):
label_part_names = ('rec_len rec_status impl_codes indicator_len identifier_len'
' base_addr user_defined'
# directory map:
' fld_len_len start_len impl_len reserved').split()
rec_len = 0
def __init__(self, iso_file=None):
self.iso_file = iso_file
self.load_label()
self.load_directory()
self.load_fields()
def __len__(self):
return self.rec_len
def load_label(self):
label = self.iso_file.read(LABEL_LEN)
if len(label) == 0:
raise StopIteration
elif len(label) != 24:
raise ValueError('Invalid record label: "%s"' % label)
parts = unpack(LABEL_FORMAT, label)
for name, part in zip(self.label_part_names, parts):
if name.endswith('_len') or name.endswith('_addr'):
part = int(part)
setattr(self, name, part)
def show_label(self):
for name in self.label_part_names:
print('%15s : %r' % (name, getattr(self, name)))
def load_directory(self):
fmt_dir = '3s %ss %ss %ss' % (self.fld_len_len, self.start_len, self.impl_len)
entry_len = TAG_LEN + self.fld_len_len + self.start_len + self.impl_len
self.directory = []
while True:
char = self.iso_file.read(1)
if char.isdigit():
entry = char + self.iso_file.read(entry_len-1)
entry = Field(* unpack(fmt_dir, entry))
self.directory.append(entry)
else:
break
def load_fields(self):
for field in self.directory:
if self.indicator_len > 0:
field.indicator = self.iso_file.read(self.indicator_len)
# XXX: lilacs30.iso has an identifier_len == 2,
# but we need to ignore it to succesfully read the field contents
# TODO: find out when to ignore the idenfier_len,
# or fix the lilacs30.iso fixture
#
##if self.identifier_len > 0: #
## field.identifier = self.iso_file.read(self.identifier_len)
value = self.iso_file.read(len(field))
assert len(value) == len(field)
field.value = value[:-1] # remove trailing field separator
self.iso_file.read(1) # discard record separator
def __iter__(self):
return self
def next(self):
for field in self.directory:
yield(field)
__next__ = next # Python 3 compatibility
def dump(self):
for field in self.directory:
print('%3s %r' % (field.tag, field.value))
class Field(object):
def __init__(self, tag, len, start, impl):
self.tag = tag
self.len = int(len)
self.start = int(start)
self.impl = impl
def show(self):
for name in 'tag len start impl'.split():
print('%15s : %r' % (name, getattr(self, name)))
def __len__(self):
return self.len
def test():
import doctest
doctest.testfile('iso2709_test.txt')
if __name__=='__main__':
test()

View File

@@ -1,142 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# ISIS-DM: the ISIS Data Model API
#
# Copyright (C) 2010 BIREME/PAHO/WHO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from collections import namedtuple
import re
MAIN_SUBFIELD_KEY = '_'
SUBFIELD_MARKER_RE = re.compile(r'\^([a-z0-9])', re.IGNORECASE)
DEFAULT_ENCODING = u'utf-8'
def expand(content, subkeys=None):
''' Parse a field into an association list of keys and subfields
>>> expand('zero^1one^2two^3three')
[('_', 'zero'), ('1', 'one'), ('2', 'two'), ('3', 'three')]
'''
if subkeys is None:
regex = SUBFIELD_MARKER_RE
elif subkeys == '':
return [(MAIN_SUBFIELD_KEY, content)]
else:
regex = re.compile(r'\^(['+subkeys+'])', re.IGNORECASE)
content = content.replace('^^', '^^ ')
parts = []
start = 0
key = MAIN_SUBFIELD_KEY
while True:
found = regex.search(content, start)
if found is None: break
parts.append((key, content[start:found.start()].rstrip()))
key = found.group(1).lower()
start = found.end()
parts.append((key, content[start:].rstrip()))
return parts
class CompositeString(object):
''' Represent an Isis field, with subfields, using
Python native datastructures
>>> author = CompositeString('John Tenniel^xillustrator',
... subkeys='x')
>>> unicode(author)
u'John Tenniel^xillustrator'
'''
def __init__(self, isis_raw, subkeys=None, encoding=DEFAULT_ENCODING):
if not isinstance(isis_raw, basestring):
raise TypeError('%r value must be unicode or str instance' % isis_raw)
self.__isis_raw = isis_raw.decode(encoding)
self.__expanded = expand(self.__isis_raw, subkeys)
def __getitem__(self, key):
for subfield in self.__expanded:
if subfield[0] == key:
return subfield[1]
else:
raise KeyError(key)
def __iter__(self):
return (subfield[0] for subfield in self.__expanded)
def items(self):
return self.__expanded
def __unicode__(self):
return self.__isis_raw
def __str__(self):
return str(self.__isis_raw)
class CompositeField(object):
''' Represent an Isis field, with subfields, using
Python native datastructures
>>> author = CompositeField( [('name','Braz, Marcelo'),('role','writer')] )
>>> print author['name']
Braz, Marcelo
>>> print author['role']
writer
>>> author
CompositeField((('name', 'Braz, Marcelo'), ('role', 'writer')))
'''
def __init__(self, value, subkeys=None):
if subkeys is None:
subkeys = [item[0] for item in value]
try:
value_as_dict = dict(value)
except TypeError:
raise TypeError('%r value must be a key-value structure' % self)
for key in value_as_dict:
if key not in subkeys:
raise TypeError('Unexpected keyword %r' % key)
self.value = tuple([(key, value_as_dict.get(key,None)) for key in subkeys])
def __getitem__(self, key):
return dict(self.value)[key]
def __repr__(self):
return "CompositeField(%s)" % str(self.items())
def items(self):
return self.value
def __unicode__(self):
unicode(self.items())
def __str__(self):
str(self.items())
def test():
import doctest
doctest.testmod()
if __name__=='__main__':
test()

View File

@@ -1,37 +0,0 @@
"""
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
RE_WORD = re.compile(r'\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text) # <1>
def __getitem__(self, index):
return self.words[index] # <2>
def __len__(self): # <3>
return len(self.words)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text) # <4>
# end::SENTENCE_SEQ[]

View File

@@ -1,54 +0,0 @@
==============================
Tests for a ``Sentence`` class
==============================
A ``Sentence`` is built from a ``str`` and allows iteration
word-by-word.
::
>>> s = Sentence('The time has come')
>>> s
Sentence('The time has come')
>>> list(s)
['The', 'time', 'has', 'come']
>>> it = iter(s)
>>> next(it)
'The'
>>> next(it)
'time'
>>> next(it)
'has'
>>> next(it)
'come'
>>> next(it)
Traceback (most recent call last):
...
StopIteration
Any punctuation is skipped while iterating::
>>> s = Sentence('"The time has come," the Walrus said,')
>>> s
Sentence('"The time ha... Walrus said,')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said']
White space including line breaks are also ignored::
>>> s = Sentence('''"The time has come," the Walrus said,
... "To talk of many things:"''')
>>> s
Sentence('"The time ha...many things:"')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said', 'To', 'talk', 'of', 'many', 'things']
Accented Latin characters are also recognized as word characters::
>>> s = Sentence('Agora vou-me. Ou me vão?')
>>> s
Sentence('Agora vou-me. Ou me vão?')
>>> list(s)
['Agora', 'vou', 'me', 'Ou', 'me', 'vão']

View File

@@ -1,28 +0,0 @@
"""
Sentence: iterate over words using a generator function
"""
# tag::SENTENCE_GEN[]
import re
import reprlib
RE_WORD = re.compile(r'\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
for word in self.words: # <1>
yield word # <2>
return # <3>
# done! <4>
# end::SENTENCE_GEN[]

View File

@@ -1,24 +0,0 @@
"""
Sentence: iterate over words using a generator function
"""
# tag::SENTENCE_GEN2[]
import re
import reprlib
RE_WORD = re.compile('r\w+')
class Sentence:
def __init__(self, text):
self.text = text # <1>
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
for match in RE_WORD.finditer(self.text): # <2>
yield match.group() # <3>
# end::SENTENCE_GEN2[]

View File

@@ -1,44 +0,0 @@
"""
Sentence: iterate over words using a generator expression
"""
# tag::SENTENCE_GENEXP[]
import re
import reprlib
RE_WORD = re.compile(r'\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
return (match.group() for match in RE_WORD.finditer(self.text))
# end::SENTENCE_GENEXP[]
def main():
import sys
import warnings
try:
filename = sys.argv[1]
word_number = int(sys.argv[2])
except (IndexError, ValueError):
print('Usage: %s <file-name> <word-number>' % sys.argv[0])
sys.exit(1)
with open(filename, 'rt', encoding='utf-8') as text_file:
s = Sentence(text_file.read())
for n, word in enumerate(s, 1):
if n == word_number:
print(word)
break
else:
warnings.warn('last word is #%d, "%s"' % (n, word))
if __name__ == '__main__':
main()

View File

@@ -1,65 +0,0 @@
"""
Sentence: iterate over words using the Iterator Pattern, take #1
WARNING: the Iterator Pattern is much simpler in idiomatic Python;
see: sentence_gen*.py.
"""
# tag::SENTENCE_ITER[]
import re
import reprlib
RE_WORD = re.compile(r'\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self): # <1>
return SentenceIterator(self.words) # <2>
class SentenceIterator:
def __init__(self, words):
self.words = words # <3>
self.index = 0 # <4>
def __next__(self):
try:
word = self.words[self.index] # <5>
except IndexError:
raise StopIteration() # <6>
self.index += 1 # <7>
return word # <8>
def __iter__(self): # <9>
return self
# end::SENTENCE_ITER[]
def main():
import sys
import warnings
try:
filename = sys.argv[1]
word_number = int(sys.argv[2])
except (IndexError, ValueError):
print('Usage: %s <file-name> <word-number>' % sys.argv[0])
sys.exit(1)
with open(filename, 'rt', encoding='utf-8') as text_file:
s = Sentence(text_file.read())
for n, word in enumerate(s, 1):
if n == word_number:
print(word)
break
else:
warnings.warn('last word is #%d, "%s"' % (n, word))
if __name__ == '__main__':
main()

View File

@@ -1,37 +0,0 @@
"""
Sentence: iterate over words using the Iterator Pattern, take #2
WARNING: the Iterator Pattern is much simpler in idiomatic Python;
see: sentence_gen*.py.
"""
import re
import reprlib
RE_WORD = re.compile(r'\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
word_iter = RE_WORD.finditer(self.text) # <1>
return SentenceIter(word_iter) # <2>
class SentenceIter():
def __init__(self, word_iter):
self.word_iter = word_iter # <3>
def __next__(self):
match = next(self.word_iter) # <4>
return match.group() # <5>
def __iter__(self):
return self

View File

@@ -1,36 +0,0 @@
import doctest
import importlib
import glob
TARGET_GLOB = 'sentence*.py'
TEST_FILE = 'sentence.rst'
TEST_MSG = '{0:16} {1.attempted:2} tests, {1.failed:2} failed - {2}'
def main(argv):
verbose = '-v' in argv
for module_file_name in sorted(glob.glob(TARGET_GLOB)):
module_name = module_file_name.replace('.py', '')
module = importlib.import_module(module_name)
try:
cls = getattr(module, 'Sentence')
except AttributeError:
continue
test(cls, verbose)
def test(cls, verbose=False):
res = doctest.testfile(
TEST_FILE,
globs={'Sentence': cls},
verbose=verbose,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
tag = 'FAIL' if res.failed else 'OK'
print(TEST_MSG.format(cls.__module__, res, tag))
if __name__ == '__main__':
import sys
main(sys.argv)

View File

@@ -1,7 +0,0 @@
def tree(cls):
yield cls.__name__
if __name__ == '__main__':
for cls_name in tree(BaseException):
print(cls_name)

View File

@@ -1,10 +0,0 @@
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}')

View File

@@ -1,12 +0,0 @@
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}')

View File

@@ -1,10 +0,0 @@
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}')

View File

@@ -1,31 +0,0 @@
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)

View File

@@ -1,101 +0,0 @@
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_2_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
result = list(render_lines(tree(Branch)))
expected = [
'Branch',
'├── Leaf1',
'└── Leaf2',
]
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

View File

@@ -1,90 +0,0 @@
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_2_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
expected = [
('Branch', 0, True),
('Leaf1', 1, False),
('Leaf2', 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

View File

@@ -1,13 +0,0 @@
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}')

View File

@@ -1,8 +0,0 @@
from tree import tree
def test_1_level():
class One: pass
expected = ['One']
result = list(tree(One))
assert expected == result

View File

@@ -1,7 +0,0 @@
def tree(cls):
yield cls.__name__
if __name__ == '__main__':
for cls_name in tree(BaseException):
print(cls_name)

View File

@@ -1,21 +0,0 @@
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_2_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 1),
]
result = list(tree(Branch))
assert expected == result

View File

@@ -1,10 +0,0 @@
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}')

View File

@@ -1,21 +0,0 @@
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_2_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 1),
]
result = list(tree(Branch))
assert expected == result

View File

@@ -1,14 +0,0 @@
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}')

View File

@@ -1,34 +0,0 @@
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_2_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 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

View File

@@ -1,16 +0,0 @@
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}')

View File

@@ -1,72 +0,0 @@
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_2_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 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

View File

@@ -1,20 +0,0 @@
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}')

View File

@@ -1,90 +0,0 @@
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_2_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 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

View File

@@ -1,15 +0,0 @@
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}')

View File

@@ -1,90 +0,0 @@
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_2_leaves():
class Branch: pass
class Leaf1(Branch): pass
class Leaf2(Branch): pass
expected = [
('Branch', 0),
('Leaf1', 1),
('Leaf2', 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 B2(A): pass
class C1(B1): pass
class C2(B2): pass
class D1(C1): pass
class D2(C1): 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

View File

@@ -1,10 +0,0 @@
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}')

View File

@@ -1,29 +0,0 @@
""" Example from `Python: The Full Monty`__ -- A Tested Semantics for the
Python Programming Language
__ http://cs.brown.edu/~sk/Publications/Papers/Published/pmmwplck-python-full-monty/
"The following program, [...] seems to perform a simple abstraction over the
process of yielding:"
Citation:
Joe Gibbs Politz, Alejandro Martinez, Matthew Milano, Sumner Warren,
Daniel Patterson, Junsong Li, Anand Chitipothu, and Shriram Krishnamurthi.
2013. Python: the full monty. SIGPLAN Not. 48, 10 (October 2013), 217-232.
DOI=10.1145/2544173.2509536 http://doi.acm.org/10.1145/2544173.2509536
"""
# tag::YIELD_DELEGATE_FAIL[]
def f():
def do_yield(n):
yield n
x = 0
while True:
x += 1
do_yield(x)
# end::YIELD_DELEGATE_FAIL[]
if __name__ == '__main__':
print('Invoking f() results in an infinite loop')
f()

View File

@@ -1,24 +0,0 @@
""" Example adapted from ``yield_delegate_fail.py``
The following program performs a simple abstraction over the process of
yielding.
"""
# tag::YIELD_DELEGATE_FIX[]
def f():
def do_yield(n):
yield n
x = 0
while True:
x += 1
yield from do_yield(x)
# end::YIELD_DELEGATE_FIX[]
if __name__ == '__main__':
print('Invoking f() now produces a generator')
g = f()
print(next(g))
print(next(g))
print(next(g))