sync from Atlas repo

This commit is contained in:
Luciano Ramalho 2014-11-14 12:03:10 -02:00
parent 661b68b235
commit 6dbc75520f
20 changed files with 2281 additions and 31 deletions

59
classes/bingoaddable.py Normal file
View File

@ -0,0 +1,59 @@
"""
======================
AddableBingoCage tests
======================
Tests for __add__ and __iadd__:
>>> vowels = 'AEIOU'
>>> globe = AddableBingoCage(vowels)
>>> len(globe)
5
>>> globe.pop() in vowels
True
>>> len(globe)
4
>>> globe2 = AddableBingoCage('XYZ')
>>> globe3 = globe + globe2
>>> len(globe3)
7
>>> void = globe + [10, 20]
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'AddableBingoCage' and 'list'
Tests for __add__ and __iadd__:
>>> globe_orig = globe
>>> len(globe)
4
>>> globe += globe2
>>> len(globe)
7
>>> globe += [10, 20]
>>> len(globe)
9
>>> globe is globe_orig
True
"""
# BEGIN ADDABLE_BINGO
import itertools # <1>
from bingobase import BingoCage
class AddableBingoCage(BingoCage): # <2>
def __add__(self, other):
if isinstance(other, AddableBingoCage): # <3>
return AddableBingoCage(itertools.chain(self, other)) # <4>
else:
return NotImplemented
def __iadd__(self, other):
self.load(other) # <5>
return self # <6>
# END ADDABLE_BINGO

96
classes/bingobase.py Normal file
View File

@ -0,0 +1,96 @@
"""
===============
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)

426
classes/vector_py3_5.py Normal file
View File

@ -0,0 +1,426 @@
"""
A multi-dimensional ``Vector`` class, take 9: operator ``@``
WARNING: This example requires Python 3.5 or later.
A ``Vector`` is built from an iterable of numbers::
>>> Vector([3.1, 4.2])
Vector([3.1, 4.2])
>>> Vector((3, 4, 5))
Vector([3.0, 4.0, 5.0])
>>> Vector(range(10))
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
Tests with 2-dimensions (same results as ``vector2d_v1.py``)::
>>> v1 = Vector([3, 4])
>>> x, y = v1
>>> x, y
(3.0, 4.0)
>>> v1
Vector([3.0, 4.0])
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0)
>>> octets = bytes(v1)
>>> octets
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1)
5.0
>>> bool(v1), bool(Vector([0, 0]))
(True, False)
Test of ``.frombytes()`` class method:
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector([3.0, 4.0])
>>> v1 == v1_clone
True
Tests with 3-dimensions::
>>> v1 = Vector([3, 4, 5])
>>> x, y, z = v1
>>> x, y, z
(3.0, 4.0, 5.0)
>>> v1
Vector([3.0, 4.0, 5.0])
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0, 5.0)
>>> abs(v1) # doctest:+ELLIPSIS
7.071067811...
>>> bool(v1), bool(Vector([0, 0, 0]))
(True, False)
Tests with many dimensions::
>>> v7 = Vector(range(7))
>>> v7
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
>>> abs(v7) # doctest:+ELLIPSIS
9.53939201...
Test of ``.__bytes__`` and ``.frombytes()`` methods::
>>> v1 = Vector([3, 4, 5])
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector([3.0, 4.0, 5.0])
>>> v1 == v1_clone
True
Tests of sequence behavior::
>>> v1 = Vector([3, 4, 5])
>>> len(v1)
3
>>> v1[0], v1[len(v1)-1], v1[-1]
(3.0, 5.0, 5.0)
Test of slicing::
>>> v7 = Vector(range(7))
>>> v7[-1]
6.0
>>> v7[1:4]
Vector([1.0, 2.0, 3.0])
>>> v7[-1:]
Vector([6.0])
>>> v7[1,2]
Traceback (most recent call last):
...
TypeError: Vector indices must be integers
Tests of dynamic attribute access::
>>> v7 = Vector(range(10))
>>> v7.x
0.0
>>> v7.y, v7.z, v7.t
(1.0, 2.0, 3.0)
Dynamic attribute lookup failures::
>>> v7.k
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 'k'
>>> v3 = Vector(range(3))
>>> v3.t
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 't'
>>> v3.spam
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 'spam'
Tests of hashing::
>>> v1 = Vector([3, 4])
>>> v2 = Vector([3.1, 4.2])
>>> v3 = Vector([3, 4, 5])
>>> v6 = Vector(range(6))
>>> hash(v1), hash(v2), hash(v3), hash(v6)
(7, 384307168202284039, 2, 1)
>>> len(set([v1, v2, v3, v6]))
4
Tests of ``format()`` with Cartesian coordinates in 2D::
>>> v1 = Vector([3, 4])
>>> format(v1)
'(3.0, 4.0)'
>>> format(v1, '.2f')
'(3.00, 4.00)'
>>> format(v1, '.3e')
'(3.000e+00, 4.000e+00)'
Tests of ``format()`` with Cartesian coordinates in 3D and 7D::
>>> v3 = Vector([3, 4, 5])
>>> format(v3)
'(3.0, 4.0, 5.0)'
>>> format(Vector(range(7)))
'(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0)'
Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D::
>>> format(Vector([1, 1]), 'h') # doctest:+ELLIPSIS
'<1.414213..., 0.785398...>'
>>> format(Vector([1, 1]), '.3eh')
'<1.414e+00, 7.854e-01>'
>>> format(Vector([1, 1]), '0.5fh')
'<1.41421, 0.78540>'
>>> format(Vector([1, 1, 1]), 'h') # doctest:+ELLIPSIS
'<1.73205..., 0.95531..., 0.78539...>'
>>> format(Vector([2, 2, 2]), '.3eh')
'<3.464e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 0, 0]), '0.5fh')
'<0.00000, 0.00000, 0.00000>'
>>> format(Vector([-1, -1, -1, -1]), 'h') # doctest:+ELLIPSIS
'<2.0, 2.09439..., 2.18627..., 3.92699...>'
>>> format(Vector([2, 2, 2, 2]), '.3eh')
'<4.000e+00, 1.047e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 1, 0, 0]), '0.5fh')
'<1.00000, 1.57080, 0.00000, 0.00000>'
Basic tests of operator ``+``::
>>> v1 = Vector([3, 4, 5])
>>> v2 = Vector([6, 7, 8])
>>> v1 + v2
Vector([9.0, 11.0, 13.0])
>>> v1 + v2 == Vector([3+6, 4+7, 5+8])
True
>>> v3 = Vector([1, 2])
>>> v1 + v3 # short vectors are filled with 0.0 on addition
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with mixed types::
>>> v1 + (10, 20, 30)
Vector([13.0, 24.0, 35.0])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> v1 + v2d
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with mixed types, swapped operands::
>>> (10, 20, 30) + v1
Vector([13.0, 24.0, 35.0])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> v2d + v1
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with an unsuitable operand:
>>> v1 + 1
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Vector' and 'int'
>>> v1 + 'ABC'
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Vector' and 'str'
Basic tests of operator ``*``::
>>> v1 = Vector([1, 2, 3])
>>> v1 * 10
Vector([10.0, 20.0, 30.0])
>>> 10 * v1
Vector([10.0, 20.0, 30.0])
Tests of ``*`` with unusual but valid operands::
>>> v1 * True
Vector([1.0, 2.0, 3.0])
>>> from fractions import Fraction
>>> v1 * Fraction(1, 3) # doctest:+ELLIPSIS
Vector([0.3333..., 0.6666..., 1.0])
Tests of ``*`` with unsuitable operands::
>>> v1 * (1, 2)
Traceback (most recent call last):
...
TypeError: can't multiply sequence by non-int of type 'Vector'
Tests of operator `==`::
>>> va = Vector(range(1, 4))
>>> vb = Vector([1.0, 2.0, 3.0])
>>> va == vb
True
>>> vc = Vector([1, 2])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> vc == v2d
True
>>> va == (1, 2, 3)
False
Tests of operator `!=`::
>>> va != vb
False
>>> vc != v2d
False
>>> va != (1, 2, 3)
True
Tests for operator `@` (Python >= 3.5), computing the dot product::
>>> va = Vector([1, 2, 3])
>>> vz = Vector([5, 6, 7])
>>> va @ vz == 38.0 # 1*5 + 2*6 + 3*7
True
>>> [10, 20, 30] @ vz
380.0
>>> va @ 3
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for @: 'Vector' and 'int'
"""
from array import array
import reprlib
import math
import functools
import operator
import itertools
import numbers
class Vector:
typecode = 'd'
def __init__(self, components):
self._components = array(self.typecode, components)
def __iter__(self):
return iter(self._components)
def __repr__(self):
components = reprlib.repr(self._components)
components = components[components.find('['):-1]
return 'Vector({})'.format(components)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return (bytes([ord(self.typecode)]) +
bytes(self._components))
def __eq__(self, other):
if isinstance(other, Vector):
return (len(self) == len(other) and
all(a == b for a, b in zip(self, other)))
else:
return NotImplemented
def __hash__(self):
hashes = (hash(x) for x in self)
return functools.reduce(operator.xor, hashes, 0)
def __abs__(self):
return math.sqrt(sum(x * x for x in self))
def __bool__(self):
return bool(abs(self))
def __len__(self):
return len(self._components)
def __getitem__(self, index):
cls = type(self)
if isinstance(index, slice):
return cls(self._components[index])
elif isinstance(index, int):
return self._components[index]
else:
msg = '{.__name__} indices must be integers'
raise TypeError(msg.format(cls))
shortcut_names = 'xyzt'
def __getattr__(self, name):
cls = type(self)
if len(name) == 1:
pos = cls.shortcut_names.find(name)
if 0 <= pos < len(self._components):
return self._components[pos]
msg = '{.__name__!r} object has no attribute {!r}'
raise AttributeError(msg.format(cls, name))
def angle(self, n):
r = math.sqrt(sum(x * x for x in self[n:]))
a = math.atan2(r, self[n-1])
if (n == len(self) - 1) and (self[-1] < 0):
return math.pi * 2 - a
else:
return a
def angles(self):
return (self.angle(n) for n in range(1, len(self)))
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('h'): # hyperspherical coordinates
fmt_spec = fmt_spec[:-1]
coords = itertools.chain([abs(self)],
self.angles())
outer_fmt = '<{}>'
else:
coords = self
outer_fmt = '({})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(', '.join(components))
@classmethod
def frombytes(cls, octets):
typecode = chr(octets[0])
memv = memoryview(octets[1:]).cast(typecode)
return cls(memv)
def __add__(self, other):
try:
pairs = itertools.zip_longest(self, other, fillvalue=0.0)
return Vector(a + b for a, b in pairs)
except TypeError:
return NotImplemented
def __radd__(self, other):
return self + other
def __mul__(self, scalar):
if isinstance(scalar, numbers.Real):
return Vector(n * scalar for n in self)
else:
return NotImplemented
def __rmul__(self, scalar):
return self * scalar
def __matmul__(self, other):
try:
return sum(a * b for a, b in zip(self, other))
except TypeError:
return NotImplemented
def __rmatmul__(self, other):
return self @ other # this only works in Python 3.5

View File

@ -99,7 +99,7 @@ class Vector:
def __repr__(self):
components = reprlib.repr(self._components) # <3>
components = components[components.find('['):-1] # <4>
components = components[components.find('['):-1] # <4>
return 'Vector({})'.format(components)
def __str__(self):

View File

@ -206,10 +206,10 @@ class Vector:
def __getattr__(self, name):
cls = type(self) # <1>
if len(name) == 1: # <2>
pos = cls.shortcut_names.find(name) # <3>
if 0 <= pos < len(self._components): # <4>
pos = cls.shortcut_names.find(name) # <3>
if 0 <= pos < len(self._components): # <4>
return self._components[pos]
msg = '{.__name__!r} object has no attribute {!r}' # <5>
msg = '{.__name__!r} object has no attribute {!r}' # <5>
raise AttributeError(msg.format(cls, name))
# END VECTOR_V3_GETATTR
@ -230,7 +230,6 @@ class Vector:
# END VECTOR_V3_SETATTR
@classmethod
def frombytes(cls, octets):
typecode = chr(octets[0])

View File

@ -201,12 +201,12 @@ class Vector:
shortcut_names = 'xyzt'
def __getattr__(self, name):
cls = type(self) # <1>
if len(name) == 1: # <2>
pos = cls.shortcut_names.find(name) # <3>
if 0 <= pos < len(self._components): # <4>
cls = type(self)
if len(name) == 1:
pos = cls.shortcut_names.find(name)
if 0 <= pos < len(self._components):
return self._components[pos]
msg = '{.__name__!r} object has no attribute {!r}' # <5>
msg = '{.__name__!r} object has no attribute {!r}'
raise AttributeError(msg.format(cls, name))
@classmethod

View File

@ -1,5 +1,5 @@
"""
A multi-dimensional ``Vector`` class, take 6
A multi-dimensional ``Vector`` class, take 6: operator ``+``
A ``Vector`` is built from an iterable of numbers::
@ -183,19 +183,49 @@ Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D::
'<1.00000, 1.57080, 0.00000, 0.00000>'
Tests of operator `==`::
Basic tests of operator ``+``::
>>> v1 = Vector(range(1, 4))
>>> v2 = Vector([1.0, 2.0, 3.0])
>>> v1 == v2
>>> v1 = Vector([3, 4, 5])
>>> v2 = Vector([6, 7, 8])
>>> v1 + v2
Vector([9.0, 11.0, 13.0])
>>> v1 + v2 == Vector([3+6, 4+7, 5+8])
True
>>> v3 = Vector([1, 2])
>>> v1 + v3 # short vectors are filled with 0.0 on addition
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with mixed types::
>>> v1 + (10, 20, 30)
Vector([13.0, 24.0, 35.0])
>>> from vector2d_v3 import Vector2d
>>> Vector([7, 8]) == Vector2d(7, 8)
True
>>> v1 == (1, 2, 3)
False
>>> v2d = Vector2d(1, 2)
>>> v1 + v2d
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with mixed types, swapped operands::
>>> (10, 20, 30) + v1
Vector([13.0, 24.0, 35.0])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> v2d + v1
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with an unsuitable operand:
>>> v1 + 1
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Vector' and 'int'
>>> v1 + 'ABC'
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Vector' and 'str'
"""
from array import array
@ -227,14 +257,9 @@ class Vector:
return (bytes([ord(self.typecode)]) +
bytes(self._components))
# BEGIN VECTOR_V6_EQ
def __eq__(self, other):
if isinstance(other, Vector):
return (len(self) == len(other) and
all(a == b for a, b in zip(self, other)))
else:
return NotImplemented
# END VECTOR_V6_EQ
return (len(self) == len(other) and
all(a == b for a, b in zip(self, other)))
def __hash__(self):
hashes = (hash(x) for x in self)
@ -299,10 +324,14 @@ class Vector:
memv = memoryview(octets[1:]).cast(typecode)
return cls(memv)
# BEGIN VECTOR_V6
def __add__(self, other):
if len(self) != len(other):
cls_name = type(self).__name__
msg = 'cannot add {!r} of different dimensions'
raise ValueError(msg.format(cls_name))
return Vector(a + b for a, b in zip(self, other))
try:
pairs = itertools.zip_longest(self, other, fillvalue=0.0)
return Vector(a + b for a, b in pairs)
except TypeError:
return NotImplemented
def __radd__(self, other):
return self + other
# END VECTOR_V6

372
classes/vector_v7.py Normal file
View File

@ -0,0 +1,372 @@
"""
A multi-dimensional ``Vector`` class, take 7: operator ``*``
A ``Vector`` is built from an iterable of numbers::
>>> Vector([3.1, 4.2])
Vector([3.1, 4.2])
>>> Vector((3, 4, 5))
Vector([3.0, 4.0, 5.0])
>>> Vector(range(10))
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
Tests with 2-dimensions (same results as ``vector2d_v1.py``)::
>>> v1 = Vector([3, 4])
>>> x, y = v1
>>> x, y
(3.0, 4.0)
>>> v1
Vector([3.0, 4.0])
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0)
>>> octets = bytes(v1)
>>> octets
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1)
5.0
>>> bool(v1), bool(Vector([0, 0]))
(True, False)
Test of ``.frombytes()`` class method:
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector([3.0, 4.0])
>>> v1 == v1_clone
True
Tests with 3-dimensions::
>>> v1 = Vector([3, 4, 5])
>>> x, y, z = v1
>>> x, y, z
(3.0, 4.0, 5.0)
>>> v1
Vector([3.0, 4.0, 5.0])
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0, 5.0)
>>> abs(v1) # doctest:+ELLIPSIS
7.071067811...
>>> bool(v1), bool(Vector([0, 0, 0]))
(True, False)
Tests with many dimensions::
>>> v7 = Vector(range(7))
>>> v7
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
>>> abs(v7) # doctest:+ELLIPSIS
9.53939201...
Test of ``.__bytes__`` and ``.frombytes()`` methods::
>>> v1 = Vector([3, 4, 5])
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector([3.0, 4.0, 5.0])
>>> v1 == v1_clone
True
Tests of sequence behavior::
>>> v1 = Vector([3, 4, 5])
>>> len(v1)
3
>>> v1[0], v1[len(v1)-1], v1[-1]
(3.0, 5.0, 5.0)
Test of slicing::
>>> v7 = Vector(range(7))
>>> v7[-1]
6.0
>>> v7[1:4]
Vector([1.0, 2.0, 3.0])
>>> v7[-1:]
Vector([6.0])
>>> v7[1,2]
Traceback (most recent call last):
...
TypeError: Vector indices must be integers
Tests of dynamic attribute access::
>>> v7 = Vector(range(10))
>>> v7.x
0.0
>>> v7.y, v7.z, v7.t
(1.0, 2.0, 3.0)
Dynamic attribute lookup failures::
>>> v7.k
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 'k'
>>> v3 = Vector(range(3))
>>> v3.t
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 't'
>>> v3.spam
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 'spam'
Tests of hashing::
>>> v1 = Vector([3, 4])
>>> v2 = Vector([3.1, 4.2])
>>> v3 = Vector([3, 4, 5])
>>> v6 = Vector(range(6))
>>> hash(v1), hash(v2), hash(v3), hash(v6)
(7, 384307168202284039, 2, 1)
>>> len(set([v1, v2, v3, v6]))
4
Tests of ``format()`` with Cartesian coordinates in 2D::
>>> v1 = Vector([3, 4])
>>> format(v1)
'(3.0, 4.0)'
>>> format(v1, '.2f')
'(3.00, 4.00)'
>>> format(v1, '.3e')
'(3.000e+00, 4.000e+00)'
Tests of ``format()`` with Cartesian coordinates in 3D and 7D::
>>> v3 = Vector([3, 4, 5])
>>> format(v3)
'(3.0, 4.0, 5.0)'
>>> format(Vector(range(7)))
'(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0)'
Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D::
>>> format(Vector([1, 1]), 'h') # doctest:+ELLIPSIS
'<1.414213..., 0.785398...>'
>>> format(Vector([1, 1]), '.3eh')
'<1.414e+00, 7.854e-01>'
>>> format(Vector([1, 1]), '0.5fh')
'<1.41421, 0.78540>'
>>> format(Vector([1, 1, 1]), 'h') # doctest:+ELLIPSIS
'<1.73205..., 0.95531..., 0.78539...>'
>>> format(Vector([2, 2, 2]), '.3eh')
'<3.464e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 0, 0]), '0.5fh')
'<0.00000, 0.00000, 0.00000>'
>>> format(Vector([-1, -1, -1, -1]), 'h') # doctest:+ELLIPSIS
'<2.0, 2.09439..., 2.18627..., 3.92699...>'
>>> format(Vector([2, 2, 2, 2]), '.3eh')
'<4.000e+00, 1.047e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 1, 0, 0]), '0.5fh')
'<1.00000, 1.57080, 0.00000, 0.00000>'
Basic tests of operator ``+``::
>>> v1 = Vector([3, 4, 5])
>>> v2 = Vector([6, 7, 8])
>>> v1 + v2
Vector([9.0, 11.0, 13.0])
>>> v1 + v2 == Vector([3+6, 4+7, 5+8])
True
>>> v3 = Vector([1, 2])
>>> v1 + v3 # short vectors are filled with 0.0 on addition
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with mixed types::
>>> v1 + (10, 20, 30)
Vector([13.0, 24.0, 35.0])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> v1 + v2d
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with mixed types, swapped operands::
>>> (10, 20, 30) + v1
Vector([13.0, 24.0, 35.0])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> v2d + v1
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with an unsuitable operand:
>>> v1 + 1
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Vector' and 'int'
>>> v1 + 'ABC'
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Vector' and 'str'
Basic tests of operator ``*``::
>>> v1 = Vector([1, 2, 3])
>>> v1 * 10
Vector([10.0, 20.0, 30.0])
>>> 10 * v1
Vector([10.0, 20.0, 30.0])
Tests of ``*`` with unusual but valid operands::
>>> v1 * True
Vector([1.0, 2.0, 3.0])
>>> from fractions import Fraction
>>> v1 * Fraction(1, 3) # doctest:+ELLIPSIS
Vector([0.3333..., 0.6666..., 1.0])
Tests of ``*`` with unsuitable operands::
>>> v1 * (1, 2)
Traceback (most recent call last):
...
TypeError: can't multiply sequence by non-int of type 'Vector'
"""
from array import array
import reprlib
import math
import functools
import operator
import itertools
import numbers
class Vector:
typecode = 'd'
def __init__(self, components):
self._components = array(self.typecode, components)
def __iter__(self):
return iter(self._components)
def __repr__(self):
components = reprlib.repr(self._components)
components = components[components.find('['):-1]
return 'Vector({})'.format(components)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return (bytes([ord(self.typecode)]) +
bytes(self._components))
def __eq__(self, other):
return (len(self) == len(other) and
all(a == b for a, b in zip(self, other)))
def __hash__(self):
hashes = (hash(x) for x in self)
return functools.reduce(operator.xor, hashes, 0)
def __abs__(self):
return math.sqrt(sum(x * x for x in self))
def __bool__(self):
return bool(abs(self))
def __len__(self):
return len(self._components)
def __getitem__(self, index):
cls = type(self)
if isinstance(index, slice):
return cls(self._components[index])
elif isinstance(index, int):
return self._components[index]
else:
msg = '{.__name__} indices must be integers'
raise TypeError(msg.format(cls))
shortcut_names = 'xyzt'
def __getattr__(self, name):
cls = type(self)
if len(name) == 1:
pos = cls.shortcut_names.find(name)
if 0 <= pos < len(self._components):
return self._components[pos]
msg = '{.__name__!r} object has no attribute {!r}'
raise AttributeError(msg.format(cls, name))
def angle(self, n):
r = math.sqrt(sum(x * x for x in self[n:]))
a = math.atan2(r, self[n-1])
if (n == len(self) - 1) and (self[-1] < 0):
return math.pi * 2 - a
else:
return a
def angles(self):
return (self.angle(n) for n in range(1, len(self)))
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('h'): # hyperspherical coordinates
fmt_spec = fmt_spec[:-1]
coords = itertools.chain([abs(self)],
self.angles())
outer_fmt = '<{}>'
else:
coords = self
outer_fmt = '({})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(', '.join(components))
@classmethod
def frombytes(cls, octets):
typecode = chr(octets[0])
memv = memoryview(octets[1:]).cast(typecode)
return cls(memv)
def __add__(self, other):
try:
pairs = itertools.zip_longest(self, other, fillvalue=0.0)
return Vector(a + b for a, b in pairs)
except TypeError:
return NotImplemented
def __radd__(self, other):
return self + other
def __mul__(self, scalar):
if isinstance(scalar, numbers.Real):
return Vector(n * scalar for n in self)
else:
return NotImplemented
def __rmul__(self, scalar):
return self * scalar

402
classes/vector_v8.py Normal file
View File

@ -0,0 +1,402 @@
"""
A multi-dimensional ``Vector`` class, take 8: operator ``==``
A ``Vector`` is built from an iterable of numbers::
>>> Vector([3.1, 4.2])
Vector([3.1, 4.2])
>>> Vector((3, 4, 5))
Vector([3.0, 4.0, 5.0])
>>> Vector(range(10))
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
Tests with 2-dimensions (same results as ``vector2d_v1.py``)::
>>> v1 = Vector([3, 4])
>>> x, y = v1
>>> x, y
(3.0, 4.0)
>>> v1
Vector([3.0, 4.0])
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0)
>>> octets = bytes(v1)
>>> octets
b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1)
5.0
>>> bool(v1), bool(Vector([0, 0]))
(True, False)
Test of ``.frombytes()`` class method:
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector([3.0, 4.0])
>>> v1 == v1_clone
True
Tests with 3-dimensions::
>>> v1 = Vector([3, 4, 5])
>>> x, y, z = v1
>>> x, y, z
(3.0, 4.0, 5.0)
>>> v1
Vector([3.0, 4.0, 5.0])
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0, 5.0)
>>> abs(v1) # doctest:+ELLIPSIS
7.071067811...
>>> bool(v1), bool(Vector([0, 0, 0]))
(True, False)
Tests with many dimensions::
>>> v7 = Vector(range(7))
>>> v7
Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])
>>> abs(v7) # doctest:+ELLIPSIS
9.53939201...
Test of ``.__bytes__`` and ``.frombytes()`` methods::
>>> v1 = Vector([3, 4, 5])
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector([3.0, 4.0, 5.0])
>>> v1 == v1_clone
True
Tests of sequence behavior::
>>> v1 = Vector([3, 4, 5])
>>> len(v1)
3
>>> v1[0], v1[len(v1)-1], v1[-1]
(3.0, 5.0, 5.0)
Test of slicing::
>>> v7 = Vector(range(7))
>>> v7[-1]
6.0
>>> v7[1:4]
Vector([1.0, 2.0, 3.0])
>>> v7[-1:]
Vector([6.0])
>>> v7[1,2]
Traceback (most recent call last):
...
TypeError: Vector indices must be integers
Tests of dynamic attribute access::
>>> v7 = Vector(range(10))
>>> v7.x
0.0
>>> v7.y, v7.z, v7.t
(1.0, 2.0, 3.0)
Dynamic attribute lookup failures::
>>> v7.k
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 'k'
>>> v3 = Vector(range(3))
>>> v3.t
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 't'
>>> v3.spam
Traceback (most recent call last):
...
AttributeError: 'Vector' object has no attribute 'spam'
Tests of hashing::
>>> v1 = Vector([3, 4])
>>> v2 = Vector([3.1, 4.2])
>>> v3 = Vector([3, 4, 5])
>>> v6 = Vector(range(6))
>>> hash(v1), hash(v2), hash(v3), hash(v6)
(7, 384307168202284039, 2, 1)
>>> len(set([v1, v2, v3, v6]))
4
Tests of ``format()`` with Cartesian coordinates in 2D::
>>> v1 = Vector([3, 4])
>>> format(v1)
'(3.0, 4.0)'
>>> format(v1, '.2f')
'(3.00, 4.00)'
>>> format(v1, '.3e')
'(3.000e+00, 4.000e+00)'
Tests of ``format()`` with Cartesian coordinates in 3D and 7D::
>>> v3 = Vector([3, 4, 5])
>>> format(v3)
'(3.0, 4.0, 5.0)'
>>> format(Vector(range(7)))
'(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0)'
Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D::
>>> format(Vector([1, 1]), 'h') # doctest:+ELLIPSIS
'<1.414213..., 0.785398...>'
>>> format(Vector([1, 1]), '.3eh')
'<1.414e+00, 7.854e-01>'
>>> format(Vector([1, 1]), '0.5fh')
'<1.41421, 0.78540>'
>>> format(Vector([1, 1, 1]), 'h') # doctest:+ELLIPSIS
'<1.73205..., 0.95531..., 0.78539...>'
>>> format(Vector([2, 2, 2]), '.3eh')
'<3.464e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 0, 0]), '0.5fh')
'<0.00000, 0.00000, 0.00000>'
>>> format(Vector([-1, -1, -1, -1]), 'h') # doctest:+ELLIPSIS
'<2.0, 2.09439..., 2.18627..., 3.92699...>'
>>> format(Vector([2, 2, 2, 2]), '.3eh')
'<4.000e+00, 1.047e+00, 9.553e-01, 7.854e-01>'
>>> format(Vector([0, 1, 0, 0]), '0.5fh')
'<1.00000, 1.57080, 0.00000, 0.00000>'
Basic tests of operator ``+``::
>>> v1 = Vector([3, 4, 5])
>>> v2 = Vector([6, 7, 8])
>>> v1 + v2
Vector([9.0, 11.0, 13.0])
>>> v1 + v2 == Vector([3+6, 4+7, 5+8])
True
>>> v3 = Vector([1, 2])
>>> v1 + v3 # short vectors are filled with 0.0 on addition
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with mixed types::
>>> v1 + (10, 20, 30)
Vector([13.0, 24.0, 35.0])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> v1 + v2d
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with mixed types, swapped operands::
>>> (10, 20, 30) + v1
Vector([13.0, 24.0, 35.0])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> v2d + v1
Vector([4.0, 6.0, 5.0])
Tests of ``+`` with an unsuitable operand:
>>> v1 + 1
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Vector' and 'int'
>>> v1 + 'ABC'
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +: 'Vector' and 'str'
Basic tests of operator ``*``::
>>> v1 = Vector([1, 2, 3])
>>> v1 * 10
Vector([10.0, 20.0, 30.0])
>>> 10 * v1
Vector([10.0, 20.0, 30.0])
Tests of ``*`` with unusual but valid operands::
>>> v1 * True
Vector([1.0, 2.0, 3.0])
>>> from fractions import Fraction
>>> v1 * Fraction(1, 3) # doctest:+ELLIPSIS
Vector([0.3333..., 0.6666..., 1.0])
Tests of ``*`` with unsuitable operands::
>>> v1 * (1, 2)
Traceback (most recent call last):
...
TypeError: can't multiply sequence by non-int of type 'Vector'
Tests of operator `==`::
>>> va = Vector(range(1, 4))
>>> vb = Vector([1.0, 2.0, 3.0])
>>> va == vb
True
>>> vc = Vector([1, 2])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> vc == v2d
True
>>> va == (1, 2, 3)
False
Tests of operator `!=`::
>>> va != vb
False
>>> vc != v2d
False
>>> va != (1, 2, 3)
True
"""
from array import array
import reprlib
import math
import functools
import operator
import itertools
import numbers
class Vector:
typecode = 'd'
def __init__(self, components):
self._components = array(self.typecode, components)
def __iter__(self):
return iter(self._components)
def __repr__(self):
components = reprlib.repr(self._components)
components = components[components.find('['):-1]
return 'Vector({})'.format(components)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return (bytes([ord(self.typecode)]) +
bytes(self._components))
# BEGIN VECTOR_V8_EQ
def __eq__(self, other):
if isinstance(other, Vector): # <1>
return (len(self) == len(other) and
all(a == b for a, b in zip(self, other)))
else:
return NotImplemented # <2>
# END VECTOR_V8_EQ
def __hash__(self):
hashes = (hash(x) for x in self)
return functools.reduce(operator.xor, hashes, 0)
def __abs__(self):
return math.sqrt(sum(x * x for x in self))
def __bool__(self):
return bool(abs(self))
def __len__(self):
return len(self._components)
def __getitem__(self, index):
cls = type(self)
if isinstance(index, slice):
return cls(self._components[index])
elif isinstance(index, int):
return self._components[index]
else:
msg = '{.__name__} indices must be integers'
raise TypeError(msg.format(cls))
shortcut_names = 'xyzt'
def __getattr__(self, name):
cls = type(self)
if len(name) == 1:
pos = cls.shortcut_names.find(name)
if 0 <= pos < len(self._components):
return self._components[pos]
msg = '{.__name__!r} object has no attribute {!r}'
raise AttributeError(msg.format(cls, name))
def angle(self, n):
r = math.sqrt(sum(x * x for x in self[n:]))
a = math.atan2(r, self[n-1])
if (n == len(self) - 1) and (self[-1] < 0):
return math.pi * 2 - a
else:
return a
def angles(self):
return (self.angle(n) for n in range(1, len(self)))
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('h'): # hyperspherical coordinates
fmt_spec = fmt_spec[:-1]
coords = itertools.chain([abs(self)],
self.angles())
outer_fmt = '<{}>'
else:
coords = self
outer_fmt = '({})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(', '.join(components))
@classmethod
def frombytes(cls, octets):
typecode = chr(octets[0])
memv = memoryview(octets[1:]).cast(typecode)
return cls(memv)
def __add__(self, other):
try:
pairs = itertools.zip_longest(self, other, fillvalue=0.0)
return Vector(a + b for a, b in pairs)
except TypeError:
return NotImplemented
def __radd__(self, other):
return self + other
def __mul__(self, scalar):
if isinstance(scalar, numbers.Real):
return Vector(n * scalar for n in self)
else:
return NotImplemented
def __rmul__(self, scalar):
return self * scalar

View File

@ -0,0 +1,195 @@
# ISO-3166-1 US-GEC name
AF AF Afghanistan
AL AL Albania
DZ AG Algeria
AD AN Andorra
AO AO Angola
AG AC Antigua and Barbuda
AR AR Argentina
AM AM Armenia
AU AS Australia
AT AU Austria
AZ AJ Azerbaijan
BS BF Bahamas
BH BA Bahrain
BD BG Bangladesh
BB BB Barbados
BY BO Belarus
BE BE Belgium
BZ BH Belize
BJ BN Benin
BT BT Bhutan
BO BL Bolivia
BA BK Bosnia and Herzegovina
BW BC Botswana
BR BR Brazil
BN BX Brunei Darussalam
BG BU Bulgaria
BF UV Burkina Faso
BI BY Burundi
KH CB Cambodia
CM CM Cameroon
CA CA Canada
CV CV Cape Verde
CF CT Central African Republic
TD CD Chad
CL CI Chile
CN CH China
CO CO Colombia
KM CN Comoros
CG CF Congo (Brazzaville)
CD CG Congo (Kinshasa)
CR CS Costa Rica
CI IV Côte d'Ivoire
HR HR Croatia
CU CU Cuba
CY CY Cyprus
CZ EZ Czech Republic
DK DA Denmark
DJ DJ Djibouti
DM DO Dominica
EC EC Ecuador
EG EG Egypt
SV ES El Salvador
GQ EK Equatorial Guinea
ER ER Eritrea
EE EN Estonia
ET ET Ethiopia
FJ FJ Fiji
FI FI Finland
FR FR France
GA GB Gabon
GM GA Gambia
GE GG Georgia
DE GM Germany
GH GH Ghana
GR GR Greece
GD GJ Grenada
GT GT Guatemala
GN GV Guinea
GW PU Guinea-Bissau
GY GY Guyana
HT HA Haiti
HN HO Honduras
HU HU Hungary
IS IC Iceland
IN IN India
ID ID Indonesia
IR IR Iran
IQ IZ Iraq
IE EI Ireland
IL IS Israel
IT IT Italy
JM JM Jamaica
JP JA Japan
JO JO Jordan
KZ KZ Kazakhstan
KE KE Kenya
KI KR Kiribati
KP KN Korea, North
KR KS Korea, South
KW KU Kuwait
KG KG Kyrgyzstan
LA LA Laos
LV LG Latvia
LB LE Lebanon
LS LT Lesotho
LR LI Liberia
LY LY Libya
LI LS Liechtenstein
LT LH Lithuania
LU LU Luxembourg
MK MK Macedonia
MG MA Madagascar
MW MI Malawi
MY MY Malaysia
MV MV Maldives
ML ML Mali
MT MT Malta
MH RM Marshall Islands
MR MR Mauritania
MU MP Mauritius
MX MX Mexico
FM FM Micronesia
MD MD Moldova
MC MN Monaco
MN MG Mongolia
ME MJ Montenegro
MA MO Morocco
MZ MZ Mozambique
MM BM Myanmar
NA WA Namibia
NR NR Nauru
NP NP Nepal
NL NL Netherlands
NZ NZ New Zealand
NI NU Nicaragua
NE NG Niger
NG NI Nigeria
NO NO Norway
OM MU Oman
PK PK Pakistan
PW PS Palau
PA PM Panama
PG PP Papua New Guinea
PY PA Paraguay
PE PE Peru
PH RP Philippines
PL PL Poland
PT PO Portugal
QA QA Qatar
RO RO Romania
RU RS Russian Federation
RW RW Rwanda
KN SC Saint Kitts and Nevis
LC ST Saint Lucia
VC VC Grenadines
WS WS Samoa
SM SM San Marino
ST TP Sao Tome and Principe
SA SA Saudi Arabia
SN SG Senegal
RS RI Serbia
SC SE Seychelles
SL SL Sierra Leone
SG SN Singapore
SK LO Slovakia
SI SI Slovenia
SB BP Solomon Islands
SO SO Somalia
ZA SF South Africa
SS OD South Sudan
ES SP Spain
LK CE Sri Lanka
SD SU Sudan
SR NS Suriname
SZ WZ Swaziland
SE SW Sweden
CH SZ Switzerland
SY SY Syria
TW TW Taiwan
TJ TI Tajikistan
TZ TZ Tanzania
TH TH Thailand
TL TT Timor-Leste
TG TO Togo
TO TN Tonga
TT TD Trinidad and Tobago
TN TS Tunisia
TR TU Turkey
TM TX Turkmenistan
TV TV Tuvalu
UG UG Uganda
UA UP Ukraine
AE AE United Arab Emirates
GB UK United Kingdom
US US United States of America
UY UY Uruguay
UZ UZ Uzbekistan
VU NH Vanuatu
VA VT Vatican City
VE VE Venezuela
VN VM Vietnam
YE YM Yemen
ZM ZA Zambia
ZW ZI Zimbabwe

View File

@ -0,0 +1,46 @@
"""
Mappings of ISO-3166-1 alpha-2 country codes to names, to GEC
(Geopolitical Entities and Codes used by the US government)
and utility functions for flag download examples
"""
DATA_FILE = 'country-codes.tab'
# original source
CIA_URL = ('https://www.cia.gov/library/publications/'
'the-world-factbook/graphics/flags/large/{gec}-lgflag.gif')
# local nginx web server
NGINX_URL = 'http://localhost:8080/ciaflags/{gec}.gif'
# Vaurien
VAURIEN_URL = 'http://localhost:8000/ciaflags/{gec}.gif'
BASE_URL = VAURIEN_URL
DEST_PATH_NAME = 'img/{cc}.gif'
cc2name = {} # ISO-3166-1 to name
cc2gec = {} # ISO-3166-1 to GEC
def _load():
with open(DATA_FILE, encoding='utf-8') as cc_txt:
for line in cc_txt:
line = line.rstrip()
if line.startswith('#'):
continue
iso_cc, gec, name = line.split('\t')
cc2name[iso_cc] = name
cc2gec[iso_cc] = gec
def flag_url(iso_cc):
return BASE_URL.format(gec=cc2gec[iso_cc].lower())
def iso_file_name(iso_cc):
return DEST_PATH_NAME.format(cc=iso_cc.lower())
def gec_file_name(iso_cc):
return DEST_PATH_NAME.format(cc=cc2gec[iso_cc].lower())
_load()

View File

@ -0,0 +1,56 @@
import requests
import countryflags as cf
import time
times = {}
def fetch(iso_cc):
resp = requests.get(cf.flag_url(iso_cc))
if resp.status_code != 200:
resp.raise_for_status()
file_name = cf.iso_file_name(iso_cc)
with open(file_name, 'wb') as img:
written = img.write(resp.content)
return written, file_name
def main():
pending = sorted(cf.cc2name)
to_download = len(pending)
downloaded = 0
t0 = time.time()
for iso_cc in pending:
print('get:', iso_cc)
try:
times[iso_cc] = [time.time() - t0]
octets, file_name = fetch(iso_cc)
times[iso_cc].append(time.time() - t0)
downloaded += 1
print('\t--> {}: {:5d} bytes'.format(file_name, octets))
except Exception as exc:
print('\t***', iso_cc, 'generated an exception:', exc)
ratio = downloaded / to_download
print('{} of {} downloaded ({:.1%})'.format(downloaded, to_download, ratio))
for iso_cc in sorted(times):
start, end = times[iso_cc]
print('{}\t{:.6g}\t{:.6g}'.format(iso_cc, start, end))
if __name__ == '__main__':
main()
"""
From cia.gov:
real 3m26.679s
user 0m5.212s
sys 0m0.383s
From localhost nginx:
real 0m1.193s
user 0m0.858s
sys 0m0.179s
From localhost nginx via Vaurien with .5s delay
real 1m40.519s
user 0m1.103s
sys 0m0.243s
"""

View File

@ -0,0 +1,56 @@
from concurrent import futures
import sys
import requests
import countryflags as cf
import time
from getsequential import fetch
DEFAULT_NUM_THREADS = 100
GLOBAL_TIMEOUT = 300 # seconds
times = {}
def main(num_threads):
pool = futures.ThreadPoolExecutor(num_threads)
pending = {}
t0 = time.time()
# submit all jobs
for iso_cc in sorted(cf.cc2name):
print('get:', iso_cc)
times[iso_cc] = [time.time() - t0]
job = pool.submit(fetch, iso_cc)
pending[job] = iso_cc
to_download = len(pending)
downloaded = 0
# get results as jobs are done
for job in futures.as_completed(pending, timeout=GLOBAL_TIMEOUT):
try:
octets, file_name = job.result()
times[pending[job]].append(time.time() - t0)
downloaded += 1
print('\t--> {}: {:5d} bytes'.format(file_name, octets))
except Exception as exc:
print('\t***', pending[job], 'generated an exception:', exc)
ratio = downloaded / to_download
print('{} of {} downloaded ({:.1%})'.format(downloaded, to_download, ratio))
for iso_cc in sorted(times):
start, end = times[iso_cc]
print('{}\t{:.6g}\t{:.6g}'.format(iso_cc, start, end))
if __name__ == '__main__':
if len(sys.argv) == 2:
num_threads = int(sys.argv[1])
else:
num_threads = DEFAULT_NUM_THREADS
main(num_threads)
"""
From localhost nginx:
real 0m1.163s
user 0m1.001s
sys 0m0.289s
"""

View File

@ -0,0 +1,3 @@
#!/bin/bash
vaurien --protocol http --proxy localhost:8000 --backend localhost:8080 \
--behavior 100:delay --behavior-delay-sleep 1

View File

@ -0,0 +1,3 @@
#!/bin/bash
vaurien --protocol http --proxy localhost:8000 --backend localhost:8080 \
--behavior 50:error,50:delay --behavior-delay-sleep .5

300
dicts/test_transformdict.py Normal file
View File

@ -0,0 +1,300 @@
"""Unit tests for transformdict.py."""
import unittest
from test import support
from test import mapping_tests
import pickle
import copy
from functools import partial
from transformdict import TransformDict
def str_lower(s):
return s.lower()
class TransformDictTestBase(unittest.TestCase):
def check_underlying_dict(self, d, expected):
"""
Check for implementation details.
"""
self.assertEqual(d._data, expected)
self.assertEqual(set(d._original), set(expected))
self.assertEqual([d._transform(v) for v in d._original.values()],
list(d._original.keys()))
class TestTransformDict(TransformDictTestBase):
def test_init(self):
with self.assertRaises(TypeError):
TransformDict()
with self.assertRaises(TypeError):
# Too many positional args
TransformDict(str.lower, {}, {})
with self.assertRaises(TypeError):
# Not a callable
TransformDict(object())
d = TransformDict(str.lower)
self.check_underlying_dict(d, {})
pairs = [('Bar', 1), ('Foo', 2)]
d = TransformDict(str.lower, pairs)
self.assertEqual(sorted(d.items()), pairs)
self.check_underlying_dict(d, {'bar': 1, 'foo': 2})
d = TransformDict(str.lower, dict(pairs))
self.assertEqual(sorted(d.items()), pairs)
self.check_underlying_dict(d, {'bar': 1, 'foo': 2})
d = TransformDict(str.lower, **dict(pairs))
self.assertEqual(sorted(d.items()), pairs)
self.check_underlying_dict(d, {'bar': 1, 'foo': 2})
d = TransformDict(str.lower, {'Bar': 1}, Foo=2)
self.assertEqual(sorted(d.items()), pairs)
self.check_underlying_dict(d, {'bar': 1, 'foo': 2})
def test_transform_func(self):
# Test the `transform_func` attribute
d = TransformDict(str.lower)
self.assertIs(d.transform_func, str.lower)
# The attribute is read-only
with self.assertRaises(AttributeError):
d.transform_func = str.upper
def test_various_transforms(self):
d = TransformDict(lambda s: s.encode('utf-8'))
d['Foo'] = 5
self.assertEqual(d['Foo'], 5)
self.check_underlying_dict(d, {b'Foo': 5})
with self.assertRaises(AttributeError):
# 'bytes' object has no attribute 'encode'
d[b'Foo']
# Another example
d = TransformDict(str.swapcase)
d['Foo'] = 5
self.assertEqual(d['Foo'], 5)
self.check_underlying_dict(d, {'fOO': 5})
with self.assertRaises(KeyError):
d['fOO']
# NOTE: we mostly test the operations which are not inherited from
# MutableMapping.
def test_setitem_getitem(self):
d = TransformDict(str.lower)
with self.assertRaises(KeyError):
d['foo']
d['Foo'] = 5
self.assertEqual(d['foo'], 5)
self.assertEqual(d['Foo'], 5)
self.assertEqual(d['FOo'], 5)
with self.assertRaises(KeyError):
d['bar']
self.check_underlying_dict(d, {'foo': 5})
d['BAR'] = 6
self.assertEqual(d['Bar'], 6)
self.check_underlying_dict(d, {'foo': 5, 'bar': 6})
# Overwriting
d['foO'] = 7
self.assertEqual(d['foo'], 7)
self.assertEqual(d['Foo'], 7)
self.assertEqual(d['FOo'], 7)
self.check_underlying_dict(d, {'foo': 7, 'bar': 6})
def test_delitem(self):
d = TransformDict(str.lower, Foo=5)
d['baR'] = 3
del d['fOO']
with self.assertRaises(KeyError):
del d['Foo']
with self.assertRaises(KeyError):
del d['foo']
self.check_underlying_dict(d, {'bar': 3})
def test_get(self):
d = TransformDict(str.lower)
default = object()
self.assertIs(d.get('foo'), None)
self.assertIs(d.get('foo', default), default)
d['Foo'] = 5
self.assertEqual(d.get('foo'), 5)
self.assertEqual(d.get('FOO'), 5)
self.assertIs(d.get('bar'), None)
self.check_underlying_dict(d, {'foo': 5})
def test_getitem(self):
d = TransformDict(str.lower)
d['Foo'] = 5
self.assertEqual(d.getitem('foo'), ('Foo', 5))
self.assertEqual(d.getitem('FOO'), ('Foo', 5))
with self.assertRaises(KeyError):
d.getitem('bar')
def test_pop(self):
d = TransformDict(str.lower)
default = object()
with self.assertRaises(KeyError):
d.pop('foo')
self.assertIs(d.pop('foo', default), default)
d['Foo'] = 5
self.assertIn('foo', d)
self.assertEqual(d.pop('foo'), 5)
self.assertNotIn('foo', d)
self.check_underlying_dict(d, {})
d['Foo'] = 5
self.assertIn('Foo', d)
self.assertEqual(d.pop('FOO'), 5)
self.assertNotIn('foo', d)
self.check_underlying_dict(d, {})
with self.assertRaises(KeyError):
d.pop('foo')
def test_clear(self):
d = TransformDict(str.lower)
d.clear()
self.check_underlying_dict(d, {})
d['Foo'] = 5
d['baR'] = 3
self.check_underlying_dict(d, {'foo': 5, 'bar': 3})
d.clear()
self.check_underlying_dict(d, {})
def test_contains(self):
d = TransformDict(str.lower)
self.assertIs(False, 'foo' in d)
d['Foo'] = 5
self.assertIs(True, 'Foo' in d)
self.assertIs(True, 'foo' in d)
self.assertIs(True, 'FOO' in d)
self.assertIs(False, 'bar' in d)
def test_len(self):
d = TransformDict(str.lower)
self.assertEqual(len(d), 0)
d['Foo'] = 5
self.assertEqual(len(d), 1)
d['BAR'] = 6
self.assertEqual(len(d), 2)
d['foo'] = 7
self.assertEqual(len(d), 2)
d['baR'] = 3
self.assertEqual(len(d), 2)
del d['Bar']
self.assertEqual(len(d), 1)
def test_iter(self):
d = TransformDict(str.lower)
it = iter(d)
with self.assertRaises(StopIteration):
next(it)
d['Foo'] = 5
d['BAR'] = 6
self.assertEqual(set(x for x in d), {'Foo', 'BAR'})
def test_first_key_retained(self):
d = TransformDict(str.lower, {'Foo': 5, 'BAR': 6})
self.assertEqual(set(d), {'Foo', 'BAR'})
d['foo'] = 7
d['baR'] = 8
d['quux'] = 9
self.assertEqual(set(d), {'Foo', 'BAR', 'quux'})
del d['foo']
d['FOO'] = 9
del d['bar']
d.setdefault('Bar', 15)
d.setdefault('BAR', 15)
self.assertEqual(set(d), {'FOO', 'Bar', 'quux'})
def test_repr(self):
d = TransformDict(str.lower)
self.assertEqual(repr(d),
"TransformDict(<method 'lower' of 'str' objects>, {})")
d['Foo'] = 5
self.assertEqual(repr(d),
"TransformDict(<method 'lower' of 'str' objects>, {'Foo': 5})")
def test_repr_non_hashable_keys(self):
d = TransformDict(id)
self.assertEqual(repr(d),
"TransformDict(<built-in function id>, {})")
d[[1]] = 2
self.assertEqual(repr(d),
"TransformDict(<built-in function id>, [([1], 2)])")
class TransformDictMappingTests(TransformDictTestBase,
mapping_tests.BasicTestMappingProtocol):
TransformDict = TransformDict
type2test = partial(TransformDict, str.lower)
def check_shallow_copy(self, copy_func):
d = self.TransformDict(str_lower, {'Foo': []})
e = copy_func(d)
self.assertIs(e.__class__, self.TransformDict)
self.assertIs(e._transform, str_lower)
self.check_underlying_dict(e, {'foo': []})
e['Bar'] = 6
self.assertEqual(e['bar'], 6)
with self.assertRaises(KeyError):
d['bar']
e['foo'].append(5)
self.assertEqual(d['foo'], [5])
self.assertEqual(set(e), {'Foo', 'Bar'})
def check_deep_copy(self, copy_func):
d = self.TransformDict(str_lower, {'Foo': []})
e = copy_func(d)
self.assertIs(e.__class__, self.TransformDict)
self.assertIs(e._transform, str_lower)
self.check_underlying_dict(e, {'foo': []})
e['Bar'] = 6
self.assertEqual(e['bar'], 6)
with self.assertRaises(KeyError):
d['bar']
e['foo'].append(5)
self.assertEqual(d['foo'], [])
self.check_underlying_dict(e, {'foo': [5], 'bar': 6})
self.assertEqual(set(e), {'Foo', 'Bar'})
def test_copy(self):
self.check_shallow_copy(lambda d: d.copy())
def test_copy_copy(self):
self.check_shallow_copy(copy.copy)
def test_cast_as_dict(self):
d = self.TransformDict(str.lower, {'Foo': 5})
e = dict(d)
self.assertEqual(e, {'Foo': 5})
def test_copy_deepcopy(self):
self.check_deep_copy(copy.deepcopy)
def test_pickling(self):
def pickle_unpickle(obj, proto):
data = pickle.dumps(obj, proto)
return pickle.loads(data)
for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
with self.subTest(pickle_protocol=proto):
self.check_deep_copy(partial(pickle_unpickle, proto=proto))
class MyTransformDict(TransformDict):
pass
class TransformDictSubclassMappingTests(TransformDictMappingTests):
TransformDict = MyTransformDict
type2test = partial(MyTransformDict, str.lower)
def test_main(verbose=None):
test_classes = [TestTransformDict, TransformDictMappingTests,
TransformDictSubclassMappingTests]
support.run_unittest(*test_classes)
if __name__ == "__main__":
test_main(verbose=True)

140
dicts/transformdict.py Normal file
View File

@ -0,0 +1,140 @@
"""Trasformdict: a mapping that trasforms keys on lookup
This module and the test_transformdict.py module were extracted from
a patch contributed by Antoine Pitrou implementing his PEP 455 --
"Adding a key-transforming dictionary to collections".
As I write this, the patch was not merged to Python 3.5, but it can be
tracked as issue #18986 "Add a case-insensitive case-preserving dict"
http://bugs.python.org/issue18986
"""
from collections.abc import MutableMapping
_sentinel = object()
class TransformDict(MutableMapping):
'''Dictionary that calls a transformation function when looking
up keys, but preserves the original keys.
>>> d = TransformDict(str.lower)
>>> d['Foo'] = 5
>>> d['foo'] == d['FOO'] == d['Foo'] == 5
True
>>> set(d.keys())
{'Foo'}
'''
__slots__ = ('_transform', '_original', '_data')
def __init__(self, transform, init_dict=None, **kwargs):
'''Create a new TransformDict with the given *transform* function.
*init_dict* and *kwargs* are optional initializers, as in the
dict constructor.
'''
if not callable(transform):
msg = 'expected a callable, got %r'
raise TypeError(msg % transform.__class__)
self._transform = transform
# transformed => original
self._original = {}
self._data = {}
if init_dict:
self.update(init_dict)
if kwargs:
self.update(kwargs)
def getitem(self, key):
'D.getitem(key) -> (stored key, value)'
transformed = self._transform(key)
original = self._original[transformed]
value = self._data[transformed]
return original, value
@property
def transform_func(self):
"This TransformDict's transformation function"
return self._transform
# Minimum set of methods required for MutableMapping
def __len__(self):
return len(self._data)
def __iter__(self):
return iter(self._original.values())
def __getitem__(self, key):
return self._data[self._transform(key)]
def __setitem__(self, key, value):
transformed = self._transform(key)
self._data[transformed] = value
self._original.setdefault(transformed, key)
def __delitem__(self, key):
transformed = self._transform(key)
del self._data[transformed]
del self._original[transformed]
# Methods overriden to mitigate the performance overhead.
def clear(self):
'D.clear() -> None. Remove all items from D.'
self._data.clear()
self._original.clear()
def __contains__(self, key):
return self._transform(key) in self._data
def get(self, key, default=None):
'D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.'
return self._data.get(self._transform(key), default)
def pop(self, key, default=_sentinel):
'''D.pop(k[,d]) -> v, remove key and return corresponding value.
If key is not found, d is returned if given, otherwise
KeyError is raised.
'''
transformed = self._transform(key)
if default is _sentinel:
del self._original[transformed]
return self._data.pop(transformed)
else:
self._original.pop(transformed, None)
return self._data.pop(transformed, default)
def popitem(self):
'''D.popitem() -> (k, v), remove and return some (key, value) pair
as a 2-tuple; but raise KeyError if D is empty.
'''
transformed, value = self._data.popitem()
return self._original.pop(transformed), value
# Other methods
def copy(self):
'D.copy() -> a shallow copy of D'
other = self.__class__(self._transform)
other._original = self._original.copy()
other._data = self._data.copy()
return other
__copy__ = copy
def __getstate__(self):
return (self._transform, self._data, self._original)
def __setstate__(self, state):
self._transform, self._data, self._original = state
def __repr__(self):
try:
equiv = dict(self)
except TypeError:
# Some keys are unhashable, fall back on .items()
equiv = list(self.items())
return '%s(%r, %s)' % (self.__class__.__name__,
self._transform, repr(equiv))

26
operator/Interest.java Normal file
View File

@ -0,0 +1,26 @@
/***
Compound interest function with ``BigDecimal``
Equivalent in Python:
def compound_interest(principal, rate, periods):
return principal * ((1 + rate) ** periods - 1)
***/
import java.math.BigDecimal;
public class Interest {
static BigDecimal compoundInterest(BigDecimal principal, BigDecimal rate, int periods) {
return principal.multiply(BigDecimal.ONE.add(rate).pow(periods).subtract(BigDecimal.ONE));
}
public static void main(String[] args) {
BigDecimal principal = new BigDecimal(1000);
BigDecimal rate = new BigDecimal("0.06");
int periods = 5;
System.out.println(compoundInterest(principal, rate, periods));
}
}

17
operator/dispatch.py Normal file
View File

@ -0,0 +1,17 @@
"""
Experiments with infix operator dispatch
>>> kadd = KnowsAdd()
>>> kadd + 1
(<KnowsAdd object>, 1)
>>> kadd * 1
"""
class KnowsAdd:
def __add__(self, other):
return self, other
def __repr__(self):
return '<{} object>'.format(type(self).__name__)

25
operator/interest.py Normal file
View File

@ -0,0 +1,25 @@
"""
Compound interest function with ``decimal.Decimal``
"""
def compound_interest(principal, rate, periods):
return principal * ((1 + rate) ** periods - 1)
def test(verbose=False):
from decimal import Decimal, getcontext
getcontext().prec = 8
fixture = [(1000, Decimal('0.05'), 1, Decimal('50')),
(1000, Decimal('0.10'), 5, Decimal('610.51')),
(1000, Decimal('0.10'), 15, Decimal('3177.2482')),
(1000, Decimal('0.06'), 5, Decimal('338.2256')),
]
for principal, rate, periods, future_value in fixture:
computed = compound_interest(principal, rate, periods)
if verbose:
print('{!r}, {!r}, {!r} -> {!r}'.format(
principal, rate, periods, computed))
assert future_value == computed, '{!r} != {!r}'.format(future_value, computed)
if __name__ == '__main__':
test(True)