TinkerPy

This Python 2 and 3 package (tested with CPython 2.7 and 3.3 as well as PyPy 2) provides:

Python 2 vs. 3

tinkerpy.PY_VERSION_GT_2

True for a Python runtime with the major version being greater than two, True otherwise.

tinkerpy.py2or3(py2, py3)

Returns one of the given arguments depending on the Python version.

Parameters:
  • py2 – The value to return in Python 2.
  • py3 – The value to return in Python 3.
Returns:

py2 or py3 depending on the Python version.

tinkerpy.metaclass(metacls)

Creates a class decorator using the given metaclass to create a new class from the one which it is decorating. This allows for easier porting between Python 2 and 3 if using metaclasses, as it has the same effect as specifying a metaclass, but is usable for Python 2, 3 and higher.

Parameters:metacls – The metaclass to use.
Returns:A class decorator which, when applied, calls the metaclass with the name, the bases and the dictionary of the decorated class.

Example:

First we define a metaclass which adds an attribute number to its class with an ever-increasing integer:

>>> class Numbering(object):
...     def __init__(self):
...         self.number = 1
...
...     def __call__(self, name, bases, dict):
...         dict['number'] = self.number
...         self.number += 1
...         cls = type(name, bases, dict)
...         return cls
>>> Numbering = Numbering()

Here are two classes using the metaclass:

>>> @metaclass(Numbering)
... class Foo(object):
...     pass
>>> @metaclass(Numbering)
... class Bar(object):
...     pass
>>> print(Foo.number)
1
>>> print(Bar.number)
2
tinkerpy.Unicode()

For Python 2 this is unicode(), for Python 3 this is str().

tinkerpy.STRING_TYPES

For Python 2 this is the tuple() (str, unicode), in Python 3 this is simply str().

Mappings

class tinkerpy.AttributeDict

A mapping like dict, which exposes its values as attributes.

It uses the __getattr__, __delattr__ and __setattr__ hooks, so be aware of that when overriding these methods.

If an attribute is retrieved which does not exist but who’s name is a key in the dictionary, the dictionary value is returned.

If an attribute is set/deleted who’s name is a key in the dictionary, the dictionary entry is updated/deleted. Otherwise the attribute is created/deleted. Thus attribute values shadow attributes on setting/deleting attributes.

Examples:

>>> ad = AttributeDict((('foo', 1), ('bar', 2)))
>>> print(ad.foo); print(ad.bar)
1
2
>>> ad.foo = 3; print(ad.foo == ad['foo'])
True
>>> del ad['bar']
>>> print(ad.bar)
Traceback (most recent call last):
AttributeError: 'bar'
>>> print('bar' in ad)
False
>>> ad.bar = 2
>>> print('bar' in ad)
False
class tinkerpy.ImmutableDict(*args, **kargs)

An immutable mapping that accepts the same constructor arguments as dict.

>>> immutable = ImmutableDict({'foo': 1, 'bar': 2})
>>> print(immutable['foo'])
1
>>> del immutable['foo']
Traceback (most recent call last):
TypeError: 'ImmutableDict' object does not support item deletion
>>> immutable['foo'] = 3
Traceback (most recent call last):
TypeError: 'ImmutableDict' object does not support item assignment
class tinkerpy.ProxyDict(original)

A mutable mapping serving as a proxy for another mapping. Setting values on the instance sets the value on the instance itself, not on the original, only those values can be deleted.

Example:

>>> original = {'foo': 0, 'bar': 1}
>>> proxy = ProxyDict(original)
>>> 'foo' in proxy and 'bar' in proxy
True
>>> proxy['foo'] = -1
>>> original['foo'] == 0 and proxy['foo'] == -1 and len(proxy) == 2
True
>>> proxy['foobar'] = 2
>>> 'foobar' in proxy and len(proxy) == 3
True
>>> sorted(proxy.keys())
['bar', 'foo', 'foobar']
>>> del proxy['foo']
>>> 'foo' in proxy
True
>>> del proxy['foo']
Traceback (most recent call last):
KeyError: 'foo'
is_virtual(key)

Return True if key is not set on the instance itself, but in the source mapping.

len

The number of items defined on the instance itself.

Decorators

tinkerpy.multi_decorator(*decorators)

Allows to create a decorator which applies a list of decorators to a target. The function returned applies the decorators in reverse order of decorators, i.e. in the same order as decorators are written above their target.

Parameters:decorators – Each item must be a callable.
Returns:a function which applies the decorators in reverse order of decorators

Examples:

>>> def data_deco(name, data):
...     def decorator(target):
...         setattr(target, name, data)
...         return target
...     return decorator
...
>>> metadata = multi_decorator(data_deco('title', 'Foo Bar'),
...     data_deco('content', 'Hello World!'))
>>> @metadata
... class Data(object): pass
>>> Data.title
'Foo Bar'
>>> Data.content
'Hello World!'
tinkerpy.namespace(mapping, *names, **attributes)

Creates a function decorator which extends the namespace of the function it is applied to with entries from mapping. Only global values are overridden.

Parameters:
  • mapping (mapping type) – The mapping containing namespace elements.
  • names – The names to define in the function namespace with values of the corresponding mapping entry. If none are given, all entries of mapping are added to the namespace (then it not only has to have the method __getitem__() but must be a mapping conformant to collections.Mapping).
  • attributes – Named attributes which set entries on the namespace, possibly overriding entries from mappings. The entry __staticglobals__ is treated specially and does not define an entry on the namespace, for its meaning see below.
Returns:

The function decorator created.

Important: If attributes contains an entry __staticglobals__, this defines if the globals are to be treated static. If globals are treated static, in the decorated function only the state of the globals at definition time is available. Otherwise changes MAY be reflected in the function, but behavior for Python 2 is unspecified. For example in CPython 2 dynamic globals are inaccessible, whereas it works in PyPy 2. In Python 3 this works, as types.FunctionType allows for arbitrary mappings to be used as globals. Thus the default for Python 2 is to use static globals for compatibility, in Python 3 the default is to use dynamic globals. To set the __staticglobals__ entry of attributes to False is the only way to archive predicatble behavior under Python 2 and 3 in all cases, otherwise be sure not modify entries in the global namespace after decorating the function.

Examples:

>>> class StringGenerator(object):
...     def __getitem__(self, name):
...         return 'StringGen: ' + name
...
>>> a = 1
>>> f = 'global'
>>> @namespace(StringGenerator(), 'a', 'c', 'd', 'e', e='namespace e')
... def test(b, c=3):
...     print(a)
...     print(b)
...     print(c)
...     print(d)
...     print(e)
...     print(f)
>>> test(2)
StringGen: a
2
3
StringGen: d
namespace e
global
>>> @namespace(StringGenerator())
... def test():
...     print(a)
...
Traceback (most recent call last):
ValueError: The first argument must be a mapping.
>>> @namespace(dict(a='namespace a', c='namespace c', d='namespace d'))
... def test(b, c=3):
...     print(a)
...     print(b)
...     print(c)
...     print(d)
>>> test(2)
namespace a
2
3
namespace d
tinkerpy.update_globals(**additional_entries)

Returns a function decorator which updates the globals of the function it is applied to with the entries from additional_entries.

Parameters:additional_entries – The entries to update the globals with. The entry __staticglobals__ is treated specially and does not define an entry on the namespace, for its meaning see below.
Returns:The function decorator created.

Important: If additional_entries contains an entry __staticglobals__, this defines if the globals are to be treated static. If globals are treated static, in the decorated function only the state of the globals at definition time is available. Otherwise changes MAY be reflected in the function, but behavior for Python 2 is unspecified. For example in CPython 2 dynamic globals are inaccessible, whereas it works in PyPy 2. In Python 3 this works, as types.FunctionType allows for arbitrary mappings to be used as globals. Thus the default for Python 2 is to use static globals for compatibility, in Python 3 the default is to use dynamic globals. To set the __staticglobals__ entry of additional_entries to False is the only way to archive predicatble behavior under Python 2 and 3 in all cases, otherwise be sure not modify entries in the global namespace after decorating the function.

Example:

>>> foo = 0
>>> bar = 1
>>> @update_globals(foo=-1)
... def foobar():
...     print(foo)
...     print(bar)
>>> foobar()
-1
1
tinkerpy.attribute_dict(target)

A decorator to create AttributeDict instances from callables return values.

Parameters:target – The callable to be wrapped.
Returns:A function which wraps target and returns an AttributeDict from the return value of target.

Example:

>>> @attribute_dict
... def Test(z):
...     def t(foo):
...         print(z)
...         print(foo)
...     return locals()
...
>>> t = Test('foo')
>>> t.z
'foo'
>>> t.t('bar')
foo
bar

SAX Handlers

class tinkerpy.LexicalHandler

A stub base class for a lexical handler (see xml.sax.handler.property_lexial_handler).

comment(content)

Receive notification of a comment.

startCDATA()

Receive notification of the beginning of CDATA section.

endCDATA()

Receive notification of the end of CDATA section.

class tinkerpy.DeclarationHandler

A stub base class for a declaration handler (see xml.sax.handler.property_declaration_handler).

startDTD(name, public_id, system_id)

Receive notification of the beginning of a DTD.

endDTD()

Receive notification of the end of a DTD.

startEntity(name)

Receive notification of the beginning of an entity.

endEntity(name)

Receive notification of the end of an entity.

Others

class tinkerpy.StrIntegers(base=10, ignore_case=False, lookup_table=None)

Instances of this class care handling of integers represented as a string in a given base, using a lookup table. The default lookup table implements an alpha-numerical mapping similar to int(): first the digits 0 through 9, then the lower-case alphabet a-z, followed by the upper-case alphabet A-Z. Thus the maximum base with this lookup table is 62. To consistently deal with lower-case letters, use the ignore_case initialization argument, but be sure to use an appropriate lookup table (if using the default this is cared for automatically).

Parameters:
  • base (class:int) – The base to use for conversion, this must be greater than or equal 2 and less than or equal the size of the lookup table. If no special lookup table is given, the maximum is 62.
  • ignore_case (bool) – If this is True, all string values given are converted to lower case. If the default lookup table is used, it is trunctated to end at the lower case z.
  • lookup_table – The lookup table to use, an iterable of non-empty string values with minimum length 1. The index defines which number the string represents. If this is None, the default lookup table consisting of the digits 0-9, the characters a-z and A-Z is used.
Raises ValueError:
 

If the base, the lookup table or its values are invalid.

:
>>> StrIntegers(1)
Traceback (most recent call last):
ValueError: Base must be >= 2 and <= 62.
>>> StrIntegers(63)
Traceback (most recent call last):
ValueError: Base must be >= 2 and <= 62.
>>> str_ints = StrIntegers(9, True, 'abcdefgh')
Traceback (most recent call last):
ValueError: Base must be >= 2 and <= 8.
>>> str_ints = StrIntegers(lookup_table=['', '0'])
Traceback (most recent call last):
ValueError: The lookup table items must be Unicode strings with minimum length 1.
>>> str_ints = StrIntegers(lookup_table=['a'])
Traceback (most recent call last):
ValueError: The lookup table must have a length of at least 2.
base

The base the instance uses for conversion.

lookup_table

The lookup table the instance uses for conversion. It is a tuple of Unicode string values of length 1.

int_to_str(value)

Converts an integer value to a string, inverse to what int() does, using the instance’s base and lookup table.

Parameters:value (int) – The value to convert, an arbitrary integer value.
Returns:A string representing the given value.
Return type:str

Examples:

>>> str_ints = StrIntegers(36)
>>> print(str_ints.int_to_str(256))
74
>>> print(str_ints.int_to_str(-256))
-74
>>> str_ints = StrIntegers(8, True, 'abcdefgh')
>>> print(str_ints.int_to_str(256))
eaa
:
>>>
>>> test_conversion = lambda si, v: int(si.int_to_str(v), si.base) == v
>>> for base in range(2, 37):
...     str_ints = StrIntegers(base)
...     for value in range(-128, 129):
...         str_value = str_ints.int_to_str(value)
...         int_value = int(str_value, str_ints.base)
...         if int_value != value:
...             print('Test failed for value {} and base {}: {} => {}'.format(value, base, str_value, int_value))
...             break
str_to_int(value)

Converts a string to an integer value, like int(), using the instance’s base and lookup table. Characters not defined in the lookup table are ignored. If no known character is found 0 is returned. The first character may be '-' or '+' to indicate a positive or negative value. The time complexity of this method is in O(len(value)).

Parameters:
  • value (Unicode string) – The string representing an integer.
  • value – The string representing an integer.
Returns:

The integer represented by value.

Return type:

int

Examples:

>>> str_ints = StrIntegers(36)
>>> str_ints.str_to_int('VALUE 74')
256
>>> str_ints.str_to_int('- 7 4')
-256
>>> str_ints = StrIntegers(8, True, 'abcdefgh')
>>> str_ints.str_to_int('0 E 1 A 2 A')
256
:
>>> for base in range(2, 37):
...     str_ints = StrIntegers(base)
...     for value in range(-128, 129):
...         str_value = str_ints.int_to_str(value)
...         int_value = str_ints.str_to_int(str_value)
...         if int_value != value:
...             print('Test failed for value {} and base {}: {} (compared to {})'.format(value, base, str_value, int_value))
...             break
increment(value)

Increments an arbitrary integer value given as a string by one, using the instance’s base and lookup table. Characters not defined in the lookup table are ignored. If no known character is found 1 is returned. The first character may be '-' or '+' to indicate a positive or negative value. The time complexity of this method is in O(len(value)).

Parameters:value (Unicode string) – The string representing an integer.
Returns:A string representing the incremented value.
Return type:Unicode string

Examples:

>>> str_ints = StrIntegers(36)
>>> print(str_ints.increment('z'))
10
>>> print(str_ints.increment(' z z '))
100
>>> print(str_ints.increment('- 1 0 '))
-z
>>> print(str_ints.increment('-100'))
-zz
>>> print(str_ints.increment('- 1'))
0
>>> print(str_ints.increment('0'))
1
>>> print(str_ints.increment('1'))
2
>>> print(str_ints.increment('zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'))
10000000000000000000000000000000000000000000000000000
>>> str_ints = StrIntegers(8, True, 'abcdefgh')
>>> print(str_ints.increment(' H H H '))
baaa
decrement(value)

Decrements an arbitrary integer value given as a string by one, using the instance’s base and lookup table. Characters not defined in the lookup table are ignored. If no known character is found -1 is returned. The first character may be '-' or '+' to indicate a positive or negative value. The time complexity of this method is in O(len(value)).

Parameters:value (Unicode string) – The string representing an integer.
Returns:A string representing the decremented value.
Return type:Unicode string

Examples:

>>> str_ints = StrIntegers(36)
>>> print(str_ints.decrement('10'))
z
>>> print(str_ints.decrement(' 1 0 0 '))
zz
>>> print(str_ints.decrement('- z '))
-10
>>> print(str_ints.decrement('-zz'))
-100
>>> print(str_ints.decrement('- 1'))
-2
>>> print(str_ints.decrement('0'))
-1
>>> print(str_ints.decrement('1'))
0
>>> print(str_ints.decrement('10000000000000000000000000000000000000000000000000000'))
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
>>> str_ints = StrIntegers(8, True, 'abcdefgh')
>>> print(str_ints.decrement(' B A A A '))
hhh
class tinkerpy.AllowingList(iterable=None)

A list which checks on adding items, if the given item is allowed. This class should be inherited from and extending classes should specify a class or instance atttibute ALLOWED_ITEMS containing an iterable. Each item of the iterable can either be a type object (i.e. a class), a callable taking one argument or a string. On adding the item is checked against the contents of ALLOWED_ITEMS: if the item is an instance of a class contained there, a callable contained there returns True for the item or the name of the item’s class and its super-classes is contained there, then the item is added to the list, otherwise a ValueError is thrown.

Example:

>>> class TestList(AllowingList):
...     ALLOWED_ITEMS = {'TestList', dict, lambda item: bool(item)}
...
>>> t = TestList([True, ''])
Traceback (most recent call last):
ValueError: Invalid item: ''
>>> t = TestList([TestList()])
>>> t[0] = False
Traceback (most recent call last):
ValueError: Invalid item: False
>>> t.append({1: 2})
>>> t.append(0)
Traceback (most recent call last):
ValueError: Invalid item: 0
>>> t.insert(0, True)
>>> t.insert(0, False)
Traceback (most recent call last):
ValueError: Invalid item: False
>>> t.extend([1, False, 'Test'])
Traceback (most recent call last):
ValueError: Invalid item: False
>>> print(t)
tinkerpy.TestList([True, tinkerpy.TestList([]), {1: 2}, 1])
is_allowed_child(item)

Return True if item is an allowed item.

Parameters:item – The item to check.
Returns:True if item is a valid item.
insert(index, item)

Insert item at index.

tinkerpy.anonymous_class(_extends=<type 'object'>, **attributes)

Creates a new-style class object extending the class extends and the attributes given by attributes.

Parameters:
  • _extends – The class to extend.
  • attributes – The attributes of the new class.
Returns:

a new class object.

Example:

>>> Test = anonymous_class(
...     foo=lambda self: self.bar,
...     bar=0,
...     baz=classmethod(lambda cls: cls.bar),
...     foobar=staticmethod(lambda: 1)
... )
>>> test = Test()
>>> test.foo()
0
>>> Test.baz()
0
>>> Test.foobar()
1
tinkerpy.flatten(obj, *flattening_configurations)

Flattens iterable data structures.

Parameters:
  • obj – The object to flatten. It should be an iterable.
  • flattening_configurations

    An arbitrary number of flattening configurations. A flattening configuration is a 1- or 2-tuple containing callables with one argument. The first callable is a test, which should return True if the configuration applies to the given object and False otherwise. The second callable, if given, is used to flatten the given object. If it does not exist, it is assumed to be None.

    If no flattening configuration is given, the following is used:

    (
        (lambda obj: isinstance(obj, collections.Mapping),
            lambda obj: obj.values()),
        (lambda obj: (isinstance(obj, collections.Iterable)
                and not isinstance(obj, STRING_TYPES)), )
    )
    
Returns:

A generator returning all descendants of all of elements of obj.

Flattening works as follows:

  1. For each element e in the object to flatten do:

    1. Iterate over the flattening configurations:

      • If the test (the first callable of the current configuration) returns True, stop iterating over the configurations and memorize e is flattable. If the second callable exists and is not None, assign e as the result of calling this callable with e. Otherwise e is not modified and memorized as being not flattable.
      • Otherwise go to next configuration.
    2. If e is flattable, flatten it and yield each resulting element. Otherwise yield e.

This function flattens obj as just described, creating a generator returning each element of each flattable descendant of obj.

Examples:

>>> mapping = {1: 'foo', 2: 'bar'}
>>> iterable = ('Hello', 'World', mapping)
>>> for e in flatten(iterable):
...     print(e)
Hello
World
foo
bar
>>> flattening_configs = (
...     (lambda obj: isinstance(obj, collections.Mapping),
...         lambda obj: obj.keys(), ),
...     (lambda obj: (isinstance(obj, collections.Iterable)
...             and not isinstance(obj, STRING_TYPES)), ),
... )
>>> tuple(flatten(iterable, *flattening_configs))
('Hello', 'World', 1, 2)

tinkerpy.fsm – A Finite State Machine API

This module implements an API for defining and executing finite state machines (FSMs) defined as classes. A FSM is a mathematical model consisting of ,,states’’ and a set of ,,transitions’’ between or on them. A FSM instance is always in only one of those states. If a transitions between two states is executed, after execution the new state of the instance is the target state of the transition.

Defining a FSM

A finite state machine is a sub-class of the following class:

class tinkerpy.fsm.FiniteStateMachine

This abstract base class implements the finite state machine execution model. Subclasses represent finite state machines.

On instanciation the metadata created by the state and transition definitions (done by state_data(), entering_state(), leaving_state(), transition() and on_transition()) is processed (thus the __init__() method should always be called). Be aware that the bindings created by those definitions are by name of the methods.

Instances allow access to the machine structure (states, transitions). Additionally they can be called with an object and a transition name to execute the transition on the object, which calls the source state’s leaving callback, the transitions callback and the target state’s entering callback (assuming those exist) and finally sets the attribute FSM_current_state on the object to the target state’s name.

Raises ValueError:
 If there are errors in the machine’s definition.
start_state

The start state name.

On sub-classes states and transitions should be defined, these can be associated with arbitrary data. Each state and transition has a name, the name of a transition identifies it under its source state. Additionally methods of the class can be specified to be called on executing a transition or on leaving or entering a state. States must not be defined specially, they can also be given indirectly by referencing them in transitions.

The following functions can be executed in the body of a FSM class to define states or transitions, respectively:

tinkerpy.fsm.state_data(state_name, **data)

Associates the given data with a state. The data dictionary of the state with the given state_name is updated with data.

Parameters:
  • state_name – The name of the state.
  • data – The entries to update the state’s data dictionary with.
tinkerpy.fsm.transition(transition_name, source_name, target_name, **data)

Defines a transition and optionally updates the transitions’s data dictionary.

Parameters:
  • transition_name – The name of the transition to identify it under its source state.
  • source_name – The name of the source state.
  • target_name – The target state’s name.
  • data – The entries to update the transition’s data dictionary with.

Executing one of the following functions creates a decorator to be applied to methods in the body of a FSM class, which registers those methods by name to be executed on entering or leaving a state or on executing a transition:

tinkerpy.fsm.entering_state(state_name=None)

The result of this function should be used as a method decorator to register the decorated function which will be executed on entering a state. If no state is given the name of the decorated method denotes the state.

Parameters:state_name – The name of the state the decorated method should be executed on entering. If this is None the name of the decorated method denotes the state.
Returns:A method decorator.
tinkerpy.fsm.leaving_state(state_name=None)

The result of this function should be used as a method decorator to register the decorated function which will be executed on leaving a state. If no state is given the name of the decorated method denotes the state.

Parameters:state_name – The name of the state the decorated method should be executed on leaving. If this is None the name of the decorated method denotes the state.
Returns:A method decorator.
tinkerpy.fsm.on_transition(*args, **data)

The result of this function should be used as a method decorator to register the decorated function which will be called on executing a transition and to optionally update the transition’s data dictionary. If no transition name is given the name of the decorated method denotes the transition’s name.

Parameters:
  • args – The positional arguments denote the transition. If there are three arguments, they are interpreted as transition name, source state name and target state name. If there are only two arguments, they are the source and target name while the transition name is determined by the decorated method’s name .
  • data – The entries to update the transition’s data dictionary with.
Returns:

A method decorator.

Raises ValueError:
 

If there are less than 2 or more than 3 positional arguments.

An Example FSM

>>> class MyFSM(FiniteStateMachine):
...    FSM_start = 'juvenile'
...
...    @entering_state()
...    def juvenile(self, obj):
...        print('Born again.')
...        return 'new body'
...
...    @entering_state()
...    def adult(self, obj):
...        print('Now being adult.')
...        return 'new responsibilities'
...
...    @leaving_state('adult')
...    def leave_adult(self, obj):
...        print('Getting old.')
...
...    @on_transition('juvenile', 'adult')
...    def grow_up(self, obj):
...        print('Growing up.')
...        return 'changed body'
...
...    @on_transition('adult', 'dead')
...    def die(self, obj):
...        print('Dying.')
...
...    transition('reincarnate', 'dead', 'juvenile')
...
>>> class MyObject(object): pass
>>> my_fsm = MyFSM()
>>> obj = MyObject()
>>> print(my_fsm(obj).name) # retrieve name of current state
juvenile
>>> my_fsm(obj, 'grow_up')
Growing up.
Now being adult.
('changed body', 'new responsibilities')
>>> print(my_fsm(obj).name)
adult
>>> my_fsm(obj, 'die')
Getting old.
Dying.
>>> print(my_fsm(obj).name)
dead
>>> my_fsm(obj, 'reincarnate')
Born again.
'new body'
>>> print(my_fsm(obj).name)
juvenile

Table Of Contents