In [1]:
import tohu
from tohu import *
from collections import namedtuple
from utils import print_generated_sequence

In [2]:
print(f"Tohu version: {tohu.__version__}")


Tohu version: v0.5.0+107.g3abf0bb.dirty

Extracting attributes and aliasing


In [3]:
letters = 'abcdefghijklmnopqrstuvwxyz'

Foobar = namedtuple('Foobar', ('foo', 'bar'))
items = [Foobar(c+c, c+c+c) for c in letters]

items[:3]


Out[3]:
[Foobar(foo='aa', bar='aaa'),
 Foobar(foo='bb', bar='bbb'),
 Foobar(foo='cc', bar='ccc')]

Let's define a generator xx which selects random elements from items, and two other generators yy and zz which extract individual attributes from these elements.


In [4]:
xx = SelectOne(items)
yy = xx.foo
zz = xx.bar

When xx is reset, internally this also automatically resets yy and zz with the same seed (because they are "dependent generators" whose parent is xx).


In [5]:
xx.reset(seed=12345)

print_generated_sequence(xx, num=10, sep='\n')
print_generated_sequence(yy, num=10)
print_generated_sequence(zz, num=10)


Generated sequence:
Foobar(foo='cc', bar='ccc')
Foobar(foo='ff', bar='fff')
Foobar(foo='bb', bar='bbb')
Foobar(foo='ee', bar='eee')
Foobar(foo='jj', bar='jjj')
Foobar(foo='ff', bar='fff')
Foobar(foo='cc', bar='ccc')
Foobar(foo='bb', bar='bbb')
Foobar(foo='ww', bar='www')
Foobar(foo='rr', bar='rrr')
Generated sequence: cc, ff, bb, ee, jj, ff, cc, bb, ww, rr
Generated sequence: ccc, fff, bbb, eee, jjj, fff, ccc, bbb, www, rrr

This also works if xx, yy, zz are defined inside a CustomGenerator.


In [6]:
class QuuxGenerator(CustomGenerator):
    xx = SelectOne(items)
    yy = xx.foo
    zz = xx.bar
    ww = yy  # alias

In [7]:
g = QuuxGenerator()

In [8]:
list(g.generate(10, seed=12345))


Out[8]:
[Quux(xx=['jj', 'jjj'], yy='jj', zz='jjj', ww='jj'),
 Quux(xx=['cc', 'ccc'], yy='cc', zz='ccc', ww='cc'),
 Quux(xx=['ee', 'eee'], yy='ee', zz='eee', ww='ee'),
 Quux(xx=['zz', 'zzz'], yy='zz', zz='zzz', ww='zz'),
 Quux(xx=['qq', 'qqq'], yy='qq', zz='qqq', ww='qq'),
 Quux(xx=['aa', 'aaa'], yy='aa', zz='aaa', ww='aa'),
 Quux(xx=['dd', 'ddd'], yy='dd', zz='ddd', ww='dd'),
 Quux(xx=['kk', 'kkk'], yy='kk', zz='kkk', ww='kk'),
 Quux(xx=['kk', 'kkk'], yy='kk', zz='kkk', ww='kk'),
 Quux(xx=['nn', 'nnn'], yy='nn', zz='nnn', ww='nn')]

Just for illustration, let's repeat the last example but with a different set of items (produced by another custom generator, although this doesn't really matter).


In [9]:
class FoobarGenerator(CustomGenerator):
    foo = Integer(0, 100)
    bar = HashDigest(length=8)

In [10]:
fg = FoobarGenerator()

In [11]:
items2 = list(fg.generate(10, seed=12345))
items2


Out[11]:
[Foobar(foo=2, bar='067BB37A'),
 Foobar(foo=64, bar='87921A22'),
 Foobar(foo=18, bar='10547EDB'),
 Foobar(foo=85, bar='EE601CEE'),
 Foobar(foo=82, bar='FE439D8A'),
 Foobar(foo=24, bar='A52C84DB'),
 Foobar(foo=49, bar='C82DCF12'),
 Foobar(foo=58, bar='9F34681B'),
 Foobar(foo=0, bar='FE3E55FB'),
 Foobar(foo=60, bar='E1110DAB')]

In [12]:
class QuuxGenerator(CustomGenerator):
    xx = SelectOne(items2)
    yy = xx.foo
    zz = xx.bar

In [13]:
g = QuuxGenerator()

In [14]:
g.reset(seed=99999); print_generated_sequence(g, num=10, sep='\n')


Generated sequence:
Quux(xx={'foo': 0, 'bar': 'FE3E55FB'}, yy=0, zz='FE3E55FB')
Quux(xx={'foo': 85, 'bar': 'EE601CEE'}, yy=85, zz='EE601CEE')
Quux(xx={'foo': 58, 'bar': '9F34681B'}, yy=58, zz='9F34681B')
Quux(xx={'foo': 0, 'bar': 'FE3E55FB'}, yy=0, zz='FE3E55FB')
Quux(xx={'foo': 2, 'bar': '067BB37A'}, yy=2, zz='067BB37A')
Quux(xx={'foo': 24, 'bar': 'A52C84DB'}, yy=24, zz='A52C84DB')
Quux(xx={'foo': 49, 'bar': 'C82DCF12'}, yy=49, zz='C82DCF12')
Quux(xx={'foo': 2, 'bar': '067BB37A'}, yy=2, zz='067BB37A')
Quux(xx={'foo': 18, 'bar': '10547EDB'}, yy=18, zz='10547EDB')
Quux(xx={'foo': 82, 'bar': 'FE439D8A'}, yy=82, zz='FE439D8A')


In [15]:
class QuuxGenerator(CustomGenerator):
    aaa = Integer(0, 100)
    bbb = HashDigest(length=6)

In [16]:
g = QuuxGenerator()

Using ExtractAttribute we can produce \"derived\" generators which extract the attributes aaa, bbb from the elements produced by g.


In [17]:
h1 = ExtractAttribute(g, 'aaa')
h2 = ExtractAttribute(g, 'bbb')

In [18]:
g.reset(seed=99999)
print_generated_sequence(g, num=10, sep='\n')
print_generated_sequence(h1, num=10)
print_generated_sequence(h2, num=10)


Generated sequence:
Quux(aaa=20, bbb='550617')
Quux(aaa=24, bbb='D461EC')
Quux(aaa=70, bbb='7221B5')
Quux(aaa=47, bbb='FB5E55')
Quux(aaa=92, bbb='539FF2')
Quux(aaa=72, bbb='E94668')
Quux(aaa=19, bbb='DCF91D')
Quux(aaa=67, bbb='DAB699')
Quux(aaa=84, bbb='CBAAAF')
Quux(aaa=57, bbb='F08250')
Generated sequence: 20, 24, 70, 47, 92, 72, 19, 67, 84, 57
Generated sequence: 550617, D461EC, 7221B5, FB5E55, 539FF2, E94668, DCF91D, DAB699, CBAAAF, F08250

Class Lookup

Create mapping from lowercase to uppercase letters.


In [19]:
letters = 'abcdefghijklmnopqrstuvwxyz'
mapping = dict([(c, c.upper()) for c in letters])

Create generator g which selects a random letter and generator h which looks up each letter in the lowercase->uppercase mapping.


In [20]:
g = SelectOne(letters)
h = Lookup(g, mapping)

In [21]:
g.reset(seed=12345)
print_generated_sequence(g, num=20)
print_generated_sequence(h, num=20)


Generated sequence: c, f, b, e, j, f, c, b, w, r, o, j, x, w, q, s, j, l, n, k
Generated sequence: C, F, B, E, J, F, C, B, W, R, O, J, X, W, Q, S, J, L, N, K

In [ ]: