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

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