update from Atlas with major reorg

This commit is contained in:
Luciano Ramalho 2015-04-17 21:29:30 -03:00
parent 57902d31b5
commit a786180239
134 changed files with 369 additions and 520 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@ concurrency/charfinder/charfinder_index.pickle
metaprog/oscon-schedule/data/schedule?_db
concurrency/wikipedia/fixture/docroot/
17-futures/countries/flags/
attic/futures/countries/flags/
# Byte-compiled / optimized / DLL files
__pycache__/

View File

@ -2,12 +2,13 @@
# BEGIN BINGO_DEMO
>>> bingo = BingoCage(range(3))
>>> bingo()
2
>>> bingo.pick()
1
>>> bingo()
0
>>> callable(bingo)
True
# END BINGO_DEMO
"""
@ -22,9 +23,13 @@ class BingoCage:
self._items = list(items) # <1>
random.shuffle(self._items) # <2>
def __call__(self):
if not self._items: # <3>
raise IndexError('pop from empty BingoCage')
def pick(self): # <3>
try:
return self._items.pop()
except IndexError:
raise LookupError('pick from empty BingoCage') # <4>
def __call__(self): # <5>
return self.pick()
# END BINGO

View File

@ -0,0 +1,43 @@
# clockdeco_param.py
"""
>>> snooze(.1) # doctest: +ELLIPSIS
[0.101...s] snooze(0.1) -> None
>>> clock('{name}: {elapsed}')(time.sleep)(.2) # doctest: +ELLIPSIS
sleep: 0.20...
>>> clock('{name}({args}) dt={elapsed:0.3f}s')(time.sleep)(.2)
sleep(0.2) dt=0.201s
"""
# BEGIN CLOCKDECO_CLS
import time
DEFAULT_FMT = '[{elapsed:0.8f}s] {name}({args}) -> {result}'
class clock:
def __init__(self, fmt=DEFAULT_FMT):
self.fmt = fmt
def __call__(self, func):
def clocked(*_args):
t0 = time.time()
_result = func(*_args)
elapsed = time.time() - t0
name = func.__name__
args = ', '.join(repr(arg) for arg in _args)
result = repr(_result)
print(self.fmt.format(**locals()))
return _result
return clocked
if __name__ == '__main__':
@clock()
def snooze(seconds):
time.sleep(seconds)
for i in range(3):
snooze(.123)
# END CLOCKDECO_CLS

View File

@ -1,3 +1,5 @@
# BEGIN TOMBOLA_BINGO
import random
from tombola import Tombola
@ -6,14 +8,21 @@ from tombola import Tombola
class BingoCage(Tombola): # <1>
def __init__(self, items):
self._balls = list(items) # <2>
self._randomizer = random.SystemRandom() # <2>
self._items = []
self.load(items) # <3>
def load(self, items):
self._balls.extend(items)
self._items.extend(items)
self._randomizer.shuffle(self._items) # <4>
def pick(self):
def pick(self): # <5>
try:
position = random.randrange(len(self._balls)) # <3>
except ValueError:
raise LookupError('pop from empty BingoCage')
return self._balls.pop(position) # <4>
return self._items.pop()
except IndexError:
raise LookupError('pick from empty BingoCage')
def __call__(self): # <7>
self.pick()
# END TOMBOLA_BINGO

View File

@ -1,3 +1,5 @@
# BEGIN LOTTERY_BLOWER
import random
from tombola import Tombola
@ -6,19 +8,23 @@ from tombola import Tombola
class LotteryBlower(Tombola):
def __init__(self, iterable):
self.randomizer = random.SystemRandom() # <1>
self.clear()
self.load(iterable)
def clear(self):
self._balls = []
self._balls = list(iterable) # <1>
def load(self, iterable):
self._balls.extend(iterable)
self.randomizer.shuffle(self._balls) # <2>
def pick(self):
return self._balls.pop() # <3>
try:
position = random.randrange(len(self._balls)) # <2>
except ValueError:
raise LookupError('pick from empty BingoCage')
return self._balls.pop(position) # <3>
def loaded(self): # <4>
return len(self._balls) > 0
return bool(self._balls)
def inspect(self): # <5>
return tuple(sorted(self._balls))
# END LOTTERY_BLOWER

View File

@ -1,3 +1,5 @@
# BEGIN TOMBOLA_ABC
import abc
class Tombola(abc.ABC): # <1>
@ -14,10 +16,20 @@ class Tombola(abc.ABC): # <1>
"""
def loaded(self): # <4>
"""Return `True` if there's at least 1 item, `False` otherwise."""
return bool(self.inspect()) # <5>
def inspect(self):
"""Return a sorted tuple with the items currently inside."""
items = []
while True: # <6>
try:
item = self.pick()
items.append(self.pick())
except LookupError:
return False
else:
self.load([item]) # put it back
return True
break
self.load(items) # <7>
return tuple(sorted(items))
# END TOMBOLA_ABC

View File

@ -11,6 +11,8 @@ Create and load instance from iterable::
>>> globe = ConcreteTombola(balls)
>>> globe.loaded()
True
>>> globe.inspect()
(0, 1, 2)
Pick and collect balls::
@ -55,7 +57,7 @@ Load and pick 100 balls to verify that they all come out::
>>> balls = list(range(100))
>>> globe = ConcreteTombola(balls)
>>> picks = []
>>> while globe.loaded():
>>> while globe.inspect():
... picks.append(globe.pick())
>>> len(picks) == len(balls)
True
@ -72,7 +74,7 @@ Check that the order has changed and is not simply reversed::
Note: the previous 2 tests have a *very* small chance of failing
even if the implementation is OK. The probability of the 100
balls coming out, by chance, in the order they were loaded is
balls coming out, by chance, in the order they were inspect is
1/100!, or approximately 1.07e-158. It's much easier to win the
Lotto or to become a billionaire working as a programmer.

View File

@ -12,8 +12,12 @@ class TomboList(list): # <2>
else:
raise LookupError('pop from empty TomboList')
def load(self, iterable): self.extend(iterable) # <5>
load = list.extend # <5>
def loaded(self): return bool(self) # <6>
def loaded(self):
return bool(self) # <6>
# Tombola.register(TomboList) # <- Python 3.2 or earlier
def inspect(self):
return tuple(sorted(self))
# Tombola.register(TomboList) # <7>

View File

@ -0,0 +1,28 @@
# BEGIN TOMBOLA_BINGO
import random
from tombola import Tombola
class BingoCage(Tombola): # <1>
def __init__(self, items):
self._randomizer = random.SystemRandom() # <2>
self._items = []
self.load(items) # <3>
def load(self, items):
self._items.extend(items)
self._randomizer.shuffle(self._items) # <4>
def pick(self): # <5>
try:
return self._items.pop()
except IndexError:
raise LookupError('pick from empty BingoCage')
def __call__(self): # <7>
self.pick()
# END TOMBOLA_BINGO

View File

@ -4,56 +4,83 @@ AddableBingoCage tests
======================
Tests for __add__ and __iadd__:
Tests for __add__:
# BEGIN ADDABLE_BINGO_ADD_DEMO
>>> vowels = 'AEIOU'
>>> globe = AddableBingoCage(vowels)
>>> len(globe)
5
>>> globe.pop() in vowels
>>> globe = AddableBingoCage(vowels) # <1>
>>> globe.inspect()
('A', 'E', 'I', 'O', 'U')
>>> globe.pick() in vowels # <2>
True
>>> len(globe)
>>> len(globe.inspect()) # <3>
4
>>> globe2 = AddableBingoCage('XYZ')
>>> globe2 = AddableBingoCage('XYZ') # <4>
>>> globe3 = globe + globe2
>>> len(globe3)
>>> len(globe3.inspect()) # <5>
7
>>> void = globe + [10, 20]
>>> void = globe + [10, 20] # <6>
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'AddableBingoCage' and 'list'
Tests for __add__ and __iadd__:
# END ADDABLE_BINGO_ADD_DEMO
>>> globe_orig = globe
>>> len(globe)
Tests for __iadd__:
# BEGIN ADDABLE_BINGO_IADD_DEMO
>>> globe_orig = globe # <1>
>>> len(globe.inspect()) # <2>
4
>>> globe += globe2
>>> len(globe)
>>> globe += globe2 # <3>
>>> len(globe.inspect())
7
>>> globe += [10, 20]
>>> len(globe)
>>> globe += ['M', 'N'] # <4>
>>> len(globe.inspect())
9
>>> globe is globe_orig
>>> globe is globe_orig # <5>
True
>>> globe += 1 # <6>
Traceback (most recent call last):
...
TypeError: right operand in += must be 'AddableBingoCage' or an iterable
# END ADDABLE_BINGO_IADD_DEMO
"""
# BEGIN ADDABLE_BINGO
import itertools # <1>
from bingobase import BingoCage
from tombola import Tombola
from bingo import BingoCage
class AddableBingoCage(BingoCage): # <2>
def __add__(self, other):
if isinstance(other, AddableBingoCage): # <3>
return AddableBingoCage(itertools.chain(self, other)) # <4>
if isinstance(other, Tombola): # <3>
return AddableBingoCage(self.inspect() + other.inspect()) # <6>
else:
return NotImplemented
def __iadd__(self, other):
self.load(other) # <5>
return self # <6>
if isinstance(other, Tombola):
other_iterable = other.inspect() # <4>
else:
try:
other_iterable = iter(other) # <5>
except TypeError: # <6>
self_cls = type(self).__name__
msg = "right operand in += must be {!r} or an iterable"
raise TypeError(msg.format(self_cls))
self.load(other_iterable) # <7>
return self # <8>
# END ADDABLE_BINGO

View File

@ -1,96 +0,0 @@
"""
===============
BingoCage tests
===============
Create and load instance from iterable::
>>> balls = list(range(3))
>>> globe = BingoCage(balls)
>>> len(globe)
3
Pop and collect balls::
>>> picks = []
>>> picks.append(globe.pop())
>>> picks.append(globe.pop())
>>> picks.append(globe.pop())
Check state and results::
>>> len(globe)
0
>>> sorted(picks) == balls
True
Reload::
>>> globe.load(balls)
>>> len(globe)
3
>>> picks = [globe.pop() for i in balls]
>>> len(globe)
0
Load and pop 20 balls to verify that the order has changed::
>>> balls = list(range(20))
>>> globe = BingoCage(balls)
>>> picks = []
>>> while globe:
... picks.append(globe.pop())
>>> len(picks) == len(balls)
True
>>> picks != balls
True
Also check that the order is not simply reversed either::
>>> picks[::-1] != balls
True
Note: last 2 tests above each have 1 chance in 20! (factorial) of
failing even if the implementation is OK. 1/20!, or approximately
4.11e-19, is the probability of the 20 balls coming out, by chance,
in the exact order the were loaded.
Check that `LookupError` (or a subclass) is the exception thrown
when the device is empty::
>>> globe = BingoCage([])
>>> try:
... globe.pop()
... except LookupError as exc:
... print('OK')
OK
"""
import random
class BingoCage():
def __init__(self, iterable):
self._balls = []
self.load(iterable)
def load(self, iterable):
self._balls.extend(iterable)
random.shuffle(self._balls)
def __len__(self):
return len(self._balls)
def pop(self):
return self._balls.pop()
def __iter__(self):
return reversed(self._balls)

View File

@ -0,0 +1,35 @@
# BEGIN TOMBOLA_ABC
import abc
class Tombola(abc.ABC): # <1>
@abc.abstractmethod
def load(self, iterable): # <2>
"""Add items from an iterable."""
@abc.abstractmethod
def pick(self): # <3>
"""Remove item at random, returning it.
This method should raise `LookupError` when the instance is empty.
"""
def loaded(self): # <4>
"""Return `True` if there's at least 1 item, `False` otherwise."""
return bool(self.inspect()) # <5>
def inspect(self):
"""Return a sorted tuple with the items currently inside."""
items = []
while True: # <6>
try:
items.append(self.pick())
except LookupError:
break
self.load(items) # <7>
return tuple(sorted(items))
# END TOMBOLA_ABC

View File

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

@ -32,7 +32,7 @@ MAX_CONCUR_REQ = 1000 # <5>
def download_many(cc_list, base_url, verbose, concur_req):
counter = collections.Counter()
with futures.ThreadPoolExecutor(concur_req) as executor: # <6>
with futures.ThreadPoolExecutor(max_workers=concur_req) as executor: # <6>
to_do_map = {} # <7>
for cc in sorted(cc_list): # <8>
future = executor.submit(download_one,

View File

@ -54,7 +54,7 @@ class NonBlank(Validated):
class EntityMeta(type):
"""Metaclass for business entities with validated fields"""
def __init__(self, name, bases, attr_dict):
def __init__(cls, name, bases, attr_dict):
super().__init__(name, bases, attr_dict) # <1>
for key, attr in attr_dict.items(): # <2>
if isinstance(attr, Validated):

View File

@ -59,14 +59,14 @@ class EntityMeta(type):
def __prepare__(cls, name, bases):
return collections.OrderedDict() # <1>
def __init__(self, name, bases, attr_dict):
def __init__(cls, name, bases, attr_dict):
super().__init__(name, bases, attr_dict)
self._field_names = [] # <2>
cls._field_names = [] # <2>
for key, attr in attr_dict.items(): # <3>
if isinstance(attr, Validated):
type_name = type(attr).__name__
attr.storage_name = '_{}#{}'.format(type_name, key)
self._field_names.append(key) # <4>
cls._field_names.append(key) # <4>
class Entity(metaclass=EntityMeta):

View File

@ -13,13 +13,13 @@ def deco_alpha(cls):
class MetaAleph(type):
print('<[400]> MetaAleph body')
def __init__(self, name, bases, dic):
def __init__(cls, name, bases, dic):
print('<[500]> MetaAleph.__init__')
def inner_2(self):
print('<[600]> MetaAleph.__init__:inner_2')
self.method_z = inner_2
cls.method_z = inner_2
# END META_ALEPH
print('<[700]> evalsupport module end')

View File

@ -9,8 +9,8 @@ record_factory: create simple classes just for holding data fields
>>> name, weight, _ = rex # <3>
>>> name, weight
('Rex', 30)
>>> "{2}'s dog weights {1}kg".format(*rex) # <4>
"Bob's dog weights 30kg"
>>> "{2}'s dog weighs {1}kg".format(*rex) # <4>
"Bob's dog weighs 30kg"
>>> rex.weight = 32 # <5>
>>> rex
Dog(name='Rex', weight=32, owner='Bob')
@ -18,33 +18,42 @@ record_factory: create simple classes just for holding data fields
(<class 'factories.Dog'>, <class 'object'>)
# END RECORD_FACTORY_DEMO
The factory also accepts a list or tuple of identifiers:
>>> Dog = record_factory('Dog', ['name', 'weight', 'owner'])
>>> Dog.__slots__
('name', 'weight', 'owner')
"""
# BEGIN RECORD_FACTORY
def record_factory(cls_name, field_names):
if isinstance(field_names, str):
field_names = field_names.replace(',', ' ').split()
field_names = tuple(field_names) # <1>
try:
field_names = field_names.replace(',', ' ').split() # <1>
except AttributeError: # no .replace or .split
pass # assume it's already a sequence of identifiers
field_names = tuple(field_names) # <2>
def __init__(self, *args, **kwargs): # <2>
def __init__(self, *args, **kwargs): # <3>
attrs = dict(zip(self.__slots__, args))
attrs.update(kwargs)
for name, value in attrs.items():
setattr(self, name, value)
def __iter__(self): # <3>
def __iter__(self): # <4>
for name in self.__slots__:
yield getattr(self, name)
def __repr__(self): # <4>
def __repr__(self): # <5>
values = ', '.join('{}={!r}'.format(*i) for i
in zip(self.__slots__, self))
return '{}({})'.format(self.__class__.__name__, values)
cls_attrs = dict(__slots__ = field_names, # <5>
cls_attrs = dict(__slots__ = field_names, # <6>
__init__ = __init__,
__iter__ = __iter__,
__repr__ = __repr__)
return type(cls_name, (object,), cls_attrs) # <6>
return type(cls_name, (object,), cls_attrs) # <7>
# END RECORD_FACTORY

Some files were not shown because too many files have changed in this diff Show More