Franck Pommereau

merged

......@@ -8,3 +8,4 @@ dput.sh
.gone
,*
*.class
*.bak
......
"""SNAKES is the Net Algebra Kit for Editors and Simulators
@author: Franck Pommereau
@organization: University of Paris 12
@copyright: (C) 2005 Franck Pommereau
@license: GNU Lesser General Public Licence (aka. GNU LGPL),
see the file C{doc/COPYING} in the distribution or visit U{the GNU
web site<http://www.gnu.org/licenses/licenses.html#LGPL>}
@contact: pommereau@univ-paris12.fr
SNAKES is a Python library allowing to model all sorts of Petri nets
and to execute them. It is very general as most Petri nets annotations
can be arbitrary Python expressions while most values can be arbitrary
......@@ -16,6 +8,14 @@ Python objects.
SNAKES can be further extended with plugins, several ones being
already provided, in particular two plugins implement the Petri nets
compositions defined for the Petri Box Calculus and its successors.
@author: Franck Pommereau
@organization: University of Evry/Paris-Saclay
@copyright: (C) 2005-2013 Franck Pommereau
@license: GNU Lesser General Public Licence (aka. GNU LGPL), see the
file `doc/COPYING` in the distribution or visit [the GNU web
site](http://www.gnu.org/licenses/licenses.html#LGPL)
@contact: franck.pommereau@ibisc.univ-evry.fr
"""
version = "0.9.16"
......
"""Basic data types and functions used in SNAKES"""
"""Basic data types and functions used in SNAKES
"""
import operator, inspect
from snakes.compat import *
......@@ -8,7 +9,7 @@ from snakes.pnml import Tree
def cross (sets) :
"""Cross-product.
>>> list(cross([[1, 2], [3, 4, 5]]))
[(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)]
>>> list(cross([[1, 2], [3, 4, 5], [6, 7, 8, 9]]))
......@@ -18,12 +19,12 @@ def cross (sets) :
(2, 4, 8), (2, 4, 9), (2, 5, 6), (2, 5, 7), (2, 5, 8), (2, 5, 9)]
>>> list(cross([[], [1]]))
[]
@param sets: the sets of values to use
@type sets: C{iterable(iterable(object))}
@return: the C{list} of obtained tuples (lists are used to allow
unhashable objects)
@rtype: C{generator(tuple(object))}
@type sets: `iterable(iterable(object))`
@return: the `list` of obtained tuples (lists are used to allow
unhashable objects)
@rtype: `generator(tuple(object))`
"""
if len(sets) == 0 :
pass
......@@ -36,8 +37,8 @@ def cross (sets) :
yield (item,) + others
def iterate (value) :
"""Like Python's builtin C{iter} but consider strings as atomic.
"""Like Python's builtin `iter` but consider strings as atomic.
>>> list(iter([1, 2, 3]))
[1, 2, 3]
>>> list(iterate([1, 2, 3]))
......@@ -46,12 +47,12 @@ def iterate (value) :
['f', 'o', 'o']
>>> list(iterate('foo'))
['foo']
@param value: any object
@type value: C{object}
@return: an iterator on the elements of C{value} if is is iterable
and is not string, an iterator on the sole C{value} otherwise
@rtype: C{generator}
@type value: `object`
@return: an iterator on the elements of `value` if is is iterable
and is not string, an iterator on the sole `value` otherwise
@rtype: `generator`
"""
if isinstance(value, str) :
return iter([value])
......@@ -62,11 +63,12 @@ def iterate (value) :
return iter([value])
class WordSet (set) :
"""A set of words being able to generate fresh words."""
"""A set of words being able to generate fresh words.
"""
def fresh (self, add=False, min=1, allowed="abcdefghijklmnopqrstuvwxyz",
base="") :
"""Create a fresh word (ie, which is not in the set).
>>> w = WordSet(['foo', 'bar'])
>>> list(sorted(w))
['bar', 'foo']
......@@ -78,13 +80,13 @@ class WordSet (set) :
'baa'
>>> list(sorted(w))
['aaa', 'baa', 'bar', 'foo']
@param add: add the created word to the set if C{add=True}
@type add: C{bool}
@param add: add the created word to the set if `add=True`
@type add: `bool`
@param min: minimal length of the new word
@type min: C{int}
@type min: `int`
@param allowed: characters allowed in the new word
@type allowed: C{str}
@type allowed: `str`
"""
if base :
result = [base] + [allowed[0]] * max(0, min - len(base))
......@@ -113,32 +115,33 @@ class WordSet (set) :
class MultiSet (hdict) :
"""Set with repetitions, ie, function from values to integers.
MultiSets support various operations, in particular: addition
(C{+}), substraction (C{-}), multiplication by a non negative
integer (C{*k}), comparisons (C{<}, C{>}, etc.), length (C{len})"""
(`+`), substraction (`-`), multiplication by a non negative
integer (`*k`), comparisons (`<`, `>`, etc.), length (`len`)
"""
def __init__ (self, values=[]) :
"""Initialise the multiset, adding values to it.
>>> MultiSet([1, 2, 3, 1, 2])
MultiSet([...])
>>> MultiSet()
MultiSet([])
@param values: a single value or an iterable object holding
values (strings are not iterated)
@type values: any atomic object (C{str} included) or an
iterable object
values (strings are not iterated)
@type values: any atomic object (`str` included) or an
iterable object
"""
self.add(values)
def copy (self) :
"""Copy a C{MultiSet}
"""Copy a `MultiSet`
>>> MultiSet([1, 2, 3, 1, 2]).copy()
MultiSet([...])
@return: a copy of the multiset
@rtype: C{MultiSet}
@rtype: `MultiSet`
"""
result = MultiSet()
result.update(self)
......@@ -202,7 +205,7 @@ class MultiSet (hdict) :
@classmethod
def __pnmlload__ (cls, tree) :
"""Load a multiset from its PNML representation
>>> t = MultiSet([1, 2, 3, 4, 1, 2]).__pnmldump__()
>>> MultiSet.__pnmlload__(t)
MultiSet([...])
......@@ -214,13 +217,12 @@ class MultiSet (hdict) :
result._add(value, times)
return result
def _add (self, value, times=1) :
"""Add a single value C{times} times.
"""Add a single value `times` times.
@param value: the value to add
@type value: any object
@param times: the number of times that C{value} has to be
added
@type times: non negative C{int}
@param times: the number of times that `value` has to be added
@type times: non negative `int`
"""
if times < 0 :
raise ValueError("negative values are forbidden")
......@@ -230,7 +232,7 @@ class MultiSet (hdict) :
self[value] = times
def add (self, values, times=1) :
"""Add values to the multiset.
>>> m = MultiSet()
>>> m.add([1, 2, 2, 3], 2)
>>> list(sorted(m.items()))
......@@ -238,23 +240,24 @@ class MultiSet (hdict) :
>>> m.add(5, 3)
>>> list(sorted(m.items()))
[1, 1, 2, 2, 2, 2, 3, 3, 5, 5, 5]
@param values: the values to add or a single value to add
@type values: any atomic object (C{str} included) or an
iterable object
@type values: any atomic object (`str` included) or an
iterable object
@param times: the number of times each value should be added
@type times: non negative C{int}"""
@type times: non negative `int`
"""
self.__mutable__()
for value in iterate(values) :
self._add(value, times)
def _remove (self, value, times=1) :
"""Remove a single value C{times} times.
"""Remove a single value `times` times.
@param value: the value to remove
@type value: any object
@param times: the number of times that C{value} has to be
removed
@type times: non negative C{int}
@param times: the number of times that `value` has to be
removed
@type times: non negative `int`
"""
if times < 0 :
raise ValueError("negative values are forbidden")
......@@ -265,7 +268,7 @@ class MultiSet (hdict) :
del self[value]
def remove (self, values, times=1) :
"""Remove values to the multiset.
>>> m = MultiSet()
>>> m.add([1, 2, 2, 3], 2)
>>> list(sorted(m.items()))
......@@ -276,82 +279,85 @@ class MultiSet (hdict) :
>>> m.remove([1, 3], 2)
>>> list(sorted(m.items()))
[2]
@param values: the values to remove or a single value to remove
@type values: any atomic object (C{str} included) or an
iterable object
@param values: the values to remove or a single value to
remove
@type values: any atomic object (`str` included) or an
iterable object
@param times: the number of times each value should be removed
@type times: non negative C{int}"""
@type times: non negative `int`
"""
self.__mutable__()
for value in iterate(values) :
self._remove(value, times)
def __call__ (self, value) :
"""Number of occurrences of C{value}.
"""Number of occurrences of `value`.
>>> m = MultiSet([1, 1, 2, 3, 3, 3])
>>> m(1), m(2), m(3), m(4)
(2, 1, 3, 0)
@param value: the value the count
@type value: C{object}
@rtype: C{int}
@type value: `object`
@rtype: `int`
"""
return self.get(value, 0)
def __iter__ (self) :
"""Iterate over the values (with repetitions).
Use C{MultiSet.keys} to ignore repetitions.
Use `MultiSet.keys` to ignore repetitions.
>>> list(sorted(iter(MultiSet([1, 2, 3, 1, 2]))))
[1, 1, 2, 2, 3]
@return: an iterator on the elements
@rtype: C{iterator}"""
@rtype: `iterator`
"""
for value in dict.__iter__(self) :
for count in range(self[value]) :
yield value
def items (self) :
"""
Return the list of items with repetitions. The list without
repetitions can be retrieved with the C{key} method.
"""Return the list of items with repetitions. The list without
repetitions can be retrieved with the `key` method.
>>> m = MultiSet([1, 2, 2, 3])
>>> list(sorted(m.items()))
[1, 2, 2, 3]
>>> list(sorted(m.keys()))
[1, 2, 3]
@return: list of items with repetitions
@rtype: C{list}"""
@rtype: `list`
"""
return list(iter(self))
def __str__ (self) :
"""Return a simple string representation of the multiset
>>> str(MultiSet([1, 2, 2, 3]))
'{...}'
@return: simple string representation of the multiset
@rtype: C{str}
@rtype: `str`
"""
return "{%s}" % ", ".join(repr(x) for x in self)
def __repr__ (self) :
"""Return a string representation of the multiset that is
suitable for C{eval}
suitable for `eval`
>>> repr(MultiSet([1, 2, 2, 3]))
'MultiSet([...])'
@return: precise string representation of the multiset
@rtype: C{str}
@rtype: `str`
"""
return "MultiSet([%s])" % ", ".join(repr(x) for x in self)
def __len__ (self) :
"""Return the number of elements, including repetitions.
>>> len(MultiSet([1, 2] * 3))
6
@rtype: C{int}
@rtype: `int`
"""
if self.size() == 0 :
return 0
......@@ -359,22 +365,22 @@ class MultiSet (hdict) :
return reduce(operator.add, self.values())
def size (self) :
"""Return the number of elements, excluding repetitions.
>>> MultiSet([1, 2] * 3).size()
2
@rtype: C{int}
@rtype: `int`
"""
return dict.__len__(self)
def __add__ (self, other) :
"""Adds two multisets.
>>> MultiSet([1, 2, 3]) + MultiSet([2, 3, 4])
MultiSet([...])
@param other: the multiset to add
@type other: C{MultiSet}
@rtype: C{MultiSet}
@type other: `MultiSet`
@rtype: `MultiSet`
"""
result = self.copy()
for value, times in dict.items(other) :
......@@ -382,17 +388,17 @@ class MultiSet (hdict) :
return result
def __sub__ (self, other) :
"""Substract two multisets.
>>> MultiSet([1, 2, 3]) - MultiSet([2, 3])
MultiSet([1])
>>> MultiSet([1, 2, 3]) - MultiSet([2, 3, 4])
Traceback (most recent call last):
...
ValueError: not enough occurrences
@param other: the multiset to substract
@type other: C{MultiSet}
@rtype: C{MultiSet}
@type other: `MultiSet`
@rtype: `MultiSet`
"""
result = self.copy()
for value, times in dict.items(other) :
......@@ -400,13 +406,13 @@ class MultiSet (hdict) :
return result
def __mul__ (self, other) :
"""Multiplication by a non-negative integer.
>>> MultiSet([1, 2]) * 3
MultiSet([...])
@param other: the integer to multiply
@type other: non-negative C{int}
@rtype: C{MultiSet}
@type other: non-negative `int`
@rtype: `MultiSet`
"""
if other < 0 :
raise ValueError("negative values are forbidden")
......@@ -420,15 +426,15 @@ class MultiSet (hdict) :
__hash__ = hdict.__hash__
def __eq__ (self, other) :
"""Test for equality.
>>> MultiSet([1, 2, 3]*2) == MultiSet([1, 2, 3]*2)
True
>>> MultiSet([1, 2, 3]) == MultiSet([1, 2, 3, 3])
False
@param other: the multiset to compare with
@type other: C{MultiSet}
@rtype: C{bool}
@type other: `MultiSet`
@rtype: `bool`
"""
if len(self) != len(other) :
return False
......@@ -442,20 +448,20 @@ class MultiSet (hdict) :
return True
def __ne__ (self, other) :
"""Test for difference.
>>> MultiSet([1, 2, 3]*2) != MultiSet([1, 2, 3]*2)
False
>>> MultiSet([1, 2, 3]) != MultiSet([1, 2, 3, 3])
True
@param other: the multiset to compare with
@type other: C{MultiSet}
@rtype: C{bool}
@type other: `MultiSet`
@rtype: `bool`
"""
return not(self == other)
def __lt__ (self, other) :
"""Test for strict inclusion.
>>> MultiSet([1, 2, 3]) < MultiSet([1, 2, 3, 4])
True
>>> MultiSet([1, 2, 3]) < MultiSet([1, 2, 3, 3])
......@@ -466,10 +472,10 @@ class MultiSet (hdict) :
False
>>> MultiSet([1, 2, 2]) < MultiSet([1, 2, 3, 4])
False
@param other: the multiset to compare with
@type other: C{MultiSet}
@rtype: C{bool}
@type other: `MultiSet`
@rtype: `bool`
"""
if not set(self.keys()) <= set(other.keys()) :
return False
......@@ -483,7 +489,7 @@ class MultiSet (hdict) :
return result or (dict.__len__(self) < dict.__len__(other))
def __le__ (self, other) :
"""Test for inclusion inclusion.
>>> MultiSet([1, 2, 3]) <= MultiSet([1, 2, 3, 4])
True
>>> MultiSet([1, 2, 3]) <= MultiSet([1, 2, 3, 3])
......@@ -494,10 +500,10 @@ class MultiSet (hdict) :
False
>>> MultiSet([1, 2, 2]) <= MultiSet([1, 2, 3, 4])
False
@param other: the multiset to compare with
@type other: C{MultiSet}
@rtype: C{bool}
@type other: `MultiSet`
@rtype: `bool`
"""
if not set(self.keys()) <= set(other.keys()) :
return False
......@@ -508,7 +514,7 @@ class MultiSet (hdict) :
return True
def __gt__ (self, other) :
"""Test for strict inclusion.
>>> MultiSet([1, 2, 3, 4]) > MultiSet([1, 2, 3])
True
>>> MultiSet([1, 2, 3, 3]) > MultiSet([1, 2, 3])
......@@ -519,15 +525,15 @@ class MultiSet (hdict) :
False
>>> MultiSet([1, 2, 3, 4]) > MultiSet([1, 2, 2])
False
@param other: the multiset to compare with
@type other: C{MultiSet}
@rtype: C{bool}
@type other: `MultiSet`
@rtype: `bool`
"""
return other.__lt__(self)
def __ge__ (self, other) :
"""Test for inclusion.
>>> MultiSet([1, 2, 3, 4]) >= MultiSet([1, 2, 3])
True
>>> MultiSet([1, 2, 3, 3]) >= MultiSet([1, 2, 3])
......@@ -538,40 +544,41 @@ class MultiSet (hdict) :
False
>>> MultiSet([1, 2, 3, 4]) >= MultiSet([1, 2, 2])
False
@param other: the multiset to compare with
@type other: C{MultiSet}
@rtype: C{bool}
@type other: `MultiSet`
@rtype: `bool`
"""
return other.__le__(self)
def domain (self) :
"""Return the domain of the multiset
>>> list(sorted((MultiSet([1, 2, 3, 4]) + MultiSet([1, 2, 3])).domain()))
[1, 2, 3, 4]
@return: the set of values in the domain
@rtype: C{set}
@rtype: `set`
"""
return set(self.keys())
class Substitution (object) :
"""Map names to values or names, equals the identity where not defined.
Substitutions support the C{+} operation (union with consistency
check between the two operands) and the C{*} operation which is
the composition of functions (C{(f*g)(x)} is C{f(g(x))}).
Several methods (eg, C{image}) return lists instead of sets, this
"""Map names to values or names, equals the identity where not
defined.
Substitutions support the `+` operation (union with consistency
check between the two operands) and the `*` operation which is the
composition of functions (`(f*g)(x)` is `f(g(x))`).
Several methods (eg, `image`) return lists instead of sets, this
avoids the restriction of having only hashable values in a
substitution image.
"""
def __init__ (self, *largs, **dargs) :
"""Initialise using a dictionnary as a mapping.
The expected arguments are any ones acceptables for
initializing a dictionnary.
>>> Substitution()
Substitution()
>>> Substitution(x=1, y=2)
......@@ -613,7 +620,7 @@ class Substitution (object) :
__pnmltag__ = "substitution"
def __pnmldump__ (self) :
"""Dumps a substitution to a PNML tree
>>> Substitution(x=1, y=2).__pnmldump__()
<?xml version="1.0" encoding="utf-8"?>
<pnml>
......@@ -640,9 +647,9 @@ class Substitution (object) :
</item>
</substitution>
</pnml>
@return: PNML representation
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
nodes = []
for name, value in self._dict.items() :
......@@ -654,15 +661,15 @@ class Substitution (object) :
@classmethod
def __pnmlload__ (cls, tree) :
"""Load a substitution from its PNML representation
>>> t = Substitution(x=1, y=2).__pnmldump__()
>>> Substitution.__pnmlload__(t)
Substitution(...)
@param tree: the PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: the substitution loaded
@rtype: C{Substitution}
@rtype: `Substitution`
"""
result = cls()
for item in tree :
......@@ -672,134 +679,134 @@ class Substitution (object) :
return result
def items (self) :
"""Return the list of pairs (name, value).
>>> Substitution(x=1, y=2).items()
[('...', ...), ('...', ...)]
@return: a list of pairs (name, value) for each mapped name
@rtype: C{list}
@rtype: `list`
"""
return list(self._dict.items())
def domain (self) :
"""Return the set of mapped names.
>>> list(sorted(Substitution(x=1, y=2).domain()))
['x', 'y']
@return: the set of mapped names
@rtype: C{set}
@rtype: `set`
"""
return set(self._dict.keys())
def image (self) :
"""Return the set of values associated to the names.
>>> list(sorted(Substitution(x=1, y=2).image()))
[1, 2]
@return: the set of values associated to names
@rtype: C{set}
@rtype: `set`
"""
return set(self._dict.values())
def __contains__ (self, name) :
"""Test if a name is mapped.
>>> 'x' in Substitution(x=1, y=2)
True
>>> 'z' in Substitution(x=1, y=2)
False
@param name: the name to test
@type name: C{str} (usually)
@type name: `str` (usually)
@return: a Boolean indicating whether this name is in the
domain or not
@rtype: C{bool}
domain or not
@rtype: `bool`
"""
return name in self._dict
def __iter__ (self) :
"""Iterate over the mapped names.
>>> list(sorted(iter(Substitution(x=1, y=2))))
['x', 'y']
@return: an iterator over the domain of the substitution
@rtype: C{generator}
@rtype: `generator`
"""
return iter(self._dict)
def __str__ (self) :
"""Return a compact string representation.
>>> str(Substitution(x=1, y=2))
'{... -> ..., ... -> ...}'
@return: a simple string representation
@rtype: C{str}
@rtype: `str`
"""
return "{%s}" % ", ".join(["%s -> %r" % (str(var), val)
for var, val in self.items()])
def __repr__ (self) :
"""Return a string representation suitable for C{eval}.
"""Return a string representation suitable for `eval`.
>>> repr(Substitution(x=1, y=2))
'Substitution(...)'
@return: a precise string representation
@rtype: C{str}
@rtype: `str`
"""
return "%s(%s)" % (self.__class__.__name__,
", ".join(("%s=%s" % (str(var), repr(val))
for var, val in self.items())))
def dict (self) :
"""Return the mapping as a dictionnary.
>>> Substitution(x=1, y=2).dict()
{'...': ..., '...': ...}
@return: a dictionnary that does the same mapping as the
substitution
@rtype: C{dict}
substitution
@rtype: `dict`
"""
return self._dict.copy()
def copy (self) :
"""Copy the mapping.
>>> Substitution(x=1, y=2).copy()
Substitution(...)
@return: a copy of the substitution
@rtype: C{Substitution}
@rtype: `Substitution`
"""
return Substitution(self.dict())
def __setitem__ (self, var, value) :
"""Assign an entry to the substitution
>>> s = Substitution()
>>> s['x'] = 42
>>> s
Substitution(x=42)
@param var: the name of the variable
@type var: C{str}
@param value: the value to which C{var} is bound
@type value: C{object}
@type var: `str`
@param value: the value to which `var` is bound
@type value: `object`
"""
self._dict[var] = value
def __getitem__ (self, var) :
"""Return the mapped value.
Fails with C{DomainError} if C{var} is not mapped.
Fails with `DomainError` if `var` is not mapped.
>>> s = Substitution(x=1, y=2)
>>> s['x']
1
>>> try : s['z']
... except DomainError : print(sys.exc_info()[1])
unbound variable 'z'
@param var: the name of the variable
@type var: C{str} (usually)
@return: the value that C{var} maps to
@rtype: C{object}
@raise DomainError: if C{var} does not belong to the domain
@type var: `str` (usually)
@return: the value that `var` maps to
@rtype: `object`
@raise DomainError: if `var` does not belong to the domain
"""
try :
return self._dict[var]
......@@ -807,20 +814,20 @@ class Substitution (object) :
raise DomainError("unbound variable '%s'" % var)
def __call__ (self, var) :
"""Return the mapped value.
Never fails but return C{var} if it is not mapped.
Never fails but return `var` if it is not mapped.
>>> s = Substitution(x=1, y=2)
>>> s('x')
1
>>> s('z')
'z'
@param var: the name of the variable
@type var: C{str} (usually)
@return: the value that C{var} maps to or C{var} itself if it
does not belong to the domain
@rtype: C{object}
@type var: `str` (usually)
@return: the value that `var` maps to or `var` itself if it
does not belong to the domain
@rtype: `object`
"""
try :
return self._dict[var]
......@@ -828,21 +835,21 @@ class Substitution (object) :
return var
def __add__ (self, other) :
"""Add two substitution.
Fails with C{DomainError} if the two substitutions map a same
Fails with `DomainError` if the two substitutions map a same
name to different values.
>>> s = Substitution(x=1, y=2) + Substitution(y=2, z=3)
>>> s('x'), s('y'), s('z')
(1, 2, 3)
>>> try : Substitution(x=1, y=2) + Substitution(y=4, z=3)
... except DomainError : print(sys.exc_info()[1])
conflict on 'y'
@param other: another substitution
@type other: C{Substitution}
@type other: `Substitution`
@return: the union of the substitutions
@rtype: C{Substitution}
@rtype: `Substitution`
@raise DomainError: when one name is mapped to distinct values
"""
for var in self :
......@@ -853,19 +860,19 @@ class Substitution (object) :
return s
def __mul__ (self, other) :
"""Compose two substitutions.
The composition of f and g is such that (f*g)(x) = f(g(x)).
>>> f = Substitution(a=1, d=3, y=5)
>>> g = Substitution(b='d', c=2, e=4, y=6)
>>> h = f*g
>>> h('a'), h('b'), h('c'), h('d'), h('e'), h('y'), h('x')
(1, 3, 2, 3, 4, 6, 'x')
@param other: another substitution
@type other: C{Substitution}
@type other: `Substitution`
@return: the composition of the substitutions
@rtype: C{Substitution}
@rtype: `Substitution`
"""
res = self.copy()
for var in other :
......@@ -876,17 +883,16 @@ class Symbol (object) :
"""A symbol that may be used as a constant
"""
def __init__ (self, name, export=True) :
"""
If C{export} is C{True}, the created symbol is exported under
its name. If C{export} is C{False}, no export is made.
Finally, if C{export} is a string, it specifies the name of
the exported symbol.
"""If `export` is `True`, the created symbol is exported under
its name. If `export` is `False`, no export is made. Finally,
if `export` is a string, it specifies the name of the exported
symbol.
@param name: the name (or value of the symbol)
@type name: C{str}
@type name: `str`
@param export: the name under which the symbol is exported
@type export: C{str} or C{bool} or C{None}
@type export: `str` or `bool` or `None`
>>> Symbol('foo')
Symbol('foo')
>>> foo
......@@ -901,7 +907,6 @@ class Symbol (object) :
Traceback (most recent call last):
...
NameError: ...
"""
self.name = name
if export is True :
......
"""Hashable mutable objets.
This module proposes hashable version of the mutable containers
C{list}, C{dict} and C{set}, called respectively C{hlist}, C{hdict}
and C{hset}. After one object has been hashed, it becomes not mutable
and raises a ValueError if a method which changes the object (let call
a I{mutation} such a method) is invoqued. The object can be then
un-hashed by calling C{unhash} on it so that it becomes mutable again.
`list`, `dict` and `set`, called respectively `hlist`, `hdict` and
`hset`. After one object has been hashed, it becomes not mutable and
raises a ValueError if a method which changes the object (let call a
_mutation_ such a method) is invoqued. The object can be then un-
hashed by calling `unhash` on it so that it becomes mutable again.
Notice that this may cause troubles if the object is stored in a set
or dict which uses its hashcode to locate it. Notice also that
hashable containers cannot be hashed if they contain non hashable
......@@ -24,9 +24,9 @@ ValueError: hashed 'hlist' object is not mutable
>>> l
hlist([0, 1, 2, 3, 4, 5])
Testing if a C{hlist} is in a C{set}, C{dict}, C{hset} or C{hdict}
causes its hashing. If this is not desirable, you should either
compensate with a call to C{unhash}, or test if a copy is in the set:
Testing if a `hlist` is in a `set`, `dict`, `hset` or `hdict` causes
its hashing. If this is not desirable, you should either compensate
with a call to `unhash`, or test if a copy is in the set:
>>> s = set()
>>> s.add(list(range(4)))
......@@ -54,7 +54,7 @@ from snakes.compat import *
def unhash (obj) :
"""Make the object hashable again.
>>> l = hlist(range(3))
>>> _ = hash(l)
>>> l.append(3)
......@@ -63,9 +63,9 @@ def unhash (obj) :
ValueError: hashed 'hlist' object is not mutable
>>> unhash(l)
>>> l.append(3)
@param obj: any object
@type obj: C{object}
@type obj: `object`
"""
try :
del obj._hash
......@@ -95,7 +95,7 @@ def hashable (cls) :
__hash__.__doc__ = classdict["__hash__"].__doc__
cls.__hash__ = __hash__
def __mutable__ (self) :
"Raise C{ValueError} if the %s has been hashed."
"Raise `ValueError` if the %s has been hashed."
if self.hashed() :
raise ValueError("hashed '%s' object is not mutable" % classname)
try :
......@@ -123,7 +123,7 @@ def hashable (cls) :
class hlist (list) :
"""Hashable lists.
>>> l = hlist(range(5))
>>> l
hlist([0, 1, 2, 3, 4])
......@@ -201,7 +201,7 @@ hlist = hashable(hlist)
class hdict (dict) :
"""Hashable dictionnaries.
>>> l = hlist(range(5))
>>> d = hdict([(l, 0)])
>>> d
......@@ -272,7 +272,7 @@ hdict = hashable(hdict)
class hset (set) :
"""Hashable sets.
>>> s = hset()
>>> l = hlist(range(5))
>>> s.add(l)
......
......@@ -17,7 +17,8 @@ class memoize(object):
return call
def __get__(self, obj, objtype):
"""Support instance methods."""
"""Support instance methods.
"""
return partial(self.__call__, obj)
def component_has_cycle(node, graph, proceeding, visited):
......@@ -237,7 +238,8 @@ class CodeGen (asdl.VisitorBase) :
return "\n".join(python(code, 0))
def compile_asdl(infilename, outfilename):
""" Helper function to compile asdl files. """
"""Helper function to compile asdl files.
"""
infile = open(infilename, 'r')
outfile = open(outfilename, 'w')
......
"""Backport of Python 2.6 'ast' module.
"""Backport of Python 2.6 `ast` module.
AST classes are wrappers around classes in Python 2.5 '_ast' module.
The rest is copied from Python 2.6 'ast.py'.
AST classes are wrappers around classes in Python 2.5 `_ast` module.
The rest is copied from Python 2.6 `ast.py`.
"""
import inspect
......@@ -33,11 +33,10 @@ for name, cls in inspect.getmembers(_ast, inspect.isclass) :
del _Ast, cls, name
def literal_eval(node_or_string):
"""
Safely evaluate an expression node or a string containing a Python
expression. The string or node provided may only consist of the following
Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
and None.
"""Safely evaluate an expression node or a string containing a
Python expression. The string or node provided may only consist of
the following Python literal structures: strings, numbers, tuples,
lists, dicts, booleans, and `None`.
"""
_safe_names = {'None': None, 'True': True, 'False': False}
if isinstance(node_or_string, basestring):
......@@ -63,13 +62,13 @@ def literal_eval(node_or_string):
return _convert(node_or_string)
def dump(node, annotate_fields=True, include_attributes=False):
"""
Return a formatted dump of the tree in *node*. This is mainly useful for
debugging purposes. The returned string will show the names and the values
for fields. This makes the code impossible to evaluate, so if evaluation is
wanted *annotate_fields* must be set to False. Attributes such as line
numbers and column offsets are not dumped by default. If this is wanted,
*include_attributes* can be set to True.
"""Return a formatted dump of the tree in `node`. This is mainly
useful for debugging purposes. The returned string will show the
names and the values for fields. This makes the code impossible to
evaluate, so if evaluation is wanted `annotate_fields` must be set
to `False`. Attributes such as line numbers and column offsets are
not dumped by default. If this is wanted, `include_attributes` can
be set to `True`.
"""
def _format(node):
if isinstance(node, AST):
......@@ -92,9 +91,8 @@ def dump(node, annotate_fields=True, include_attributes=False):
return _format(node)
def copy_location(new_node, old_node):
"""
Copy source location (`lineno` and `col_offset` attributes) from
*old_node* to *new_node* if possible, and return *new_node*.
"""Copy source location (`lineno` and `col_offset` attributes)
from `old_node` to `new_node` if possible, and return `new_node`.
"""
for attr in 'lineno', 'col_offset':
if attr in old_node._attributes and attr in new_node._attributes \
......@@ -103,12 +101,12 @@ def copy_location(new_node, old_node):
return new_node
def fix_missing_locations(node):
"""
When you compile a node tree with compile(), the compiler expects lineno and
col_offset attributes for every node that supports them. This is rather
tedious to fill in for generated nodes, so this helper adds these attributes
recursively where not already set, by setting them to the values of the
parent node. It works recursively starting at *node*.
"""When you compile a node tree with `compile()`, the compiler
expects `lineno` and `col_offset` attributes for every node that
supports them. This is rather tedious to fill in for generated
nodes, so this helper adds these attributes recursively where not
already set, by setting them to the values of the parent node. It
works recursively starting at `node`.
"""
def _fix(node, lineno, col_offset):
if 'lineno' in node._attributes:
......@@ -127,10 +125,10 @@ def fix_missing_locations(node):
return node
def increment_lineno(node, n=1):
"""
Increment the line number of each node in the tree starting at *node* by *n*.
This is useful to "move code" to a different location in a file.
"""
'''Increment the line number of each node in the tree starting at
`node` by `n`. This is useful to "move code" to a different
location in a file.
'''
if 'lineno' in node._attributes:
node.lineno = getattr(node, 'lineno', 0) + n
for child in walk(node):
......@@ -139,9 +137,8 @@ def increment_lineno(node, n=1):
return node
def iter_fields(node):
"""
Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
that is present on *node*.
"""Yield a tuple of `(fieldname, value)` for each field in
`node._fields` that is present on `node`.
"""
for field in node._fields:
try:
......@@ -150,9 +147,8 @@ def iter_fields(node):
pass
def iter_child_nodes(node):
"""
Yield all direct child nodes of *node*, that is, all fields that are nodes
and all items of fields that are lists of nodes.
"""Yield all direct child nodes of `node`, that is, all fields
that are nodes and all items of fields that are lists of nodes.
"""
for name, field in iter_fields(node):
if isinstance(field, AST):
......@@ -163,10 +159,9 @@ def iter_child_nodes(node):
yield item
def get_docstring(node, clean=True):
"""
Return the docstring for the given node or None if no docstring can
be found. If the node provided does not have docstrings a TypeError
will be raised.
"""Return the docstring for the given `node` or `None` if no
docstring can be found. If the node provided does not have
docstrings a `TypeError` will be raised.
"""
if not isinstance(node, (FunctionDef, ClassDef, Module)):
raise TypeError("%r can't have docstrings" % node.__class__.__name__)
......@@ -177,10 +172,9 @@ def get_docstring(node, clean=True):
return node.body[0].value.s
def walk(node):
"""
Recursively yield all child nodes of *node*, in no specified order. This is
useful if you only want to modify nodes in place and don't care about the
context.
"""Recursively yield all child nodes of `node`, in no specified
order. This is useful if you only want to modify nodes in place
and don't care about the context.
"""
from collections import deque
todo = deque([node])
......@@ -190,23 +184,23 @@ def walk(node):
yield node
class NodeVisitor(object):
"""
A node visitor base class that walks the abstract syntax tree and calls a
visitor function for every node found. This function may return a value
which is forwarded by the `visit` method.
"""A node visitor base class that walks the abstract syntax tree
and calls a visitor function for every node found. This function
may return a value which is forwarded by the `visit` method.
This class is meant to be subclassed, with the subclass adding visitor
methods.
This class is meant to be subclassed, with the subclass adding
visitor methods.
Per default the visitor functions for the nodes are ``'visit_'`` +
class name of the node. So a `TryFinally` node visit function would
be `visit_TryFinally`. This behavior can be changed by overriding
the `visit` method. If no visitor function exists for a node
(return value `None`) the `generic_visit` visitor is used instead.
Per default the visitor functions for the nodes are `visit_` +
class name of the node. So a `TryFinally` node visit function
would be `visit_TryFinally`. This behavior can be changed by
overriding the `visit` method. If no visitor function exists for a
node (return value `None`) the `generic_visit` visitor is used
instead.
Don't use the `NodeVisitor` if you want to apply changes to nodes during
traversing. For this a special visitor exists (`NodeTransformer`) that
allows modifications.
Don't use the `NodeVisitor` if you want to apply changes to nodes
during traversing. For this a special visitor exists
(`NodeTransformer`) that allows modifications.
"""
def visit(self, node):
"""Visit a node."""
......@@ -224,39 +218,39 @@ class NodeVisitor(object):
self.visit(value)
class NodeTransformer(NodeVisitor):
"""
A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
allows modification of nodes.
"""A `NodeVisitor` subclass that walks the abstract syntax tree
and allows modification of nodes.
The `NodeTransformer` will walk the AST and use the return value of the
visitor methods to replace or remove the old node. If the return value of
the visitor method is ``None``, the node will be removed from its location,
otherwise it is replaced with the return value. The return value may be the
original node in which case no replacement takes place.
The `NodeTransformer` will walk the AST and use the return value
of the visitor methods to replace or remove the old node. If the
return value of the visitor method is `None`, the node will be
removed from its location, otherwise it is replaced with the
return value. The return value may be the original node in which
case no replacement takes place.
Here is an example transformer that rewrites all occurrences of name lookups
(``foo``) to ``data['foo']``::
Here is an example transformer that rewrites all occurrences of
name lookups (`foo`) to `data['foo']`:
class RewriteName(NodeTransformer):
class RewriteName(NodeTransformer):
def visit_Name(self, node):
return copy_location(Subscript(
value=Name(id='data', ctx=Load()),
slice=Index(value=Str(s=node.id)),
ctx=node.ctx
), node)
def visit_Name(self, node):
return copy_location(Subscript(
value=Name(id='data', ctx=Load()),
slice=Index(value=Str(s=node.id)),
ctx=node.ctx
), node)
Keep in mind that if the node you're operating on has child
nodes you must either transform the child nodes yourself or call
the `generic_visit` method for the node first.
Keep in mind that if the node you're operating on has child nodes you must
either transform the child nodes yourself or call the :meth:`generic_visit`
method for the node first.
For nodes that were part of a collection of statements (that
applies to all statement nodes), the visitor may also return a
list of nodes rather than just a single node.
For nodes that were part of a collection of statements (that applies to all
statement nodes), the visitor may also return a list of nodes rather than
just a single node.
Usually you use the transformer like this:
Usually you use the transformer like this::
node = YourTransformer().visit(node)
node = YourTransformer().visit(node)
"""
def generic_visit(self, node):
for field, old_value in iter_fields(node):
......@@ -303,8 +297,7 @@ def _ast2ast (node) :
return new
def parse(expr, filename='<unknown>', mode='exec'):
"""
Parse an expression into an AST node.
Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST).
"""Parse an expression into an AST node. Equivalent to
`compile(expr, filename, mode, PyCF_ONLY_AST)`.
"""
return _ast2ast(compile(expr, filename, mode, PyCF_ONLY_AST))
......
......@@ -41,7 +41,7 @@ class Token (str) :
non-mutable object and this __init__ could not assign a str
content. For more information see:
http://docs.python.org/reference/datamodel.html#object.__new__
http://docs.python.org/reference/datamodel.html#object.__new__
"""
kind = token[0]
text = token[1]
......@@ -196,10 +196,8 @@ class Tokenizer (object) :
- skip: a collection of tokens that the tokenizer will
automatically skip (default to [COMMENT, NL])
- additional keywords arguments allow to define new tokens,
for instance, providing
DOLLAR='$'
defines a new token called 'DOLLAR' (its kind will be
automatically computed)
for instance, providing DOLLAR='$' defines a new token
called 'DOLLAR' (its kind will be automatically computed)
An instance of Tokenizer has the following attributes:
- self.opmap: a dict mapping operators token literals to the
......
......@@ -111,7 +111,7 @@ class ASDLParser(spark.GenericParser, object):
raise ASDLSyntaxError(tok.lineno, tok)
def p_module_0(self, arg):
" module ::= Id Id version { } "
"module ::= Id Id version { }"
(module, name, version, _0, _1) = arg
if module.value != "module":
raise ASDLSyntaxError(module.lineno,
......@@ -119,7 +119,7 @@ class ASDLParser(spark.GenericParser, object):
return Module(name, None, version)
def p_module(self, arg):
" module ::= Id Id version { definitions } "
"module ::= Id Id version { definitions }"
(module, name, version, _0, definitions, _1) = arg
if module.value != "module":
raise ASDLSyntaxError(module.lineno,
......@@ -135,32 +135,32 @@ class ASDLParser(spark.GenericParser, object):
return V
def p_definition_0(self, arg):
" definitions ::= definition "
"definitions ::= definition"
(definition,) = arg
return definition
def p_definition_1(self, arg):
" definitions ::= definition definitions "
"definitions ::= definition definitions"
(definitions, definition) = arg
return definitions + definition
def p_definition(self, arg):
" definition ::= Id = type "
"definition ::= Id = type"
(id, _, type) = arg
return [Type(id, type)]
def p_type_0(self, arg):
" type ::= product "
"type ::= product"
(product,) = arg
return product
def p_type_1(self, arg):
" type ::= sum "
"type ::= sum"
(sum,) = arg
return Sum(sum)
def p_type_2(self, arg):
" type ::= sum Id ( fields ) "
"type ::= sum Id ( fields )"
(sum, id, _0, attributes, _1) = arg
if id.value != "attributes":
raise ASDLSyntaxError(id.lineno,
......@@ -170,74 +170,74 @@ class ASDLParser(spark.GenericParser, object):
return Sum(sum, attributes)
def p_product(self, arg):
" product ::= ( fields ) "
"product ::= ( fields )"
(_0, fields, _1) = arg
fields.reverse()
return Product(fields)
def p_sum_0(self, arg):
" sum ::= constructor """
"sum ::= constructor"""
(constructor,) = arg
return [constructor]
def p_sum_1(self, arg):
" sum ::= constructor | sum "
"sum ::= constructor | sum"
(constructor, _, sum) = arg
return [constructor] + sum
def p_sum_2(self, arg):
" sum ::= constructor | sum "
"sum ::= constructor | sum"
(constructor, _, sum) = arg
return [constructor] + sum
def p_constructor_0(self, arg):
" constructor ::= Id "
"constructor ::= Id"
(id,) = arg
return Constructor(id)
def p_constructor_1(self, arg):
" constructor ::= Id ( fields ) "
"constructor ::= Id ( fields )"
(id, _0, fields, _1) = arg
fields.reverse()
return Constructor(id, fields)
def p_fields_0(self, arg):
" fields ::= field "
"fields ::= field"
(field,) = arg
return [field]
def p_fields_1(self, arg):
" fields ::= field , fields "
"fields ::= field , fields"
(field, _, fields) = arg
return fields + [field]
def p_field_0(self, arg):
" field ::= Id "
"field ::= Id"
(type,) = arg
return Field(type)
def p_field_1(self, arg):
" field ::= Id Id "
"field ::= Id Id"
(type, name) = arg
return Field(type, name)
def p_field_2(self, arg):
" field ::= Id * Id "
"field ::= Id * Id"
(type, _, name) = arg
return Field(type, name, seq=1)
def p_field_3(self, arg):
" field ::= Id ? Id "
"field ::= Id ? Id"
(type, _, name) = arg
return Field(type, name, opt=1)
def p_field_4(self, arg):
" field ::= Id * "
"field ::= Id *"
(type, _) = arg
return Field(type, seq=1)
def p_field_5(self, arg):
" field ::= Id ? "
"field ::= Id ?"
(type, _) = arg
return Field(type, opt=1)
......
......@@ -1623,7 +1623,7 @@ class Translator (object) :
return self.ST.Set(lineno=st.srow, col_offset=st.scol,
elts=[self.do(st[0], ctx)])
elif st[1].text == ":" :
if st[3].text == "," :
if len(st) < 4 or st[3].text == "," :
return self.ST.Dict(lineno=st.srow, col_offset=st.scol,
keys=[self.do(child, ctx)
for child in st[::4]],
......
......@@ -17,13 +17,16 @@ def interleave(inter, f, seq):
f(x)
class Unparser:
"""Methods in this class recursively traverse an AST and
output source code for the abstract syntax; original formatting
is disregarged. """
"""Methods in this class recursively traverse an AST and output
source code for the abstract syntax; original formatting is
disregarged.
"""
def __init__(self, tree, file = sys.stdout):
"""Unparser(tree, file=sys.stdout) -> None.
Print the source for tree to file."""
Print the source for tree to file.
"""
self.f = file
self._indent = 0
self.dispatch(tree)
......@@ -31,7 +34,9 @@ class Unparser:
self.f.flush()
def fill(self, text = ""):
"Indent a piece of text, according to the current indentation level"
"""Indent a piece of text, according to the current indentation
level
"""
self.f.write("\n" + " "*self._indent + text)
def write(self, text):
......
This diff could not be displayed because it is too large.
"""A plugins system.
The first example shows how to load a plugin: we load
C{snakes.plugins.hello} and plug it into C{snakes.nets}, which results
in a new module that actually C{snakes.nets} extended by
C{snakes.plugins.hello}.
`snakes.plugins.hello` and plug it into `snakes.nets`, which results
in a new module that actually `snakes.nets` extended by
`snakes.plugins.hello`.
>>> import snakes.plugins as plugins
>>> hello_nets = plugins.load('hello', 'snakes.nets')
......@@ -14,14 +14,14 @@ Hello from N
>>> n.hello()
Hi, this is N!
The next example shows how to simulate the effect of C{import module}:
we give to C{load} a thrid argument that is the name of the created
module, from which it becomes possible to import names or C{*}.
The next example shows how to simulate the effect of `import module`:
we give to `load` a thrid argument that is the name of the created
module, from which it becomes possible to import names or `*`.
B{Warning:} this feature will not work C{load} is not called from the
module where we then do the C{from ... import ...}. This is exactly
the same when, from a module C{foo} that you load a module C{bar}: if
C{bar} loads other modules they will not be imported in C{foo}.
**Warning:** this feature will not work `load` is not called from the
module where we then do the `from ... import ...`. This is exactly the
same when, from a module `foo` that you load a module `bar`: if `bar`
loads other modules they will not be imported in `foo`.
>>> plugins.load('hello', 'snakes.nets', 'another_version')
<module ...>
......@@ -33,7 +33,7 @@ Hello from another net
>>> n.hello()
Hi, this is yet another net!
How to define a plugin is explained in the example C{hello}.
How to define a plugin is explained in the example `hello`.
"""
import imp, sys, inspect
......@@ -56,21 +56,20 @@ def update (module, objects) :
def build (name, module, *objects) :
"""Builds an extended module.
The parameter C{module} is exactly that taken by the function
C{extend} of a plugin. This list argument C{objects} holds all the
objects, constructed in C{extend}, that are extensions of objects
from C{module}. The resulting value should be returned by
C{extend}.
The parameter `module` is exactly that taken by the function
`extend` of a plugin. This list argument `objects` holds all the
objects, constructed in `extend`, that are extensions of objects
from `module`. The resulting value should be returned by `extend`.
@param name: the name of the constructed module
@type name: C{str}
@type name: `str`
@param module: the extended module
@type module: C{module}
@type module: `module`
@param objects: the sub-objects
@type objects: each is a class object
@return: the new module
@rtype: C{module}
@rtype: `module`
"""
result = imp.new_module(name)
result.__dict__.update(module.__dict__)
......@@ -85,22 +84,22 @@ def build (name, module, *objects) :
def load (plugins, base, name=None) :
"""Load plugins.
C{plugins} can be a single plugin name or module or a list of such
values. If C{name} is not C{None}, the extended module is loaded
ad C{name} in C{sys.modules} as well as in the global environment
from which C{load} was called.
@param plugins: the module that implements the plugin, or its name,
or a collection of such values
@type plugins: C{str} or C{module}, or a C{list}/C{tuple}/... of
such values
`plugins` can be a single plugin name or module or a list of such
values. If `name` is not `None`, the extended module is loaded ad
`name` in `sys.modules` as well as in the global environment from
which `load` was called.
@param plugins: the module that implements the plugin, or its
name, or a collection of such values
@type plugins: `str` or `module`, or a `list`/`tuple`/... of such
values
@param base: the module being extended or its name
@type base: C{str} or C{module}
@type base: `str` or `module`
@param name: the name of the created module
@type name: C{str}
@type name: `str`
@return: the extended module
@rtype: C{module}
@rtype: `module`
"""
if type(base) is str :
result = __import__(base, fromlist=["__name__"])
......@@ -128,7 +127,7 @@ def load (plugins, base, name=None) :
def plugin (base, depends=[], conflicts=[]) :
"""Decorator for extension functions
@param base: name of base module (usually 'snakes.nets')
@type base: str
@param depends: list of plugins on which this one depends
......@@ -166,7 +165,7 @@ def plugin (base, depends=[], conflicts=[]) :
return wrapper
def new_instance (cls, obj) :
"""Create a copy of C{obj} which is an instance of C{cls}
"""Create a copy of `obj` which is an instance of `cls`
"""
result = object.__new__(cls)
result.__dict__.update(obj.__dict__)
......
......@@ -12,7 +12,6 @@ class Cluster (object) :
... Cluster(['3', '4', '5'],
... [Cluster(['C', 'D'])])])
Cluster(...)
"""
self._nodes = set(nodes)
self._children = []
......@@ -160,7 +159,6 @@ class Cluster (object) :
[Cluster(['A'], [])]),
Cluster(['...', '...'],
[Cluster(['...', '...'], [])])])
"""
if name in self._cluster :
self._cluster[name].remove_node(name)
......
"""Draw Petri nets using PyGraphViz
- adds a method C{draw} to C{PetriNet} and C{StateGraph} that creates
a drawing of the object in a file.
* adds a method `draw` to `PetriNet` and `StateGraph` that creates
a drawing of the object in a file.
>>> import snakes.plugins
>>> snakes.plugins.load('gv', 'snakes.nets', 'nets')
......@@ -16,14 +16,11 @@
>>> n.add_output('p11', 't10', Expression('x+1'))
>>> n.add_input('p11', 't01', Variable('y'))
>>> n.add_output('p00', 't01', Expression('y-1'))
>>> for engine in ('neato', 'dot', 'circo', 'twopi', 'fdp') :
... n.draw(',test-gv-%s.png' % engine, engine=engine)
>>> s = StateGraph(n)
>>> s.build()
>>> s.draw(',test-gv-graph.png')
>>> for node in sorted(n.node(), key=str) :
... node.pos.moveto(-100, -100)
>>> n.layout()
......@@ -151,36 +148,37 @@ class Graph (Cluster) :
"snakes.plugins.pos"])
def extend (module) :
class PetriNet (module.PetriNet) :
"An extension with a method C{draw}"
"An extension with a method `draw`"
def draw (self, filename=None, engine="dot", debug=False,
graph_attr=None, cluster_attr=None,
place_attr=None, trans_attr=None, arc_attr=None) :
"""
@param filename: the name of the image file to create or
C{None} if only the computed graph is needed
@type filename: C{None} or C{str}
`None` if only the computed graph is needed
@type filename: `None` or `str`
@param engine: the layout engine to use: 'dot' (default),
'neato', 'circo', 'twopi' or 'fdp'
@type engine: C{str}
'neato', 'circo', 'twopi' or 'fdp'
@type engine: `str`
@param place_attr: a function to format places, it will be
called with the place and its attributes dict as
parameters
@type place_attr: C{function(Place,dict)->None}
called with the place and its attributes dict as
parameters
@type place_attr: `function(Place,dict)->None`
@param trans_attr: a function to format transitions, it
will be called with the transition and its attributes dict
as parameters
@type trans_attr: C{function(Transition,dict)->None}
@param arc_attr: a function to format arcs, it will be
called with the label and its attributes dict as
parameters
@type arc_attr: C{function(ArcAnnotation,dict)->None}
will be called with the transition and its attributes
dict as parameters
@type trans_attr: `function(Transition,dict)->None`
@param arc_attr: a function to format arcs, it will be
called with the label and its attributes dict as
parameters
@type arc_attr: `function(ArcAnnotation,dict)->None`
@param cluster_attr: a function to format clusters of
nodes, it will be called with the cluster and its
attributes dict as parameters
@type cluster_attr: C{function(snakes.plugins.clusters.Cluster,dict)->None}
@return: C{None} if C{filename} is not C{None}, the
computed graph otherwise
@rtype: C{None} or C{pygraphviz.AGraph}
nodes, it will be called with the cluster and its
attributes dict as parameters
@type cluster_attr:
`function(snakes.plugins.clusters.Cluster,dict)->None`
@return: `None` if `filename` is not `None`, the computed
graph otherwise
@rtype: `None` or `pygraphviz.AGraph`
"""
nodemap = dict((node.name, "node_%s" % num)
for num, node in enumerate(self.node()))
......@@ -243,31 +241,31 @@ def extend (module) :
self.node(node[n]).pos.moveto(x*xscale, y*yscale)
class StateGraph (module.StateGraph) :
"An extension with a method C{draw}"
"An extension with a method `draw`"
def draw (self, filename=None, engine="dot", debug=False,
node_attr=None, edge_attr=None, graph_attr=None) :
"""
@param filename: the name of the image file to create or
C{None} if only the computed graph is needed
@type filename: C{None} or C{str}
"""@param filename: the name of the image file to create or
`None` if only the computed graph is needed
@type filename: `None` or `str`
@param engine: the layout engine to use: 'dot' (default),
'neato', 'circo', 'twopi' or 'fdp'
@type engine: C{str}
'neato', 'circo', 'twopi' or 'fdp'
@type engine: `str`
@param node_attr: a function to format nodes, it will be
called with the state number, the C{StateGraph} object
and attributes dict as parameters
@type node_attr: C{function(int,StateGraph,dict)->None}
@param edge_attr: a function to format edges, it
will be called with the transition, its mode and
attributes dict as parameters
@type trans_attr: C{function(Transition,Substitution,dict)->None}
@param graph_attr: a function to format grapg, it
will be called with the state graphe and attributes dict
as parameters
@type graph_attr: C{function(StateGraph,dict)->None}
@return: C{None} if C{filename} is not C{None}, the
computed graph otherwise
@rtype: C{None} or C{pygraphviz.AGraph}
called with the state number, the `StateGraph` object
and attributes dict as parameters
@type node_attr: `function(int,StateGraph,dict)->None`
@param edge_attr: a function to format edges, it will be
called with the transition, its mode and attributes
dict as parameters
@type trans_attr:
`function(Transition,Substitution,dict)->None`
@param graph_attr: a function to format grapg, it will be
called with the state graphe and attributes dict as
parameters
@type graph_attr: `function(StateGraph,dict)->None`
@return: `None` if `filename` is not `None`, the computed
graph otherwise
@rtype: `None` or `pygraphviz.AGraph`
"""
attr = dict(style="invis",
splines="true")
......
"""An example plugin that allows instances class C{PetriNet} to say hello.
"""An example plugin that allows instances class `PetriNet` to say hello.
A new method C{hello} is added. The constructor is added a keyword
argument C{hello} that must be the C{str} to print when calling
C{hello}, with one C{%s} that will be replaced by the name of the net
when C{hello} is called.
A new method `hello` is added. The constructor is added a keyword
argument `hello` that must be the `str` to print when calling `hello`,
with one `%s` that will be replaced by the name of the net when
`hello` is called.
Defining a plugins need writing a module with a single function called
C{extend} that takes a single argument that is the module to be
`extend` that takes a single argument that is the module to be
extended.
Inside the function, extensions of the classes in the module are
defined as normal sub-classes.
The function C{extend} should return the extended module created by
C{snakes.plugins.build} that takes as arguments: the name of the
The function `extend` should return the extended module created by
`snakes.plugins.build` that takes as arguments: the name of the
extended module, the module taken as argument and the sub-classes
defined (expected as a list argument C{*args} in no special order).
defined (expected as a list argument `*args` in no special order).
If the plugin depends on other plugins, for instance C{foo} and
C{bar}, the function C{extend} should be decorated by
C{@depends('foo', 'bar')}.
If the plugin depends on other plugins, for instance `foo` and `bar`,
the function `extend` should be decorated by `@depends('foo', 'bar')`.
Read the source code of this module to have an example
"""
......@@ -28,34 +27,34 @@ import snakes.plugins
@snakes.plugins.plugin("snakes.nets")
def extend (module) :
"""Extends C{module}
"""Extends `module`
"""
class PetriNet (module.PetriNet) :
"""Extension of the class C{PetriNet} in C{module}
"""Extension of the class `PetriNet` in `module`
"""
def __init__ (self, name, **args) :
"""When extending an existing method, take care that you
may be working on an already extended class, so you so not
"""When extending an existing method, take care that you may
be working on an already extended class, so you so not
know how its arguments have been changed. So, always use
those from the unextended class plus C{**args}, remove
from it what your plugin needs and pass it to the method
of the extended class if you need to call it.
those from the unextended class plus `**args`, remove from
it what your plugin needs and pass it to the method of the
extended class if you need to call it.
>>> PetriNet('N').hello()
Hello from N
>>> PetriNet('N', hello='Hi! This is %s...').hello()
Hi! This is N...
@param args: plugin options
@keyword hello: the message to print, with C{%s} where the
net name should appear.
@type hello: C{str}
@keyword hello: the message to print, with `%s` where the
net name should appear.
@type hello: `str`
"""
self._hello = args.pop("hello", "Hello from %s")
module.PetriNet.__init__(self, name, **args)
def hello (self) :
"""A new method C{hello}
"""A new method `hello`
>>> n = PetriNet('N')
>>> n.hello()
Hello from N
......
"""A plugin to add labels to nodes and nets.
"""
from snakes.plugins import plugin, new_instance
......
"""A plugin to add positions to the nodes.
- C{Place} and C{Transition} constructors are added an optional
argument C{pos=(x,y)} to set their position
- C{Place} and C{Transition} are added an attribute C{pos} that is
pair of numbers with attributes C{x} and C{y} and methods
C{shift(dx, dy)} and C{moveto(x, y)}
- Petri nets are added methods C{bbox()} that returns a pair of
extrema C{((xmin, ymin), (xmax, ymax))}, a method C{shift(dx, dy)}
that shift all the nodes, and a method C{transpose()} that rotates
* `Place` and `Transition` constructors are added an optional argument
`pos=(x,y)` to set their position
* `Place` and `Transition` are added an attribute `pos` that is pair
of numbers with attributes `x` and `y` and methods `shift(dx, dy)`
and `moveto(x, y)`
* Petri nets are added methods `bbox()` that returns a pair of
extrema `((xmin, ymin), (xmax, ymax))`, a method `shift(dx, dy)`
that shift all the nodes, and a method `transpose()` that rotates
the net in such a way that the top-down direction becomes
left-right
......
"""A plugin to add nodes status.
Several status are defined by default: C{entry}, C{internal}, C{exit},
C{buffer}, C{safebuffer} for places and {tick} for transitions.
Several status are defined by default: `entry`, `internal`, `exit`,
`buffer`, `safebuffer` for places and `tick` for transitions.
>>> import snakes.plugins
>>> snakes.plugins.load('status', 'snakes.nets', 'nets')
......@@ -28,7 +28,7 @@ class Status (object) :
"""Initialize with a status name and an optional value
@param name: the name of the status
@type name: C{str}
@type name: `str`
@param value: an optional additional value to make a
difference between status with te same name
@type value: hashable
......@@ -37,10 +37,10 @@ class Status (object) :
self._value = value
__pnmltag__ = "status"
def __pnmldump__ (self) :
"""Dump a C{Status} as a PNML tree
"""Dump a `Status` as a PNML tree
@return: PNML tree
@rtype: C{pnml.Tree}
@rtype: `pnml.Tree`
>>> Status('foo', 42).__pnmldump__()
<?xml version="1.0" encoding="utf-8"?>
......@@ -62,16 +62,16 @@ class Status (object) :
Tree("value", None, Tree.from_obj(self._value)))
@classmethod
def __pnmlload__ (cls, tree) :
"""Create a C{Status} from a PNML tree
@param tree: the tree to convert
@type tree: C{pnml.Tree}
@return: the status built
@rtype: C{Status}
"""Create a `Status` from a PNML tree
>>> t = Status('foo', 42).__pnmldump__()
>>> Status.__pnmlload__(t)
Status('foo',42)
@param tree: the tree to convert
@type tree: `pnml.Tree`
@return: the status built
@rtype: `Status`
"""
return cls(tree.child("name").data,
tree.child("value").child().to_obj())
......@@ -82,7 +82,7 @@ class Status (object) :
unless the user decides to store additional data in status.
@return: a copy of the status
@rtype: C{Status}
@rtype: `Status`
"""
return self.__class__(self._name, self._value)
def __str__ (self) :
......@@ -94,7 +94,7 @@ class Status (object) :
'buffer(buf)'
@return: a textual representation
@rtype: C{str}
@rtype: `str`
"""
if self._value is None :
return str(self._name)
......@@ -108,8 +108,8 @@ class Status (object) :
>>> repr(buffer('buf'))
"Buffer('buffer','buf')"
@return: a textual representation suitable for C{eval}
@rtype: C{str}
@return: a textual representation suitable for `eval`
@rtype: `str`
"""
if self._value is None :
return "%s(%s)" % (self.__class__.__name__, repr(self._name))
......@@ -120,7 +120,7 @@ class Status (object) :
"""Hash a status
@return: the hash value
@rtype: C{int}
@rtype: `int`
"""
return hash((self._name, self._value))
def __eq__ (self, other) :
......@@ -136,9 +136,9 @@ class Status (object) :
False
@param other: a status
@type other: C{Status}
@return: C{True} is they are equal, C{False} otherwise
@rtype: C{bool}
@type other: `Status`
@return: `True` is they are equal, `False` otherwise
@rtype: `bool`
"""
try :
return (self._name, self._value) == (other._name, other._value)
......@@ -156,19 +156,19 @@ class Status (object) :
def value (self) :
return self._value
def merge (self, net, nodes, name=None) :
"""Merge C{nodes} in C{net} into a new node called C{name}
"""Merge `nodes` in `net` into a new node called `name`
This does nothing by default, other status will refine this
method. Merged nodes are removed, only the newly created one
remains.
@param net: the Petri net where nodes should be merged
@type net: C{PetriNet}
@type net: `PetriNet`
@param nodes: a collection of node names to be merged
@type nodes: iterable of C{str}
@param name: the name of the new node or C{Node} if it should
be generated
@type name: C{str}
@type nodes: iterable of `str`
@param name: the name of the new node or `Node` if it should
be generated
@type name: `str`
"""
pass
......@@ -179,13 +179,13 @@ internal = Status('internal')
class Buffer (Status) :
"A status for buffer places"
def merge (self, net, nodes, name=None) :
"""Merge C{nodes} in C{net}
"""Merge `nodes` in `net`
Buffer places with the status status C{Buffer('buffer', None)}
Buffer places with the status status `Buffer('buffer', None)`
are not merged. Other buffer places are merged exactly has
C{PetriNet.merge_places} does.
`PetriNet.merge_places` does.
If C{name} is C{None} the name generated is a concatenation of
If `name` is `None` the name generated is a concatenation of
the nodes names separated by '+', with parenthesis outside.
>>> import snakes.plugins
......@@ -205,12 +205,12 @@ class Buffer (Status) :
True
@param net: the Petri net where places should be merged
@type net: C{PetriNet}
@type net: `PetriNet`
@param nodes: a collection of place names to be merged
@type nodes: iterable of C{str}
@param name: the name of the new place or C{Node} if it should
be generated
@type name: C{str}
@type nodes: iterable of `str`
@param name: the name of the new place or `Node` if it should
be generated
@type name: `str`
"""
if self._value is None :
return
......@@ -221,26 +221,26 @@ class Buffer (Status) :
net.remove_place(src)
def buffer (name) :
"""Generate a buffer status called C{name}
"""Generate a buffer status called `name`
@param name: the name of the buffer
@type name: C{str}
@return: C{Buffer('buffer', name)}
@rtype: C{Buffer}
@type name: `str`
@return: `Buffer('buffer', name)`
@rtype: `Buffer`
"""
return Buffer('buffer', name)
class Safebuffer (Buffer) :
"A status for safe buffers (ie, variables) places"
def merge (self, net, nodes, name=None) :
"""Merge C{nodes} in C{net}
"""Merge `nodes` in `net`
Safe buffers places with the status C{Safebuffer('safebuffer',
None)} are not merged. Other safe buffers places are merged if
Safe buffers places with the status `Safebuffer('safebuffer',
None)` are not merged. Other safe buffers places are merged if
they all have the same marking, which becomes the marking of
the resulting place. Otherwise, C{ConstraintError} is raised.
the resulting place. Otherwise, `ConstraintError` is raised.
If C{name} is C{None} the name generated is a concatenation of
If `name` is `None` the name generated is a concatenation of
the nodes names separated by '+', with parenthesis outside.
>>> import snakes.plugins
......@@ -262,12 +262,12 @@ class Safebuffer (Buffer) :
incompatible markings
@param net: the Petri net where places should be merged
@type net: C{PetriNet}
@type net: `PetriNet`
@param nodes: a collection of place names to be merged
@type nodes: iterable of C{str}
@param name: the name of the new place or C{Node} if it should
@type nodes: iterable of `str`
@param name: the name of the new place or `Node` if it should
be generated
@type name: C{str}
@type name: `str`
"""
if self._value is None :
return
......@@ -284,24 +284,24 @@ class Safebuffer (Buffer) :
net.place(name).reset(marking)
def safebuffer (name) :
"""Generate a safebuffer status called C{name}
"""Generate a safebuffer status called `name`
@param name: the name of the safebuffer
@type name: C{str}
@return: C{Safebuffer('safebuffer', name)}
@rtype: C{Safebuffer}
@type name: `str`
@return: `Safebuffer('safebuffer', name)`
@rtype: `Safebuffer`
"""
return Safebuffer('safebuffer', name)
class Tick (Status) :
"A status for tick transition"
def merge (self, net, nodes, name=None) :
"""Merge C{nodes} in C{net}
"""Merge `nodes` in `net`
Tick transitions are merged exactly as
C{PetriNet.merge_transitions} does.
`PetriNet.merge_transitions` does.
If C{name} is C{None} the name generated is a concatenation of
If `name` is `None` the name generated is a concatenation of
the nodes names separated by '+', with parenthesis outside.
>>> import snakes.plugins
......@@ -319,12 +319,12 @@ class Tick (Status) :
Transition('t', Expression('((...) and (...)) and (...)'), status=Tick('tick','tick'))
@param net: the Petri net where transitions should be merged
@type net: C{PetriNet}
@type net: `PetriNet`
@param nodes: a collection of transition names to be merged
@type nodes: iterable of C{str}
@param name: the name of the new transition or C{Node} if it
@type nodes: iterable of `str`
@param name: the name of the new transition or `Node` if it
should be generated
@type name: C{str}
@type name: `str`
"""
if self._value is None :
return
......@@ -335,12 +335,12 @@ class Tick (Status) :
net.remove_transition(src)
def tick (name) :
"""Generate a tick status called C{name}
"""Generate a tick status called `name`
@param name: the name of the tick
@type name: C{str}
@return: C{Tick('tick', name)}
@rtype: C{Tick}
@type name: `str`
@return: `Tick('tick', name)`
@rtype: `Tick`
"""
return Tick('tick', name)
......@@ -349,15 +349,15 @@ class StatusDict (object) :
def __init__ (self, net) :
"""
@param net: the Petri net for which nodes will be recorded
@type net: C{PetriNet}
@type net: `PetriNet`
"""
self._nodes = {}
self._net = weakref.ref(net)
def copy (self, net=None) :
"""
@param net: the Petri net for which nodes will be recorded
(C{None} if it is the same as the copied object)
@type net: C{PetriNet}
(`None` if it is the same as the copied object)
@type net: `PetriNet`
"""
if net is None :
net = self._net()
......@@ -368,38 +368,38 @@ class StatusDict (object) :
def __iter__ (self) :
return iter(self._nodes)
def record (self, node) :
"""Called when C{node} is added to the net
"""Called when `node` is added to the net
@param node: the added node
@type node: C{Node}
@type node: `Node`
"""
if node.status not in self._nodes :
self._nodes[node.status] = set([node.name])
else :
self._nodes[node.status].add(node.name)
def remove (self, node) :
"""Called when C{node} is removed from the net
"""Called when `node` is removed from the net
@param node: the added node
@type node: C{Node}
@type node: `Node`
"""
if node.status in self._nodes :
self._nodes[node.status].discard(node.name)
if len(self._nodes[node.status]) == 0 :
del self._nodes[node.status]
def __call__ (self, status) :
"""Return the nodes having C{status}
"""Return the nodes having `status`
@param status: the searched status
@type status: C{Status}
@type status: `Status`
@return: the node names in the net having this status
@rtype: C{tuple} of C{str}
@rtype: `tuple` of `str`
"""
return tuple(self._nodes.get(status, tuple()))
def merge (self, status, name=None) :
"""Merge the nodes in the net having C{status}
"""Merge the nodes in the net having `status`
This is a shortcut to call C{status.merge} with the right
This is a shortcut to call `status.merge` with the right
parameters.
@param status: the status for which nodes have to be merged
......
......@@ -6,25 +6,24 @@ The plugin proposes a generalisation of the M-nets synchronisation in
that it does not impose a fixed correspondence between action names
and action arities.
- class C{Action} corresponds to a synchronisable action, it has a
name, a send/receive flag and a list of parameters. Actions have no
predetermined arities, only conjugated actions with the same arity
will be able to synchronise.
- class C{MultiAction} corresponds to a multiset of actions. It is
forbidden to build a multiaction that holds a pair of conjugated
actions (this leads to infinite nets when synchronising).
- Transition.__init__ accepts a parameter C{actions} that is a
collection of instances of C{Action}, this multiaction is added in
the attribute C{actions} of the transition.
- PetriNet is given new methods: C{synchronise(action_name)} to
perform the M-net synchronisation, C{restrict(action_name)} to
perform the restriction and C{scope(action_name)} for the scoping.
B{Remark:} the instances of C{Substitution} used in this plugins must
map variable names to instances of C{Variable} or C{Value}, but not to
* class `Action` corresponds to a synchronisable action, it has a
name, a send/receive flag and a list of parameters. Actions have
no predetermined arities, only conjugated actions with the same
arity will be able to synchronise
* class `MultiAction` corresponds to a multiset of actions. It is
forbidden to build a multiaction that holds a pair of conjugated
actions (this leads to infinite nets when synchronising)
* Transition.__init__ accepts a parameter `actions` that is a
collection of instances of `Action`, this multiaction is added in
the attribute `actions` of the transition
* PetriNet is given new methods: `synchronise(action_name)` to
perform the M-net synchronisation, `restrict(action_name)` to
perform the restriction and `scope(action_name)` for the scoping
**Remark:** the instances of `Substitution` used in this plugins must
map variable names to instances of `Variable` or `Value`, but not to
other variable names.
>>> import snakes.plugins
......@@ -81,12 +80,12 @@ class Action (object) :
def __init__ (self, name, send, params) :
"""
@param name: the name of the action
@type name: C{str}
@type name: `str`
@param send: a flag indicating whether this is a send or
receive action
@type send: C{bool}
receive action
@type send: `bool`
@param params: the list of parameters
@type params: C{list} of C{Variable} or C{Value}
@type params: `list` of `Variable` or `Value`
"""
self.name = name
self.send = send
......@@ -150,19 +149,18 @@ class Action (object) :
str(self.send),
", ".join([repr(p) for p in self]))
def __len__ (self) :
"""Return the number of parameters, aka the arity of the
action.
"""Return the number of parameters, aka the arity of the action.
>>> len(Action('a', True, [Value(1), Variable('x')]))
2
@return: the arity of the action
@rtype: non negative C{int}
@rtype: non negative `int`
"""
return len(self.params)
def __iter__ (self) :
"""Iterate on the parameters
>>> list(Action('a', True, [Value(1), Variable('x')]))
[Value(1), Variable('x')]
"""
......@@ -171,7 +169,7 @@ class Action (object) :
def __eq__ (self, other) :
"""Two actions are equal if they have the same name, same send
flags and same parameters.
>>> Action('a', True, [Value(1), Variable('x')]) == Action('a', True, [Value(1), Variable('x')])
True
>>> Action('a', True, [Value(1), Variable('x')]) == Action('b', True, [Value(1), Variable('x')])
......@@ -182,12 +180,12 @@ class Action (object) :
False
>>> Action('a', True, [Value(1), Variable('x')]) == Action('a', True, [Value(1)])
False
@param other: the action to compare
@type other: C{Action}
@return: C{True} if the two actions are equal, C{False}
otherwise
@rtype: C{bool}
@type other: `Action`
@return: `True` if the two actions are equal, `False`
otherwise
@rtype: `bool`
"""
if self.name != other.name :
return False
......@@ -203,21 +201,21 @@ class Action (object) :
return not (self == other)
def copy (self, subst=None) :
"""Copy the action, optionally substituting its parameters.
>>> a = Action('a', True, [Variable('x'), Value(2)])
>>> a.copy()
Action('a', True, [Variable('x'), Value(2)])
>>> a = Action('a', True, [Variable('x'), Value(2)])
>>> a.copy(Substitution(x=Value(3)))
Action('a', True, [Value(3), Value(2)])
@param subst: if not C{None}, a substitution to apply to the
parameters of the copy
@type subst: C{None} or C{Substitution} mapping variables
names to C{Value} or C{Variable}
@return: a copy of the action, substituted by C{subst} if not
C{None}
@rtype: C{Action}
@param subst: if not `None`, a substitution to apply to the
parameters of the copy
@type subst: `None` or `Substitution` mapping variables names
to `Value` or `Variable`
@return: a copy of the action, substituted by `subst` if not
`None`
@rtype: `Action`
"""
result = self.__class__(self.name, self.send,
[p.copy() for p in self.params])
......@@ -225,16 +223,16 @@ class Action (object) :
result.substitute(subst)
return result
def substitute (self, subst) :
"""Substitute the parameters according to C{subst}
"""Substitute the parameters according to `subst`
>>> a = Action('a', True, [Variable('x'), Value(2)])
>>> a.substitute(Substitution(x=Value(3)))
>>> a
Action('a', True, [Value(3), Value(2)])
@param subst: a substitution to apply to the parameters
@type subst: C{Substitution} mapping variables names to
C{Value} or C{Variable}
@type subst: `Substitution` mapping variables names to `Value`
or `Variable`
"""
for i, p in enumerate(self.params) :
if isinstance(p, Variable) and p.name in subst :
......@@ -243,21 +241,21 @@ class Action (object) :
"""
>>> Action('a', True, [Value(3), Variable('x'), Variable('y'), Variable('x')]).vars() == set(['x', 'y'])
True
@return: the set of variable names appearing in the parameters
of the action
@rtype: C{set} of C{str}
of the action
@rtype: `set` of `str`
"""
return set(p.name for p in self.params if isinstance(p, Variable))
def __and__ (self, other) :
"""Compute an unification of two conjugated actions.
An unification is a C{Substitution} that maps variable names
to C{Variable} or C{Values}. If both actions are substituted
by this unification, their parameters lists become equal. If
no unification can be found, C{ConstraintError} is raised (or,
rarely, C{DomainError} depending on the cause of the failure).
An unification is a `Substitution` that maps variable names to
`Variable` or `Values`. If both actions are substituted by
this unification, their parameters lists become equal. If no
unification can be found, `ConstraintError` is raised (or,
rarely, `DomainError` depending on the cause of the failure).
>>> s = Action('a', True, [Value(3), Variable('x'), Variable('y'), Variable('x')])
>>> r = Action('a', False, [Value(3), Value(2), Variable('t'), Variable('z')])
>>> u = s & r
......@@ -269,7 +267,6 @@ class Action (object) :
True
>>> s.params
[Value(3), Value(2), Variable('t'), Value(2)]
>>> s = Action('a', True, [Value(2), Variable('x'), Variable('y'), Variable('x')])
>>> r = Action('a', False, [Value(3), Value(2), Variable('t'), Variable('z')])
>>> try : s & r
......@@ -287,11 +284,11 @@ class Action (object) :
>>> try : s & r
... except ConstraintError : print(sys.exc_info()[1])
actions not conjugated
@param other: the other action to unify with
@type other: C{Action}
@type other: `Action`
@return: a substitution that unify both actions
@rtype: C{Substitution}
@rtype: `Substitution`
"""
if (self.name != other.name) or (self.send == other.send) :
raise ConstraintError("actions not conjugated")
......@@ -326,10 +323,10 @@ class MultiAction (object) :
... Action('a', False, [Value(2)])])
... except ConstraintError : print(sys.exc_info()[1])
conjugated actions in the same multiaction
@param actions: a collection of actions with no conjugated
actions in it
@type actions: C{list} of C{Action}
actions in it
@type actions: `list` of `Action`
"""
self._actions = []
self._sndrcv = {}
......@@ -393,12 +390,12 @@ class MultiAction (object) :
"""
return "[%s]" % ", ".join(str(act) for act in self._actions)
def send (self, name) :
"""Returns the send flag of the action C{name} in this
"""Returns the send flag of the action `name` in this
multiaction.
This value is unique as conjugated actions are forbidden in
the same multiaction.
>>> m = MultiAction([Action('a', True, [Variable('x')]),
... Action('b', False, [Variable('y'), Value(2)])])
>>> m.send('a'), m.send('b')
......@@ -407,12 +404,12 @@ class MultiAction (object) :
return self._sndrcv[name]
def add (self, action) :
"""Add an action to the multiaction.
This may raise C{ConstraintError} if the added action is
This may raise `ConstraintError` if the added action is
conjugated to one that already belongs to the multiaction.
@param action: the action to add
@type action: C{Action}
@type action: `Action`
"""
if self._sndrcv.get(action.name, action.send) != action.send :
raise ConstraintError("conjugated actions in the same multiaction")
......@@ -421,12 +418,12 @@ class MultiAction (object) :
self._actions.append(action)
def remove (self, action) :
"""Remove an action from the multiaction.
This may raise C{ValueError} if the removed action does
belongs to the multiaction.
This may raise `ValueError` if the removed action does belongs
to the multiaction.
@param action: the action to remove
@type action: C{Action}
@type action: `Action`
"""
self._actions.remove(action)
self._count[action.name] -= 1
......@@ -435,7 +432,7 @@ class MultiAction (object) :
del self._sndrcv[action.name]
def __iter__ (self) :
"""Iterate over the actions in the multiaction.
>>> list(MultiAction([Action('a', True, [Variable('x')]),
... Action('b', False, [Variable('y'), Value(2)])]))
[Action('a', True, [Variable('x')]),
......@@ -445,18 +442,18 @@ class MultiAction (object) :
yield action
def __len__ (self) :
"""Return the number of actions in a multiaction.
>>> len(MultiAction([Action('a', True, [Variable('x')]),
... Action('b', False, [Variable('y'), Value(2)])]))
2
@return: the number of contained actions
@rtype: non negative C{int}
@rtype: non negative `int`
"""
return len(self._actions)
def substitute (self, subst) :
"""Substitute bu C{subt} all the actions in the multiaction.
"""Substitute bu `subt` all the actions in the multiaction.
>>> m = MultiAction([Action('a', True, [Variable('x')]),
... Action('b', False, [Variable('y'), Variable('x')])])
>>> m.substitute(Substitution(x=Value(4)))
......@@ -467,14 +464,14 @@ class MultiAction (object) :
for action in self._actions :
action.substitute(subst)
def copy (self, subst=None) :
""" Copy the multiaction (and the actions is contains)
optionally substituting it.
@param subst: if not C{None}, the substitution to apply to the
copy.
@type subst: C{None} or C{Substitution}
"""Copy the multiaction (and the actions is contains) optionally
substituting it.
@param subst: if not `None`, the substitution to apply to the
copy.
@type subst: `None` or `Substitution`
@return: a copy of the multiaction, optionally substituted
@rtype: C{MultiAction}
@rtype: `MultiAction`
"""
result = self.__class__(act.copy() for act in self._actions)
if subst is not None :
......@@ -482,10 +479,10 @@ class MultiAction (object) :
return result
def __contains__ (self, action) :
"""Search an action in the multiaction.
The searched action may be a complete C{Action}, just an
action name, or a pair C{(name, send_flag)}.
The searched action may be a complete `Action`, just an action
name, or a pair `(name, send_flag)`.
>>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
... Action('a', True, [Value(3), Variable('y')]),
... Action('b', False, [Variable('x'), Variable('y')])])
......@@ -501,13 +498,13 @@ class MultiAction (object) :
False
>>> Action('c', True, [Variable('x'), Value(2)]) in m
False
@param action: an complete action, or its name or its name and
send flag
@type action: C{Action} or C{str} or C{tuple(str, bool)}
@return: C{True} if the specified action was found, C{False}
otherwise
@rtype: C{bool}
send flag
@type action: `Action` or `str` or `tuple(str, bool)`
@return: `True` if the specified action was found, `False`
otherwise
@rtype: `bool`
"""
if isinstance(action, Action) :
return action in self._actions
......@@ -520,7 +517,7 @@ class MultiAction (object) :
raise ValueError("invalid action specification")
def __add__ (self, other) :
"""Create a multiaction by adding the actions of two others.
>>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
... Action('a', True, [Value(3), Variable('y')]),
... Action('b', False, [Variable('x'), Variable('y')])])
......@@ -536,12 +533,12 @@ class MultiAction (object) :
Action('a', True, [Value(3), Variable('y')]),
Action('b', False, [Variable('x'), Variable('y')]),
Action('c', True, [])])
@param other: the other multiaction to combine or a single
action
@type other: C{MultiAction} or C{Action}
action
@type other: `MultiAction` or `Action`
@return: the concatenated multiaction
@rtype: C{MultiAction}
@rtype: `MultiAction`
"""
if isinstance(other, Action) :
other = self.__class__([other])
......@@ -550,8 +547,9 @@ class MultiAction (object) :
result.add(action)
return result
def __sub__ (self, other) :
"""Create a multiaction by substracting the actions of two others.
"""Create a multiaction by substracting the actions of two
others.
>>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
... Action('a', True, [Value(3), Variable('y')]),
... Action('b', False, [Variable('x'), Variable('y')])])
......@@ -560,12 +558,12 @@ class MultiAction (object) :
>>> m - Action('b', False, [Variable('x'), Variable('y')])
MultiAction([Action('a', True, [Variable('x'), Value(2)]),
Action('a', True, [Value(3), Variable('y')])])
@param other: the other multiaction to combine or a single
action
@type other: C{MultiAction} or C{Action}
action
@type other: `MultiAction` or `Action`
@return: the resulting multiaction
@rtype: C{MultiAction}
@rtype: `MultiAction`
"""
if isinstance(other, Action) :
other = self.__class__([other])
......@@ -576,39 +574,38 @@ class MultiAction (object) :
def vars (self) :
"""Return the set of variable names used in all the actions of
the multiaction.
>>> MultiAction([Action('a', True, [Variable('x'), Value(2)]),
... Action('a', True, [Value(3), Variable('y')]),
... Action('b', False, [Variable('x'), Variable('z')])]).vars() == set(['x', 'y', 'z'])
True
@return: the set of variable names
@rtype: C{set} of C{str}
@rtype: `set` of `str`
"""
result = set()
for action in self._actions :
result.update(action.vars())
return result
def synchronise (self, other, name) :
"""Search all the possible synchronisation on an action name
with another multiaction.
"""Search all the possible synchronisation on an action name with
another multiaction.
This method returns an iterator that yields for each possible
synchronisation a 4-tuple whose components are:
1. The sending action that did synchronise, it is already
unified, so the corresponding receiving action is just the
same with the reversed send flag.
2. The multiaction resulting from the synchronisation that is
also unified.
3. The substitution that must be applied to the transition
that provided the sending action.
4. The substitution that must be applied to the transition
that provided the receiving action.
* the sending action that did synchronise, it is already
unified, so the corresponding receiving action is just
the same with the reversed send flag
* the multiaction resulting from the synchronisation that
is also unified
* the substitution that must be applied to the transition
that provided the sending action
* the substitution that must be applied to the transition
that provided the receiving action
>>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
... Action('a', True, [Value(3), Variable('y')]),
... Action('b', False, [Variable('x'), Variable('y')])])
......@@ -618,13 +615,14 @@ class MultiAction (object) :
... print('%s %s %s %s' % (str(a), str(x), list(sorted(u.items())), list(sorted(v.items()))))
a!(w,2) [a!(3,y), b?(w,y), c?(a)] [('a', Value(2)), ('x', Variable('w'))] [('a', Value(2)), ('x', Variable('w')), ('y', Variable('a'))]
a!(3,a) [a!(x,2), b?(x,a), c?(a)] [('w', Value(3)), ('y', Variable('a'))] [('w', Value(3)), ('y', Variable('a'))]
@param other: the other multiaction to synchronise with
@type other: C{MultiAction}
@type other: `MultiAction`
@param name: the name of the action to synchronise on
@type name: C{str}
@type name: `str`
@return: an iterator over the possible synchronisations
@rtype: iterator of C{tuple(Action, MultiAction, Substitution, Substitution)}
@rtype: iterator of `tuple(Action, MultiAction, Substitution,
Substitution)`
"""
renamer = Substitution()
common = self.vars() & other.vars()
......
......@@ -160,7 +160,7 @@ class _set (object) :
class Tree (object) :
"""Abstraction of a PNML tree
>>> Tree('tag', 'data', Tree('child', None), attr='attribute value')
<?xml version="1.0" encoding="utf-8"?>
<pnml>
......@@ -172,7 +172,7 @@ class Tree (object) :
"""
def __init__ (self, _name, _data, *_children, **_attributes) :
"""Initialize a PNML tree
>>> Tree('tag', 'data',
... Tree('first_child', None),
... Tree('second_child', None),
......@@ -186,18 +186,18 @@ class Tree (object) :
data
</tag>
</pnml>
Note: parameters names start with a '_' in order to allow for
using them as attributes.
@param _name: the name of the tag
@type _name: C{str}
@param _data: the text held by the tag or C{None}
@type _data: C{str} or C{None}
@type _name: `str`
@param _data: the text held by the tag or `None`
@type _data: `str` or `None`
@param _children: children nodes
@type _children: C{Tree}
@type _children: `Tree`
@param _attributes: attributes and values of the tag
@type _attributes: C{str}
@type _attributes: `str`
"""
self.name = _name
if _data is not None and _data.strip() == "" :
......@@ -225,7 +225,7 @@ class Tree (object) :
return result
def to_pnml (self) :
"""Dumps a PNML tree to an XML string
>>> print(Tree('tag', 'data', Tree('child', None), attr='value').to_pnml())
<?xml version="1.0" encoding="utf-8"?>
<pnml>
......@@ -234,9 +234,9 @@ class Tree (object) :
data
</tag>
</pnml>
@return: the XML string that represents the PNML tree
@rtype: C{str}
@rtype: `str`
"""
if self.name == "pnml" :
tree = self
......@@ -269,7 +269,7 @@ class Tree (object) :
@classmethod
def from_dom (cls, node) :
"""Load a PNML tree from an XML DOM representation
>>> src = Tree('object', '42', type='int').to_pnml()
>>> dom = xml.dom.minidom.parseString(src)
>>> Tree.from_dom(dom.documentElement)
......@@ -279,11 +279,11 @@ class Tree (object) :
42
</object>
</pnml>
@param node: the DOM node to load
@type node: C{xml.dom.minidom.Element}
@type node: `xml.dom.minidom.Element`
@return: the loaded PNML tree
@rtype: C{Tree}
@rtype: `Tree`
"""
result = cls(node.tagName, node.nodeValue)
for i in range(node.attributes.length) :
......@@ -299,7 +299,7 @@ class Tree (object) :
@classmethod
def from_pnml (cls, source, plugins=[]) :
"""Load a PNML tree from an XML string representation
>>> src = Tree('object', '42', type='int').to_pnml()
>>> Tree.from_pnml(src)
<?xml version="1.0" encoding="utf-8"?>
......@@ -308,12 +308,12 @@ class Tree (object) :
42
</object>
</pnml>
@param source: the XML string to load or an opened file that
contains it
@type source: C{str} or C{file}
contains it
@type source: `str` or `file`
@return: the loaded PNML tree
@rtype: C{Tree}
@rtype: `Tree`
"""
try :
doc = xml.dom.minidom.parse(source)
......@@ -360,7 +360,7 @@ class Tree (object) :
return result
def nodes (self) :
"""Iterate over all the nodes (top-down) in a tree
>>> t = Tree('foo', None,
... Tree('bar', None),
... Tree('egg', None,
......@@ -371,19 +371,19 @@ class Tree (object) :
<PNML tree 'bar'>
<PNML tree 'egg'>
<PNML tree 'spam'>
@return: an iterator on all the nodes in the tree, including
this one
@rtype: C{generator}
this one
@rtype: `generator`
"""
yield self
for child in self.children :
for node in child.nodes() :
yield node
def update (self, other) :
"""Incorporates children, attributes and data from another
PNML tree
"""Incorporates children, attributes and data from another PNML
tree
>>> t = Tree('foo', 'hello',
... Tree('bar', None),
... Tree('egg', None,
......@@ -411,10 +411,11 @@ class Tree (object) :
>>> try : t.update(o)
... except SnakesError : print(sys.exc_info()[1])
tag mismatch 'foo', 'oops'
@param other: the other tree to get data from
@type other: C{Tree}
@raise SnakesError: when C{other} has not the same tag as C{self}
@type other: `Tree`
@raise SnakesError: when `other` has not the same tag as
`self`
"""
if self.name != other.name :
raise SnakesError("tag mismatch '%s', '%s'" % (self.name, other.name))
......@@ -423,7 +424,7 @@ class Tree (object) :
self.add_data(other.data)
def add_child (self, child) :
"""Add a child to a PNML tree
>>> t = Tree('foo', None)
>>> t.add_child(Tree('bar', None))
>>> t
......@@ -433,14 +434,14 @@ class Tree (object) :
<bar/>
</foo>
</pnml>
@param child: the PNML tree to append
@type child: C{Tree}
@type child: `Tree`
"""
self.children.append(child)
def add_data (self, data, sep='\n') :
"""Appends data to the current node
>>> t = Tree('foo', None)
>>> t.add_data('hello')
>>> t
......@@ -468,11 +469,11 @@ class Tree (object) :
world!
</foo>
</pnml>
@param data: the data to add
@type data: C{str}
@type data: `str`
@param sep: separator to insert between pieces of data
@type sep: C{str}
@type sep: `str`
"""
try :
data = data.strip()
......@@ -485,24 +486,24 @@ class Tree (object) :
pass
def __getitem__ (self, name) :
"""Returns one attribute
>>> Tree('foo', None, x='egg', y='spam')['x']
'egg'
>>> Tree('foo', None, x='egg', y='spam')['z']
Traceback (most recent call last):
...
KeyError: 'z'
@param name: the name of the attribute
@type name: C{str}
@type name: `str`
@return: the value of the attribute
@rtype: C{str}
@rtype: `str`
@raise KeyError: if no such attribute is found
"""
return self.attributes[name]
def __setitem__ (self, name, value) :
"""Sets an attribute
>>> t = Tree('foo', None)
>>> t['egg'] = 'spam'
>>> t
......@@ -510,29 +511,29 @@ class Tree (object) :
<pnml>
<foo egg="spam"/>
</pnml>
@param name: the name of the attribute
@type name: C{str}
@type name: `str`
@param value: the value of the attribute
@type value: C{str}
@type value: `str`
"""
self.attributes[name] = value
def __iter__ (self) :
"""Iterate over children nodes
>>> [str(node) for node in Tree('foo', None,
... Tree('egg', None),
... Tree('spam', None,
... Tree('bar', None)))]
["<PNML tree 'egg'>", "<PNML tree 'spam'>"]
@return: an iterator over direct children of the node
@rtype: C{generator}
@rtype: `generator`
"""
return iter(self.children)
def has_child (self, name) :
"""Test if the tree has the given tag as a direct child
>>> t = Tree('foo', None,
... Tree('egg', None),
... Tree('spam', None,
......@@ -543,12 +544,12 @@ class Tree (object) :
False
>>> t.has_child('python')
False
@param name: tag name to search for
@type name: C{str}
@type name: `str`
@return: a Boolean value indicating wether such a child was
found or not
@rtype: C{bool}
found or not
@rtype: `bool`
"""
for child in self :
if child.name == name :
......@@ -556,7 +557,7 @@ class Tree (object) :
return False
def child (self, name=None) :
"""Return the direct child that as the given tag
>>> t = Tree('foo', None,
... Tree('egg', 'first'),
... Tree('egg', 'second'),
......@@ -581,15 +582,15 @@ class Tree (object) :
>>> try : t.child()
... except SnakesError : print(sys.exc_info()[1])
multiple children
@param name: name of the tag to search for, if C{None}, the
fisrt child is returned if it is the only child
@type name: C{str} or C{None}
@param name: name of the tag to search for, if `None`, the
fisrt child is returned if it is the only child
@type name: `str` or `None`
@return: the only child with the given name, or the only child
if no name is given
@rtype: C{Tree}
if no name is given
@rtype: `Tree`
@raise SnakesError: when no child or more than one child could
be returned
be returned
"""
result = None
for child in self :
......@@ -605,7 +606,7 @@ class Tree (object) :
return result
def get_children (self, name=None) :
"""Iterates over direct children having the given tag
>>> t = Tree('foo', None,
... Tree('egg', 'first'),
... Tree('egg', 'second'),
......@@ -619,32 +620,32 @@ class Tree (object) :
[]
>>> [str(n) for n in t.get_children('bar')]
[]
@param name: tag to search for or C{None}
@type name: C{str} or C{None}
@return: iterator over all the children if C{name} is C{None},
or over the children with tag C{name} otherwise
@rtype: C{generator}
@param name: tag to search for or `None`
@type name: `str` or `None`
@return: iterator over all the children if `name` is `None`,
or over the children with tag `name` otherwise
@rtype: `generator`
"""
for child in self :
if name is None or child.name == name :
yield child
def __str__ (self) :
"""Return a simple string representation of the node
>>> str(Tree('foo', None, Tree('child', None)))
"<PNML tree 'foo'>"
@return: simple string representation of the node
@rtype: C{str}
@rtype: `str`
"""
return "<PNML tree %r>" % self.name
def __repr__ (self) :
"""Return a detailed representation of the node.
This is actually the XML text that corresponds to the C{Tree},
as returned by C{Tree.to_pnml}.
This is actually the XML text that corresponds to the `Tree`,
as returned by `Tree.to_pnml`.
>>> print(repr(Tree('foo', None, Tree('child', None))))
<?xml version="1.0" encoding="utf-8"?>
<pnml>
......@@ -652,9 +653,9 @@ class Tree (object) :
<child/>
</foo>
</pnml>
@return: XML string representation of the node
@rtype: C{str}
@rtype: `str`
"""
return self.to_pnml()
_elementary = set(("str", "int", "float", "bool"))
......@@ -662,10 +663,10 @@ class Tree (object) :
@classmethod
def from_obj (cls, obj) :
"""Builds a PNML tree from an object.
Objects defined in SNAKES usually have a method
C{__pnmldump__} that handles the conversion, for instance:
Objects defined in SNAKES usually have a method `__pnmldump__`
that handles the conversion, for instance:
>>> import snakes.nets
>>> Tree.from_obj(snakes.nets.Place('p'))
<?xml version="1.0" encoding="utf-8"?>
......@@ -677,9 +678,9 @@ class Tree (object) :
</initialMarking>
</place>
</pnml>
Most basic Python classes are handled has readable XML:
>>> Tree.from_obj(42)
<?xml version="1.0" encoding="utf-8"?>
<pnml>
......@@ -702,10 +703,10 @@ class Tree (object) :
</object>
</object>
</pnml>
Otherwise, the object is serialised using module C{pickle},
Otherwise, the object is serialised using module `pickle`,
which allows to embed almost anything into PNML.
>>> import re
>>> Tree.from_obj(re.compile('foo|bar')) # serialized data replaced with '...'
<?xml version="1.0" encoding="utf-8"?>
......@@ -714,11 +715,11 @@ class Tree (object) :
...
</object>
</pnml>
@param obj: the object to convert to PNML
@type obj: C{object}
@type obj: `object`
@return: the corresponding PNML tree
@rtype: C{Tree}
@rtype: `Tree`
"""
try :
result = obj.__pnmldump__()
......@@ -853,19 +854,19 @@ class Tree (object) :
return pickle.loads(self.data)
def to_obj (self) :
"""Build an object from its PNML representation
This is just the reverse as C{Tree.from_obj}, objects that
have a C{__pnmldump__} method should also have a
C{__pnmlload__} class method to perform the reverse operation,
together with an attribute C{__pnmltag__}. Indeed, when a PNML
node with tag name 'foo' has to be converted to an object, a
class C{C} such that C{C.__pnmltag__ == 'foo'} is searched in
module C{snakes.nets} and C{C.__pnmlload__(tree)} is called to
rebuild the object.
This is just the reverse as `Tree.from_obj`, objects that have
a `__pnmldump__` method should also have a `__pnmlload__`
class method to perform the reverse operation, together with
an attribute `__pnmltag__`. Indeed, when a PNML node with tag
name 'foo' has to be converted to an object, a class `C` such
that `C.__pnmltag__ == 'foo'` is searched in module
`snakes.nets` and `C.__pnmlload__(tree)` is called to rebuild
the object.
Standard Python objects and pickled ones are also recognised
and correctly rebuilt.
>>> import snakes.nets
>>> Tree.from_obj(snakes.nets.Place('p')).to_obj()
Place('p', MultiSet([]), tAll)
......@@ -876,9 +877,9 @@ class Tree (object) :
>>> import re
>>> Tree.from_obj(re.compile('foo|bar')).to_obj()
<... object at ...>
@return: the Python object encoded by the PNML tree
@rtype: C{object}
@rtype: `object`
"""
if self.name == "pnml" :
if len(self.children) == 0 :
......@@ -909,7 +910,7 @@ class Tree (object) :
def dumps (obj) :
"""Dump an object to a PNML string
>>> print(dumps(42))
<?xml version="1.0" encoding="utf-8"?>
<pnml>
......@@ -917,23 +918,23 @@ def dumps (obj) :
42
</object>
</pnml>
@param obj: the object to dump
@type obj: C{object}
@type obj: `object`
@return: the PNML that represents the object
@rtype: C{str}
@rtype: `str`
"""
return Tree.from_obj(obj).to_pnml()
def loads (source, plugins=[]) :
"""Load an object from a PNML string
>>> loads(dumps(42))
42
@param source: the data to parse
@type source: C{str}
@type source: `str`
@return: the object represented by the source
@rtype: C{object}
@rtype: `object`
"""
return Tree.from_pnml(source, plugins).to_obj()
......
......@@ -25,26 +25,26 @@ True
False
The various compositions are the same as sets operations, plus the
complement: C{&}, C{|}, C{-}, C{^} and C{~} (complement).
complement: `&`, `|`, `-`, `^` and `~` (complement).
Various types are predefined (like C{tInteger}) the others can be
Various types are predefined (like `tInteger`) the others can be
constructed using the various classes in the module. Prefefined types
are:
- C{tAll}: any value is in the type
- C{tNothing}: empty type
- C{tString}: string values
- C{tList}: lists values
- C{tInteger}: integer values
- C{tNatural}: non-negative integers
- C{tPositive}: strictly positive integers
- C{tFloat}: float values
- C{tNumber}: integers or float values
- C{tDict}: Python's C{dict} values
- C{tNone}: the single value C{None}
- C{tBoolean}: C{True} or C{False}
- C{tTuple}: tuple values
- C{tPair}: tuples of length two
* `tAll`: any value is in the type
* `tNothing`: empty type
* `tString`: string values
* `tList`: lists values
* `tInteger`: integer values
* `tNatural`: non-negative integers
* `tPositive`: strictly positive integers
* `tFloat`: float values
* `tNumber`: integers or float values
* `tDict`: Python's `dict` values
* `tNone`: the single value `None`
* `tBoolean`: `True` or `False`
* `tTuple`: tuple values
* `tPair`: tuples of length two
Types with finitely many elements can be iterated:
......@@ -80,10 +80,10 @@ def _iterable (obj, *types) :
class Type (object) :
"""Base class for all types.
Implement operations C{&}, C{|}, C{-}, C{^} and C{~} to build new
Implement operations `&`, `|`, `-`, `^` and `~` to build new
types. Also implement the typechecking of several values. All the
subclasses should implement the method C{__contains__} to
typecheck a single object.
subclasses should implement the method `__contains__` to typecheck
a single object.
"""
def __init__ (self) :
"""Abstract method
......@@ -108,9 +108,9 @@ class Type (object) :
(Instance(int) & Greater(0))
@param other: the other type in the intersection
@type other: C{Type}
@type other: `Type`
@return: the intersection of both types
@rtype: C{Type}
@rtype: `Type`
"""
if other is self :
return self
......@@ -123,9 +123,9 @@ class Type (object) :
(Instance(int) | Instance(bool))
@param other: the other type in the union
@type other: C{Type}
@type other: `Type`
@return: the union of both types
@rtype: C{Type}
@rtype: `Type`
"""
if other is self :
return self
......@@ -138,9 +138,9 @@ class Type (object) :
(Instance(int) - OneOf([0, 1, 2]))
@param other: the other type in the substraction
@type other: C{Type}
@return: the type C{self} minus the type C{other}
@rtype: C{Type}
@type other: `Type`
@return: the type `self` minus the type `other`
@rtype: `Type`
"""
if other is self :
return tNothing
......@@ -153,9 +153,9 @@ class Type (object) :
(Greater(0) ^ Instance(float))
@param other: the other type in the disjoint union
@type other: C{Type}
@type other: `Type`
@return: the disjoint union of both types
@rtype: C{Type}
@rtype: `Type`
"""
if other is self :
return tNothing
......@@ -168,7 +168,7 @@ class Type (object) :
(~Instance(int))
@return: the complementary type
@rtype: C{Type}
@rtype: `Type`
"""
return _Invert(self)
def __iterable__ (self) :
......@@ -188,10 +188,10 @@ class Type (object) :
False
@param values: values that have to be checked
@type values: C{object}
@return: C{True} if all the values are in the types, C{False}
otherwise
@rtype: C{bool}
@type values: `object`
@return: `True` if all the values are in the types, `False`
otherwise
@rtype: `bool`
"""
for v in values :
if v not in self :
......@@ -201,9 +201,9 @@ class Type (object) :
_typemap = None
@classmethod
def __pnmlload__ (cls, tree) :
"""Load a C{Type} from a PNML tree
"""Load a `Type` from a PNML tree
Uses the attribute C{__pnmltype__} to know which type
Uses the attribute `__pnmltype__` to know which type
corresponds to a tag "<type domain='xxx'>"
>>> s = List(tNatural | tBoolean).__pnmldump__()
......@@ -213,9 +213,9 @@ class Type (object) :
| OneOf(True, False)))
@param tree: the PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: the loaded type
@rtype: C{Type}
@rtype: `Type`
"""
if cls._typemap is None :
cls._typemap = {}
......@@ -256,9 +256,9 @@ class _And (_BinaryType) :
"""Check wether a value is in the type.
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return (value in self._left) and (value in self._right)
def __iter__ (self) :
......@@ -280,9 +280,9 @@ class _Or (_BinaryType) :
"""Check wether a value is in the type.
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return (value in self._left) or (value in self._right)
def __iter__ (self) :
......@@ -305,9 +305,9 @@ class _Sub (_BinaryType) :
"""Check wether a value is in the type.
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return (value in self._left) and (value not in self._right)
def __iter__ (self) :
......@@ -329,9 +329,9 @@ class _Xor (_BinaryType) :
"""Check wether a value is in the type.
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
if value in self._left :
return value not in self._right
......@@ -357,9 +357,9 @@ class _Invert (Type) :
"""Check wether a value is in the type.
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return (value not in self._base)
__pnmltype__ = "complement"
......@@ -391,9 +391,9 @@ class _All (Type) :
"""Check wether a value is in the type.
@param value: the value to check
@type value: C{object}
@return: C{True}
@rtype: C{bool}
@type value: `object`
@return: `True`
@rtype: `bool`
"""
return True
def __call__ (self, *values) :
......@@ -401,7 +401,7 @@ class _All (Type) :
@param values: values that have to be checked
@type values: any objet
@return: C{True}
@return: `True`
"""
return True
__pnmltype__ = "universal"
......@@ -442,9 +442,9 @@ class _Nothing (Type) :
"""Check wether a value is in the type.
@param value: the value to check
@type value: C{object}
@return: C{False}
@rtype: C{bool}
@type value: `object`
@return: `False`
@rtype: `bool`
"""
return False
def __call__ (self, *values) :
......@@ -452,7 +452,7 @@ class _Nothing (Type) :
@param values: values that have to be checked
@type values: any objet
@return: C{False}
@return: `False`
"""
return False
def __iter__ (self) :
......@@ -481,7 +481,7 @@ class Instance (Type) :
@param _class: the class of instance
@type _class: any class
@return: initialized object
@rtype: C{Instance}
@rtype: `Instance`
"""
self._class = _class
def __contains__ (self, value) :
......@@ -493,19 +493,19 @@ class Instance (Type) :
False
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return isinstance(value, self._class)
def __repr__ (self) :
"""String representation of the type, suitable for C{eval}
"""String representation of the type, suitable for `eval`
>>> repr(Instance(str))
'Instance(str)'
@return: precise string representation
@rtype: C{str}
@rtype: `str`
"""
return "Instance(%s)" % self._class.__name__
__pnmltype__ = "instance"
......@@ -521,7 +521,7 @@ class Instance (Type) :
</pnml>
@return: the PNML representation of the type
@rtype: C{str}
@rtype: `str`
"""
return Tree(self.__pnmltag__, None,
Tree.from_obj(self._class),
......@@ -535,9 +535,9 @@ class Instance (Type) :
Instance(int)
@param tree: the PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: the loaded type
@rtype: C{Instance}
@rtype: `Instance`
"""
return cls(tree.child().to_obj())
......@@ -583,11 +583,11 @@ class TypeCheck (Type) :
TypeCheck(...truth, snakes.typing.true_values)
@param checker: a function that checks one value and returns
C{True} if it is in te type and C{False} otherwise
@type checker: C{function(value)->bool}
@param iterate: C{None} or an iterator over the values of the
type
@type iterate: C{None} or C{iterator}
`True` if it is in te type and `False` otherwise
@type checker: `function(value)->bool`
@param iterate: `None` or an iterator over the values of the
type
@type iterate: `None` or `iterator`
"""
self._check = checker
self._iterate = iterate
......@@ -626,9 +626,9 @@ class TypeCheck (Type) :
False
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return self._check(value)
def __repr__ (self) :
......@@ -690,12 +690,12 @@ class TypeCheck (Type) :
</type>
</pnml>
Note that this last example would not work as C{true_value} as
Note that this last example would not work as `true_value` as
been defined inside a docstring. In order to allow it to be
re-loaded from PNML, it should be defined at module level.
@return: the type serialized to PNML
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
Tree("checker", None, Tree.from_obj(self._check)),
......@@ -717,9 +717,9 @@ class TypeCheck (Type) :
... yield {True: 42}
@param tree: the PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: the loaded type
@rtype: C{TypeChecker}
@rtype: `TypeChecker`
"""
return cls(tree.child("checker").child().to_obj(),
tree.child("iterator").child().to_obj())
......@@ -747,9 +747,9 @@ class OneOf (Type) :
False
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return value in self._values
def __repr__ (self) :
......@@ -791,7 +791,7 @@ class OneOf (Type) :
</pnml>
@return: PNML representation of the type
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
*(Tree.from_obj(val) for val in self._values),
......@@ -804,9 +804,9 @@ class OneOf (Type) :
OneOf(1, 2, 3, 4, 5)
@param tree: PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: loaded type
@rtype: C{OneOf}
@rtype: `OneOf`
"""
return cls(*(child.to_obj() for child in tree.children))
......@@ -832,9 +832,9 @@ class Collection (Type) :
@param items: the type of the items
@type items: any type
@param min: the smallest allowed value
@type min: any value in C{items}
@type min: any value in `items`
@param max: the greatest allowed value
@type max: any value in C{items}
@type max: any value in `items`
"""
self._collection = collection
self._class = collection._class
......@@ -852,9 +852,9 @@ class Collection (Type) :
False
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
if value not in self._collection :
return False
......@@ -933,7 +933,7 @@ class Collection (Type) :
</pnml>
@return: the PNML representation of the type
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
Tree("container", None, Tree.from_obj(self._collection)),
......@@ -951,9 +951,9 @@ class Collection (Type) :
min=3, max=10)
@param tree: the PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: the loaded type
@rtype: C{Collection}
@rtype: `Collection`
"""
return cls(tree.child("container").child().to_obj(),
tree.child("items").child().to_obj(),
......@@ -961,58 +961,58 @@ class Collection (Type) :
tree.child("max").child().to_obj())
def List (items, min=None, max=None) :
"""Shorthand for instantiating C{Collection}
"""Shorthand for instantiating `Collection`
>>> List(tNumber, min=3, max=10)
Collection(Instance(list), (Instance(int) | Instance(float)), min=3, max=10)
@param items: the type of the elements in the collection
@type items: C{Type}
@type items: `Type`
@param min: the minimum number of elements in the collection
@type min: C{int} or C{None}
@type min: `int` or `None`
@param max: the maximum number of elements in the collection
@type max: C{int} or C{None}
@type max: `int` or `None`
@return: a type that checks the given constraints
@rtype: C{Collection}
@rtype: `Collection`
"""
return Collection(Instance(list), items, min, max)
def Tuple (items, min=None, max=None) :
"""Shorthand for instantiating C{Collection}
"""Shorthand for instantiating `Collection`
>>> Tuple(tNumber, min=3, max=10)
Collection(Instance(tuple), (Instance(int) | Instance(float)), min=3, max=10)
@param items: the type of the elements in the collection
@type items: C{Type}
@type items: `Type`
@param min: the minimum number of elements in the collection
@type min: C{int} or C{None}
@type min: `int` or `None`
@param max: the maximum number of elements in the collection
@type max: C{int} or C{None}
@type max: `int` or `None`
@return: a type that checks the given constraints
@rtype: C{Collection}
@rtype: `Collection`
"""
return Collection(Instance(tuple), items, min, max)
def Set (items, min=None, max=None) :
"""Shorthand for instantiating C{Collection}
"""Shorthand for instantiating `Collection`
>>> Set(tNumber, min=3, max=10)
Collection(Instance(set), (Instance(int) | Instance(float)), min=3, max=10)
@param items: the type of the elements in the collection
@type items: C{Type}
@type items: `Type`
@param min: the minimum number of elements in the collection
@type min: C{int} or C{None}
@type min: `int` or `None`
@param max: the maximum number of elements in the collection
@type max: C{int} or C{None}
@type max: `int` or `None`
@return: a type that checks the given constraints
@rtype: C{Collection}
@rtype: `Collection`
"""
return Collection(Instance(set), items, min, max)
class Mapping (Type) :
"""A type whose values are mapping (eg, C{dict})
"""A type whose values are mapping (eg, `dict`)
>>> {'Yes': True, 'No': False} in Mapping(tString, tAll)
True
......@@ -1033,7 +1033,7 @@ class Mapping (Type) :
@param items: the type for the items
@type items: any type
@param _dict: the class that mapping must be instances of
@type _dict: any C{dict} like class
@type _dict: any `dict` like class
"""
self._keys = keys
self._items = items
......@@ -1047,9 +1047,9 @@ class Mapping (Type) :
False
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
if not self._dict(value) :
return False
......@@ -1060,13 +1060,13 @@ class Mapping (Type) :
return True
return True
def __repr__ (self) :
"""Return a string representation of the type suitable for C{eval}
"""Return a string representation of the type suitable for `eval`
>>> repr(Mapping(tString, tAll))
'Mapping(Instance(str), tAll, Instance(dict))'
@return: precise string representation
@rtype: C{str}
@rtype: `str`
"""
return "Mapping(%s, %s, %s)" % (repr(self._keys),
repr(self._items),
......@@ -1097,7 +1097,7 @@ class Mapping (Type) :
</pnml>
@return: PNML representation of the type
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
Tree("keys", None, Tree.from_obj(self._keys)),
......@@ -1114,9 +1114,9 @@ class Mapping (Type) :
Mapping(Instance(str), tAll, Instance(hdict))
@param tree: PNML representation of the type
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: the loaded type
@rtype: C{Mapping}
@rtype: `Mapping`
"""
return cls(tree.child("keys").child().to_obj(),
tree.child("items").child().to_obj(),
......@@ -1134,8 +1134,8 @@ class Range (Type) :
False
"""
def __init__ (self, first, last, step=1) :
"""The values are those that the builtin C{range(first, last, step)}
would return.
"""The values are those that the builtin `range(first, last,
step)` would return.
>>> Range(1, 10)
Range(1, 10)
......@@ -1143,11 +1143,11 @@ class Range (Type) :
Range(1, 10, 2)
@param first: first element in the range
@type first: C{int}
@type first: `int`
@param last: upper bound of the range, not belonging to it
@type last: C{int}
@type last: `int`
@param step: step between elements in the range
@type step: C{int}
@type step: `int`
"""
self._first, self._last, self._step = first, last, step
def __contains__ (self, value) :
......@@ -1163,20 +1163,20 @@ class Range (Type) :
False
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return ((self._first <= value < self._last)
and ((value - self._first) % self._step == 0))
def __repr__ (self) :
"""Return a string representation of the type suitable for C{eval}
"""Return a string representation of the type suitable for `eval`
>>> repr(Range(1, 2**128, 2))
'Range(1, 340282366920938463463374607431768211456, 2)'
@return: precise string representation
@rtype: C{str}
@rtype: `str`
"""
if self._step == 1 :
return "Range(%s, %s)" % (self._first, self._last)
......@@ -1189,7 +1189,7 @@ class Range (Type) :
[1, 4, 7]
@return: an iterator over the values belonging to the range
@rtype: C{generator}
@rtype: `generator`
"""
return iter(xrange(self._first, self._last, self._step))
__pnmltype__ = "range"
......@@ -1219,7 +1219,7 @@ class Range (Type) :
</pnml>
@return: PNML representation of the type
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
Tree("first", None, Tree.from_obj(self._first)),
......@@ -1234,9 +1234,9 @@ class Range (Type) :
Range(1, 10, 2)
@param tree: PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: the loaded type
@rtype: C{Range}
@rtype: `Range`
"""
return cls(tree.child("first").child().to_obj(),
tree.child("last").child().to_obj(),
......@@ -1246,7 +1246,7 @@ class Greater (Type) :
"""A type whose values are greater than a minimum.
The minimum and the checked values can be of any type as soon as
they can be compared with C{>}.
they can be compared with `>`.
>>> 6 in Greater(3)
True
......@@ -1260,7 +1260,7 @@ class Greater (Type) :
Greater(5)
@param min: the greatest value not included in the type
@type min: any C{object} that support comparison
@type min: any `object` that support comparison
"""
self._min = min
def __contains__ (self, value) :
......@@ -1276,22 +1276,22 @@ class Greater (Type) :
False
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
try :
return value > self._min
except :
return False
def __repr__ (self) :
"""Return a string representation of the type suitable for C{eval}
"""Return a string representation of the type suitable for `eval`
>>> repr(Greater(3))
'Greater(3)'
@return: precise string representation
@rtype: C{str}
@rtype: `str`
"""
return "Greater(%s)" % repr(self._min)
__pnmltype__ = "greater"
......@@ -1309,7 +1309,7 @@ class Greater (Type) :
</pnml>
@return: PNML representation of the type
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
Tree.from_obj(self._min),
......@@ -1322,16 +1322,16 @@ class Greater (Type) :
Greater(42)
@param tree: PNML representation to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: loaded type
@rtype: C{Greater}
@rtype: `Greater`
"""
return cls(tree.child().to_obj())
class GreaterOrEqual (Type) :
"""A type whose values are greater or equal than a minimum.
See the description of C{Greater}
See the description of `Greater`
"""
def __init__ (self, min) :
"""Initialises the type
......@@ -1340,7 +1340,7 @@ class GreaterOrEqual (Type) :
GreaterOrEqual(5)
@param min: the minimal allowed value
@type min: any C{object} that support comparison
@type min: any `object` that support comparison
"""
self._min = min
def __contains__ (self, value) :
......@@ -1356,22 +1356,22 @@ class GreaterOrEqual (Type) :
False
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
try :
return value >= self._min
except :
False
def __repr__ (self) :
"""Return a strign representation of the type suitable for C{eval}
"""Return a strign representation of the type suitable for `eval`
>>> repr(GreaterOrEqual(3))
'GreaterOrEqual(3)'
@return: precise string representation
@rtype: C{str}
@rtype: `str`
"""
return "GreaterOrEqual(%s)" % repr(self._min)
__pnmltype__ = "greatereq"
......@@ -1389,7 +1389,7 @@ class GreaterOrEqual (Type) :
</pnml>
@return: PNML representation of the type
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
Tree.from_obj(self._min),
......@@ -1402,16 +1402,16 @@ class GreaterOrEqual (Type) :
GreaterOrEqual(42)
@param tree: PNML representation to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: loaded type
@rtype: C{GreaterOrEqual}
@rtype: `GreaterOrEqual`
"""
return cls(tree.child().to_obj())
class Less (Type) :
"""A type whose values are less than a maximum.
See the description of C{Greater}
See the description of `Greater`
"""
def __init__ (self, max) :
"""Initialises the type
......@@ -1420,7 +1420,7 @@ class Less (Type) :
Less(5)
@param min: the smallest value not included in the type
@type min: any C{object} that support comparison
@type min: any `object` that support comparison
"""
self._max = max
def __contains__ (self, value) :
......@@ -1434,19 +1434,19 @@ class Less (Type) :
True
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return value < self._max
def __repr__ (self) :
"""Return a string representation of the type suitable for C{eval}
"""Return a string representation of the type suitable for `eval`
>>> repr(Less(3))
'Less(3)'
@return: precise string representation
@rtype: C{str}
@rtype: `str`
"""
return "Less(%s)" % repr(self._max)
__pnmltype__ = "less"
......@@ -1464,7 +1464,7 @@ class Less (Type) :
</pnml>
@return: PNML representation of the type
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
Tree.from_obj(self._max),
......@@ -1477,16 +1477,16 @@ class Less (Type) :
Less(3)
@param tree: PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: loaded type
@rtype: C{Less}
@rtype: `Less`
"""
return cls(tree.child().to_obj())
class LessOrEqual (Type) :
"""A type whose values are less than or equal to a maximum.
See the description of C{Greater}
See the description of `Greater`
"""
def __init__ (self, max) :
"""Initialises the type
......@@ -1495,7 +1495,7 @@ class LessOrEqual (Type) :
LessOrEqual(5)
@param min: the greatest value the type
@type min: any C{object} that support comparison
@type min: any `object` that support comparison
"""
self._max = max
......@@ -1510,19 +1510,19 @@ class LessOrEqual (Type) :
True
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
return value <= self._max
def __repr__ (self) :
"""Return a string representation of the type suitable for C{eval}
"""Return a string representation of the type suitable for `eval`
>>> repr(LessOrEqual(3))
'LessOrEqual(3)'
@return: precise string representation
@rtype: C{str}
@rtype: `str`
"""
return "LessOrEqual(%s)" % repr(self._max)
__pnmltype__ = "lesseq"
......@@ -1540,7 +1540,7 @@ class LessOrEqual (Type) :
</pnml>
@return: PNML representation of the type
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
Tree.from_obj(self._max),
......@@ -1553,9 +1553,9 @@ class LessOrEqual (Type) :
LessOrEqual(4)
@param tree: PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: loaded type
@rtype: C{LessOrEqual}
@rtype: `LessOrEqual`
"""
return cls(tree.child().to_obj())
......@@ -1575,19 +1575,20 @@ class CrossProduct (Type) :
>>> CrossProduct(Instance(int), Instance(float))
CrossProduct(Instance(int), Instance(float))
@param types: the types of each component of the allowed tuples
@type types: C{Type}
@param types: the types of each component of the allowed
tuples
@type types: `Type`
"""
self._types = types
_iterable(self, *types)
def __repr__ (self) :
"""Return a string representation of the type suitable for C{eval}
"""Return a string representation of the type suitable for `eval`
>>> repr(CrossProduct(Range(1, 10), Range(1, 10, 2), Range(1, 10)))
'CrossProduct(Range(1, 10), Range(1, 10, 2), Range(1, 10))'
@return: precise string representation
@rtype: C{str}
@rtype: `str`
"""
return "CrossProduct(%s)" % ", ".join([repr(t) for t in self._types])
def __contains__ (self, value) :
......@@ -1599,9 +1600,9 @@ class CrossProduct (Type) :
False
@param value: the value to check
@type value: C{object}
@return: C{True} if C{value} is in the type, C{False} otherwise
@rtype: C{bool}
@type value: `object`
@return: `True` if `value` is in the type, `False` otherwise
@rtype: `bool`
"""
if not isinstance(value, tuple) :
return False
......@@ -1622,7 +1623,7 @@ class CrossProduct (Type) :
ValueError: iteration over non-sequence
@return: an iterator over the values in the type
@rtype: C{generator}
@rtype: `generator`
@raise ValueError: when one component is not iterable
"""
self.__iterable__()
......@@ -1645,7 +1646,7 @@ class CrossProduct (Type) :
</pnml>
@return: PNML representation of the type
@rtype: C{snakes.pnml.Tree}
@rtype: `snakes.pnml.Tree`
"""
return Tree(self.__pnmltag__, None,
*(Tree.from_obj(t) for t in self._types),
......@@ -1659,9 +1660,9 @@ class CrossProduct (Type) :
CrossProduct(Instance(int), Instance(float))
@param tree: PNML tree to load
@type tree: C{snakes.pnml.Tree}
@type tree: `snakes.pnml.Tree`
@return: loaded type
@rtype: C{CrossProduct}
@rtype: `CrossProduct`
"""
return cls(*(child.to_obj() for child in tree.children))
......
......@@ -40,7 +40,8 @@ class Decl (object) :
setattr(self, key, val)
class GetInstanceArgs (object) :
"""Bind arguments for a net instance"""
"""Bind arguments for a net instance
"""
def __init__ (self, node) :
self.argspec = []
self.arg = {}
......