Franck Pommereau

doc update

1 -"""SNAKES is the Net Algebra Kit for Editors and Simulators 1 +"""SNAKES library is organised into three parts:
2 - 2 +
3 -SNAKES is a Python library allowing to model all sorts of Petri nets 3 + * the core library is package `snakes` and its modules, among which
4 -and to execute them. It is very general as most Petri nets annotations 4 + `snakes.nets` is the one to work with Petri nets while the others
5 -can be arbitrary Python expressions while most values can be arbitrary 5 + can be seen as auxiliary modules
6 -Python objects. 6 + * the plugin system and the plugins themselves reside into package
7 - 7 + `snakes.plugins`
8 -SNAKES can be further extended with plugins, several ones being 8 + * auxiliary tools are kept into other sub-packages: `snakes.lang`
9 -already provided, in particular two plugins implement the Petri nets 9 + for all the material related to parsing Python and other
10 -compositions defined for the Petri Box Calculus and its successors. 10 + languages, `snakes.utils` for various utilities like the ABCD
11 + compiler
11 12
12 @author: Franck Pommereau 13 @author: Franck Pommereau
13 @organization: University of Evry/Paris-Saclay 14 @organization: University of Evry/Paris-Saclay
14 @copyright: (C) 2005-2013 Franck Pommereau 15 @copyright: (C) 2005-2013 Franck Pommereau
15 @license: GNU Lesser General Public Licence (aka. GNU LGPL), see the 16 @license: GNU Lesser General Public Licence (aka. GNU LGPL), see the
16 - file `doc/COPYING` in the distribution or visit [the GNU web 17 + file `doc/COPYING` in the distribution or visit the [GNU web
17 site](http://www.gnu.org/licenses/licenses.html#LGPL) 18 site](http://www.gnu.org/licenses/licenses.html#LGPL)
18 @contact: franck.pommereau@ibisc.univ-evry.fr 19 @contact: franck.pommereau@ibisc.univ-evry.fr
19 """ 20 """
...@@ -21,8 +22,13 @@ compositions defined for the Petri Box Calculus and its successors. ...@@ -21,8 +22,13 @@ compositions defined for the Petri Box Calculus and its successors.
21 version = "0.9.16" 22 version = "0.9.16"
22 defaultencoding = "utf-8" 23 defaultencoding = "utf-8"
23 24
25 +"""## Module `snakes`
26 +
27 +This module only provides the exceptions used throughout SNAKES.
28 +"""
29 +
24 class SnakesError (Exception) : 30 class SnakesError (Exception) :
25 - "An error in SNAKES" 31 + "Generic error in SNAKES"
26 pass 32 pass
27 33
28 class ConstraintError (SnakesError) : 34 class ConstraintError (SnakesError) :
......
1 -"""Basic data types and functions used in SNAKES 1 +"""Basic data types and related functions used in SNAKES
2 """ 2 """
3 3
4 import operator, inspect 4 import operator, inspect
...@@ -8,8 +8,9 @@ from snakes.hashables import hdict ...@@ -8,8 +8,9 @@ from snakes.hashables import hdict
8 from snakes.pnml import Tree 8 from snakes.pnml import Tree
9 9
10 def cross (sets) : 10 def cross (sets) :
11 - """Cross-product. 11 + """Cross-product of some iterable collections (typically, `list`
12 - 12 + or `set`).
13 +
13 >>> list(cross([[1, 2], [3, 4, 5]])) 14 >>> list(cross([[1, 2], [3, 4, 5]]))
14 [(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)] 15 [(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)]
15 >>> list(cross([[1, 2], [3, 4, 5], [6, 7, 8, 9]])) 16 >>> list(cross([[1, 2], [3, 4, 5], [6, 7, 8, 9]]))
...@@ -19,12 +20,11 @@ def cross (sets) : ...@@ -19,12 +20,11 @@ def cross (sets) :
19 (2, 4, 8), (2, 4, 9), (2, 5, 6), (2, 5, 7), (2, 5, 8), (2, 5, 9)] 20 (2, 4, 8), (2, 4, 9), (2, 5, 6), (2, 5, 7), (2, 5, 8), (2, 5, 9)]
20 >>> list(cross([[], [1]])) 21 >>> list(cross([[], [1]]))
21 [] 22 []
22 - 23 +
23 @param sets: the sets of values to use 24 @param sets: the sets of values to use
24 - @type sets: `iterable(iterable(object))` 25 + @type sets: `iterable`
25 - @return: the `list` of obtained tuples (lists are used to allow 26 + @return: an iterator over the tuples in the cross-product
26 - unhashable objects) 27 + @rtype: `generator`
27 - @rtype: `generator(tuple(object))`
28 """ 28 """
29 if len(sets) == 0 : 29 if len(sets) == 0 :
30 pass 30 pass
...@@ -38,7 +38,7 @@ def cross (sets) : ...@@ -38,7 +38,7 @@ def cross (sets) :
38 38
39 def iterate (value) : 39 def iterate (value) :
40 """Like Python's builtin `iter` but consider strings as atomic. 40 """Like Python's builtin `iter` but consider strings as atomic.
41 - 41 +
42 >>> list(iter([1, 2, 3])) 42 >>> list(iter([1, 2, 3]))
43 [1, 2, 3] 43 [1, 2, 3]
44 >>> list(iterate([1, 2, 3])) 44 >>> list(iterate([1, 2, 3]))
...@@ -47,7 +47,7 @@ def iterate (value) : ...@@ -47,7 +47,7 @@ def iterate (value) :
47 ['f', 'o', 'o'] 47 ['f', 'o', 'o']
48 >>> list(iterate('foo')) 48 >>> list(iterate('foo'))
49 ['foo'] 49 ['foo']
50 - 50 +
51 @param value: any object 51 @param value: any object
52 @type value: `object` 52 @type value: `object`
53 @return: an iterator on the elements of `value` if is is iterable 53 @return: an iterator on the elements of `value` if is is iterable
...@@ -65,10 +65,10 @@ def iterate (value) : ...@@ -65,10 +65,10 @@ def iterate (value) :
65 class WordSet (set) : 65 class WordSet (set) :
66 """A set of words being able to generate fresh words. 66 """A set of words being able to generate fresh words.
67 """ 67 """
68 - def fresh (self, add=False, min=1, allowed="abcdefghijklmnopqrstuvwxyz", 68 + def fresh (self, add=False, min=1, base="",
69 - base="") : 69 + allowed="abcdefghijklmnopqrstuvwxyz") :
70 """Create a fresh word (ie, which is not in the set). 70 """Create a fresh word (ie, which is not in the set).
71 - 71 +
72 >>> w = WordSet(['foo', 'bar']) 72 >>> w = WordSet(['foo', 'bar'])
73 >>> list(sorted(w)) 73 >>> list(sorted(w))
74 ['bar', 'foo'] 74 ['bar', 'foo']
...@@ -80,13 +80,15 @@ class WordSet (set) : ...@@ -80,13 +80,15 @@ class WordSet (set) :
80 'baa' 80 'baa'
81 >>> list(sorted(w)) 81 >>> list(sorted(w))
82 ['aaa', 'baa', 'bar', 'foo'] 82 ['aaa', 'baa', 'bar', 'foo']
83 - 83 +
84 @param add: add the created word to the set if `add=True` 84 @param add: add the created word to the set if `add=True`
85 @type add: `bool` 85 @type add: `bool`
86 @param min: minimal length of the new word 86 @param min: minimal length of the new word
87 @type min: `int` 87 @type min: `int`
88 @param allowed: characters allowed in the new word 88 @param allowed: characters allowed in the new word
89 @type allowed: `str` 89 @type allowed: `str`
90 + @param base: prefix of generated words
91 + @type base: `str`
90 """ 92 """
91 if base : 93 if base :
92 result = [base] + [allowed[0]] * max(0, min - len(base)) 94 result = [base] + [allowed[0]] * max(0, min - len(base))
...@@ -115,31 +117,32 @@ class WordSet (set) : ...@@ -115,31 +117,32 @@ class WordSet (set) :
115 117
116 class MultiSet (hdict) : 118 class MultiSet (hdict) :
117 """Set with repetitions, ie, function from values to integers. 119 """Set with repetitions, ie, function from values to integers.
118 - 120 +
119 MultiSets support various operations, in particular: addition 121 MultiSets support various operations, in particular: addition
120 (`+`), substraction (`-`), multiplication by a non negative 122 (`+`), substraction (`-`), multiplication by a non negative
121 - integer (`*k`), comparisons (`<`, `>`, etc.), length (`len`) 123 + integer (`*k`), comparisons (`<`, `>`, etc.), length (`len`).
122 """ 124 """
123 def __init__ (self, values=[]) : 125 def __init__ (self, values=[]) :
124 """Initialise the multiset, adding values to it. 126 """Initialise the multiset, adding values to it.
125 - 127 +
126 >>> MultiSet([1, 2, 3, 1, 2]) 128 >>> MultiSet([1, 2, 3, 1, 2])
127 MultiSet([...]) 129 MultiSet([...])
128 >>> MultiSet() 130 >>> MultiSet()
129 MultiSet([]) 131 MultiSet([])
130 - 132 +
131 - @param values: a single value or an iterable object holding 133 + @param values: a single value or an iterable collection of
132 values (strings are not iterated) 134 values (strings are not iterated)
133 - @type values: any atomic object (`str` included) or an 135 + @type values: `object`
134 - iterable object
135 """ 136 """
136 self.add(values) 137 self.add(values)
137 def copy (self) : 138 def copy (self) :
138 """Copy a `MultiSet` 139 """Copy a `MultiSet`
139 - 140 +
140 - >>> MultiSet([1, 2, 3, 1, 2]).copy() 141 + >>> m1 = MultiSet([1, 2, 3, 1, 2])
141 - MultiSet([...]) 142 + >>> m2 = m1.copy()
142 - 143 + >>> m1 == m2 and m1 is not m2
144 + True
145 +
143 @return: a copy of the multiset 146 @return: a copy of the multiset
144 @rtype: `MultiSet` 147 @rtype: `MultiSet`
145 """ 148 """
...@@ -147,6 +150,7 @@ class MultiSet (hdict) : ...@@ -147,6 +150,7 @@ class MultiSet (hdict) :
147 result.update(self) 150 result.update(self)
148 return result 151 return result
149 __pnmltag__ = "multiset" 152 __pnmltag__ = "multiset"
153 + # apidoc skip
150 def __pnmldump__ (self) : 154 def __pnmldump__ (self) :
151 """ 155 """
152 >>> MultiSet([1, 2, 3, 4, 1, 2]).__pnmldump__() 156 >>> MultiSet([1, 2, 3, 4, 1, 2]).__pnmldump__()
...@@ -202,10 +206,11 @@ class MultiSet (hdict) : ...@@ -202,10 +206,11 @@ class MultiSet (hdict) :
202 Tree("value", None, Tree.from_obj(value)), 206 Tree("value", None, Tree.from_obj(value)),
203 Tree("multiplicity", str(self[value])))) 207 Tree("multiplicity", str(self[value]))))
204 return Tree(self.__pnmltag__, None, *nodes) 208 return Tree(self.__pnmltag__, None, *nodes)
209 + # apidoc skip
205 @classmethod 210 @classmethod
206 def __pnmlload__ (cls, tree) : 211 def __pnmlload__ (cls, tree) :
207 """Load a multiset from its PNML representation 212 """Load a multiset from its PNML representation
208 - 213 +
209 >>> t = MultiSet([1, 2, 3, 4, 1, 2]).__pnmldump__() 214 >>> t = MultiSet([1, 2, 3, 4, 1, 2]).__pnmldump__()
210 >>> MultiSet.__pnmlload__(t) 215 >>> MultiSet.__pnmlload__(t)
211 MultiSet([...]) 216 MultiSet([...])
...@@ -218,11 +223,12 @@ class MultiSet (hdict) : ...@@ -218,11 +223,12 @@ class MultiSet (hdict) :
218 return result 223 return result
219 def _add (self, value, times=1) : 224 def _add (self, value, times=1) :
220 """Add a single value `times` times. 225 """Add a single value `times` times.
221 - 226 +
222 @param value: the value to add 227 @param value: the value to add
223 - @type value: any object 228 + @type value: `object`
224 @param times: the number of times that `value` has to be added 229 @param times: the number of times that `value` has to be added
225 @type times: non negative `int` 230 @type times: non negative `int`
231 + @raise ValueError: when `times` is negative
226 """ 232 """
227 if times < 0 : 233 if times < 0 :
228 raise ValueError("negative values are forbidden") 234 raise ValueError("negative values are forbidden")
...@@ -232,7 +238,7 @@ class MultiSet (hdict) : ...@@ -232,7 +238,7 @@ class MultiSet (hdict) :
232 self[value] = times 238 self[value] = times
233 def add (self, values, times=1) : 239 def add (self, values, times=1) :
234 """Add values to the multiset. 240 """Add values to the multiset.
235 - 241 +
236 >>> m = MultiSet() 242 >>> m = MultiSet()
237 >>> m.add([1, 2, 2, 3], 2) 243 >>> m.add([1, 2, 2, 3], 2)
238 >>> list(sorted(m.items())) 244 >>> list(sorted(m.items()))
...@@ -240,24 +246,26 @@ class MultiSet (hdict) : ...@@ -240,24 +246,26 @@ class MultiSet (hdict) :
240 >>> m.add(5, 3) 246 >>> m.add(5, 3)
241 >>> list(sorted(m.items())) 247 >>> list(sorted(m.items()))
242 [1, 1, 2, 2, 2, 2, 3, 3, 5, 5, 5] 248 [1, 1, 2, 2, 2, 2, 3, 3, 5, 5, 5]
243 - 249 +
244 @param values: the values to add or a single value to add 250 @param values: the values to add or a single value to add
245 - @type values: any atomic object (`str` included) or an 251 + @type values: `object`
246 - iterable object
247 @param times: the number of times each value should be added 252 @param times: the number of times each value should be added
248 - @type times: non negative `int` 253 + (must be non-negative)
254 + @type times: `int`
255 + @raise ValueError: when `times` is negative
249 """ 256 """
250 self.__mutable__() 257 self.__mutable__()
251 for value in iterate(values) : 258 for value in iterate(values) :
252 self._add(value, times) 259 self._add(value, times)
253 def _remove (self, value, times=1) : 260 def _remove (self, value, times=1) :
254 """Remove a single value `times` times. 261 """Remove a single value `times` times.
255 - 262 +
256 @param value: the value to remove 263 @param value: the value to remove
257 @type value: any object 264 @type value: any object
258 @param times: the number of times that `value` has to be 265 @param times: the number of times that `value` has to be
259 removed 266 removed
260 @type times: non negative `int` 267 @type times: non negative `int`
268 + @raise ValueError: when `times` is negative
261 """ 269 """
262 if times < 0 : 270 if times < 0 :
263 raise ValueError("negative values are forbidden") 271 raise ValueError("negative values are forbidden")
...@@ -268,9 +276,8 @@ class MultiSet (hdict) : ...@@ -268,9 +276,8 @@ class MultiSet (hdict) :
268 del self[value] 276 del self[value]
269 def remove (self, values, times=1) : 277 def remove (self, values, times=1) :
270 """Remove values to the multiset. 278 """Remove values to the multiset.
271 - 279 +
272 - >>> m = MultiSet() 280 + >>> m = MultiSet([1, 2, 2, 3] * 2)
273 - >>> m.add([1, 2, 2, 3], 2)
274 >>> list(sorted(m.items())) 281 >>> list(sorted(m.items()))
275 [1, 1, 2, 2, 2, 2, 3, 3] 282 [1, 1, 2, 2, 2, 2, 3, 3]
276 >>> m.remove(2, 3) 283 >>> m.remove(2, 3)
...@@ -279,63 +286,63 @@ class MultiSet (hdict) : ...@@ -279,63 +286,63 @@ class MultiSet (hdict) :
279 >>> m.remove([1, 3], 2) 286 >>> m.remove([1, 3], 2)
280 >>> list(sorted(m.items())) 287 >>> list(sorted(m.items()))
281 [2] 288 [2]
282 - 289 +
283 @param values: the values to remove or a single value to 290 @param values: the values to remove or a single value to
284 remove 291 remove
285 - @type values: any atomic object (`str` included) or an 292 + @type values: `object`
286 - iterable object
287 @param times: the number of times each value should be removed 293 @param times: the number of times each value should be removed
288 - @type times: non negative `int` 294 + (must be non negative)
295 + @type times: `int`
296 + @raise ValueError: when `times` is negative
289 """ 297 """
290 self.__mutable__() 298 self.__mutable__()
291 for value in iterate(values) : 299 for value in iterate(values) :
292 self._remove(value, times) 300 self._remove(value, times)
293 def __call__ (self, value) : 301 def __call__ (self, value) :
294 """Number of occurrences of `value`. 302 """Number of occurrences of `value`.
295 - 303 +
296 >>> m = MultiSet([1, 1, 2, 3, 3, 3]) 304 >>> m = MultiSet([1, 1, 2, 3, 3, 3])
297 >>> m(1), m(2), m(3), m(4) 305 >>> m(1), m(2), m(3), m(4)
298 (2, 1, 3, 0) 306 (2, 1, 3, 0)
299 - 307 +
300 @param value: the value the count 308 @param value: the value the count
301 @type value: `object` 309 @type value: `object`
302 @rtype: `int` 310 @rtype: `int`
303 """ 311 """
304 return self.get(value, 0) 312 return self.get(value, 0)
305 def __iter__ (self) : 313 def __iter__ (self) :
306 - """Iterate over the values (with repetitions). 314 + """Iterate over the values, _including repetitions_. Use
307 - 315 + `MultiSet.keys` to ignore repetitions.
308 - Use `MultiSet.keys` to ignore repetitions. 316 +
309 -
310 >>> list(sorted(iter(MultiSet([1, 2, 3, 1, 2])))) 317 >>> list(sorted(iter(MultiSet([1, 2, 3, 1, 2]))))
311 [1, 1, 2, 2, 3] 318 [1, 1, 2, 2, 3]
312 - 319 +
313 @return: an iterator on the elements 320 @return: an iterator on the elements
314 - @rtype: `iterator` 321 + @rtype: `generator`
315 """ 322 """
316 for value in dict.__iter__(self) : 323 for value in dict.__iter__(self) :
317 for count in range(self[value]) : 324 for count in range(self[value]) :
318 yield value 325 yield value
319 def items (self) : 326 def items (self) :
320 """Return the list of items with repetitions. The list without 327 """Return the list of items with repetitions. The list without
321 - repetitions can be retrieved with the `key` method. 328 + repetitions can be retrieved with `MultiSet.key`.
322 - 329 +
323 >>> m = MultiSet([1, 2, 2, 3]) 330 >>> m = MultiSet([1, 2, 2, 3])
324 >>> list(sorted(m.items())) 331 >>> list(sorted(m.items()))
325 [1, 2, 2, 3] 332 [1, 2, 2, 3]
326 >>> list(sorted(m.keys())) 333 >>> list(sorted(m.keys()))
327 [1, 2, 3] 334 [1, 2, 3]
328 - 335 +
329 - @return: list of items with repetitions 336 + @return: list of items including repetitions
330 @rtype: `list` 337 @rtype: `list`
331 """ 338 """
332 return list(iter(self)) 339 return list(iter(self))
333 def __str__ (self) : 340 def __str__ (self) :
334 """Return a simple string representation of the multiset 341 """Return a simple string representation of the multiset
335 - 342 +
336 >>> str(MultiSet([1, 2, 2, 3])) 343 >>> str(MultiSet([1, 2, 2, 3]))
337 '{...}' 344 '{...}'
338 - 345 +
339 @return: simple string representation of the multiset 346 @return: simple string representation of the multiset
340 @rtype: `str` 347 @rtype: `str`
341 """ 348 """
...@@ -343,20 +350,20 @@ class MultiSet (hdict) : ...@@ -343,20 +350,20 @@ class MultiSet (hdict) :
343 def __repr__ (self) : 350 def __repr__ (self) :
344 """Return a string representation of the multiset that is 351 """Return a string representation of the multiset that is
345 suitable for `eval` 352 suitable for `eval`
346 - 353 +
347 >>> repr(MultiSet([1, 2, 2, 3])) 354 >>> repr(MultiSet([1, 2, 2, 3]))
348 'MultiSet([...])' 355 'MultiSet([...])'
349 - 356 +
350 @return: precise string representation of the multiset 357 @return: precise string representation of the multiset
351 @rtype: `str` 358 @rtype: `str`
352 """ 359 """
353 return "MultiSet([%s])" % ", ".join(repr(x) for x in self) 360 return "MultiSet([%s])" % ", ".join(repr(x) for x in self)
354 def __len__ (self) : 361 def __len__ (self) :
355 - """Return the number of elements, including repetitions. 362 + """Return the number of elements, _including repetitions_.
356 - 363 +
357 >>> len(MultiSet([1, 2] * 3)) 364 >>> len(MultiSet([1, 2] * 3))
358 6 365 6
359 - 366 +
360 @rtype: `int` 367 @rtype: `int`
361 """ 368 """
362 if self.size() == 0 : 369 if self.size() == 0 :
...@@ -364,20 +371,20 @@ class MultiSet (hdict) : ...@@ -364,20 +371,20 @@ class MultiSet (hdict) :
364 else : 371 else :
365 return reduce(operator.add, self.values()) 372 return reduce(operator.add, self.values())
366 def size (self) : 373 def size (self) :
367 - """Return the number of elements, excluding repetitions. 374 + """Return the number of elements, _excluding repetitions_.
368 - 375 +
369 >>> MultiSet([1, 2] * 3).size() 376 >>> MultiSet([1, 2] * 3).size()
370 2 377 2
371 - 378 +
372 @rtype: `int` 379 @rtype: `int`
373 """ 380 """
374 return dict.__len__(self) 381 return dict.__len__(self)
375 def __add__ (self, other) : 382 def __add__ (self, other) :
376 """Adds two multisets. 383 """Adds two multisets.
377 - 384 +
378 >>> MultiSet([1, 2, 3]) + MultiSet([2, 3, 4]) 385 >>> MultiSet([1, 2, 3]) + MultiSet([2, 3, 4])
379 MultiSet([...]) 386 MultiSet([...])
380 - 387 +
381 @param other: the multiset to add 388 @param other: the multiset to add
382 @type other: `MultiSet` 389 @type other: `MultiSet`
383 @rtype: `MultiSet` 390 @rtype: `MultiSet`
...@@ -387,18 +394,20 @@ class MultiSet (hdict) : ...@@ -387,18 +394,20 @@ class MultiSet (hdict) :
387 result._add(value, times) 394 result._add(value, times)
388 return result 395 return result
389 def __sub__ (self, other) : 396 def __sub__ (self, other) :
390 - """Substract two multisets. 397 + """Substract two multisets. The second multiset must be
391 - 398 + smaller than the first one.
399 +
392 >>> MultiSet([1, 2, 3]) - MultiSet([2, 3]) 400 >>> MultiSet([1, 2, 3]) - MultiSet([2, 3])
393 MultiSet([1]) 401 MultiSet([1])
394 >>> MultiSet([1, 2, 3]) - MultiSet([2, 3, 4]) 402 >>> MultiSet([1, 2, 3]) - MultiSet([2, 3, 4])
395 Traceback (most recent call last): 403 Traceback (most recent call last):
396 ... 404 ...
397 ValueError: not enough occurrences 405 ValueError: not enough occurrences
398 - 406 +
399 @param other: the multiset to substract 407 @param other: the multiset to substract
400 @type other: `MultiSet` 408 @type other: `MultiSet`
401 @rtype: `MultiSet` 409 @rtype: `MultiSet`
410 + @raise ValueError: when the second multiset is not smaller
402 """ 411 """
403 result = self.copy() 412 result = self.copy()
404 for value, times in dict.items(other) : 413 for value, times in dict.items(other) :
...@@ -406,13 +415,14 @@ class MultiSet (hdict) : ...@@ -406,13 +415,14 @@ class MultiSet (hdict) :
406 return result 415 return result
407 def __mul__ (self, other) : 416 def __mul__ (self, other) :
408 """Multiplication by a non-negative integer. 417 """Multiplication by a non-negative integer.
409 - 418 +
410 - >>> MultiSet([1, 2]) * 3 419 + >>> MultiSet([1, 2]) * 3 == MultiSet([1, 2] * 3)
411 - MultiSet([...]) 420 + True
412 - 421 +
413 @param other: the integer to multiply 422 @param other: the integer to multiply
414 @type other: non-negative `int` 423 @type other: non-negative `int`
415 @rtype: `MultiSet` 424 @rtype: `MultiSet`
425 + @raise ValueError: when `other` is negative
416 """ 426 """
417 if other < 0 : 427 if other < 0 :
418 raise ValueError("negative values are forbidden") 428 raise ValueError("negative values are forbidden")
...@@ -426,12 +436,12 @@ class MultiSet (hdict) : ...@@ -426,12 +436,12 @@ class MultiSet (hdict) :
426 __hash__ = hdict.__hash__ 436 __hash__ = hdict.__hash__
427 def __eq__ (self, other) : 437 def __eq__ (self, other) :
428 """Test for equality. 438 """Test for equality.
429 - 439 +
430 >>> MultiSet([1, 2, 3]*2) == MultiSet([1, 2, 3]*2) 440 >>> MultiSet([1, 2, 3]*2) == MultiSet([1, 2, 3]*2)
431 True 441 True
432 >>> MultiSet([1, 2, 3]) == MultiSet([1, 2, 3, 3]) 442 >>> MultiSet([1, 2, 3]) == MultiSet([1, 2, 3, 3])
433 False 443 False
434 - 444 +
435 @param other: the multiset to compare with 445 @param other: the multiset to compare with
436 @type other: `MultiSet` 446 @type other: `MultiSet`
437 @rtype: `bool` 447 @rtype: `bool`
...@@ -448,20 +458,22 @@ class MultiSet (hdict) : ...@@ -448,20 +458,22 @@ class MultiSet (hdict) :
448 return True 458 return True
449 def __ne__ (self, other) : 459 def __ne__ (self, other) :
450 """Test for difference. 460 """Test for difference.
451 - 461 +
452 >>> MultiSet([1, 2, 3]*2) != MultiSet([1, 2, 3]*2) 462 >>> MultiSet([1, 2, 3]*2) != MultiSet([1, 2, 3]*2)
453 False 463 False
454 >>> MultiSet([1, 2, 3]) != MultiSet([1, 2, 3, 3]) 464 >>> MultiSet([1, 2, 3]) != MultiSet([1, 2, 3, 3])
455 True 465 True
456 - 466 +
457 @param other: the multiset to compare with 467 @param other: the multiset to compare with
458 @type other: `MultiSet` 468 @type other: `MultiSet`
459 @rtype: `bool` 469 @rtype: `bool`
460 """ 470 """
461 return not(self == other) 471 return not(self == other)
462 def __lt__ (self, other) : 472 def __lt__ (self, other) :
463 - """Test for strict inclusion. 473 + """Test for strict inclusion. A multiset `A` is strictly
464 - 474 + included in a multiset `B` iff every element in `A` is also in
475 + `B` but less repetitions `A` than in `B`.
476 +
465 >>> MultiSet([1, 2, 3]) < MultiSet([1, 2, 3, 4]) 477 >>> MultiSet([1, 2, 3]) < MultiSet([1, 2, 3, 4])
466 True 478 True
467 >>> MultiSet([1, 2, 3]) < MultiSet([1, 2, 3, 3]) 479 >>> MultiSet([1, 2, 3]) < MultiSet([1, 2, 3, 3])
...@@ -472,7 +484,7 @@ class MultiSet (hdict) : ...@@ -472,7 +484,7 @@ class MultiSet (hdict) :
472 False 484 False
473 >>> MultiSet([1, 2, 2]) < MultiSet([1, 2, 3, 4]) 485 >>> MultiSet([1, 2, 2]) < MultiSet([1, 2, 3, 4])
474 False 486 False
475 - 487 +
476 @param other: the multiset to compare with 488 @param other: the multiset to compare with
477 @type other: `MultiSet` 489 @type other: `MultiSet`
478 @rtype: `bool` 490 @rtype: `bool`
...@@ -488,8 +500,8 @@ class MultiSet (hdict) : ...@@ -488,8 +500,8 @@ class MultiSet (hdict) :
488 result = True 500 result = True
489 return result or (dict.__len__(self) < dict.__len__(other)) 501 return result or (dict.__len__(self) < dict.__len__(other))
490 def __le__ (self, other) : 502 def __le__ (self, other) :
491 - """Test for inclusion inclusion. 503 + """Test for inclusion.
492 - 504 +
493 >>> MultiSet([1, 2, 3]) <= MultiSet([1, 2, 3, 4]) 505 >>> MultiSet([1, 2, 3]) <= MultiSet([1, 2, 3, 4])
494 True 506 True
495 >>> MultiSet([1, 2, 3]) <= MultiSet([1, 2, 3, 3]) 507 >>> MultiSet([1, 2, 3]) <= MultiSet([1, 2, 3, 3])
...@@ -500,7 +512,7 @@ class MultiSet (hdict) : ...@@ -500,7 +512,7 @@ class MultiSet (hdict) :
500 False 512 False
501 >>> MultiSet([1, 2, 2]) <= MultiSet([1, 2, 3, 4]) 513 >>> MultiSet([1, 2, 2]) <= MultiSet([1, 2, 3, 4])
502 False 514 False
503 - 515 +
504 @param other: the multiset to compare with 516 @param other: the multiset to compare with
505 @type other: `MultiSet` 517 @type other: `MultiSet`
506 @rtype: `bool` 518 @rtype: `bool`
...@@ -514,7 +526,7 @@ class MultiSet (hdict) : ...@@ -514,7 +526,7 @@ class MultiSet (hdict) :
514 return True 526 return True
515 def __gt__ (self, other) : 527 def __gt__ (self, other) :
516 """Test for strict inclusion. 528 """Test for strict inclusion.
517 - 529 +
518 >>> MultiSet([1, 2, 3, 4]) > MultiSet([1, 2, 3]) 530 >>> MultiSet([1, 2, 3, 4]) > MultiSet([1, 2, 3])
519 True 531 True
520 >>> MultiSet([1, 2, 3, 3]) > MultiSet([1, 2, 3]) 532 >>> MultiSet([1, 2, 3, 3]) > MultiSet([1, 2, 3])
...@@ -525,7 +537,7 @@ class MultiSet (hdict) : ...@@ -525,7 +537,7 @@ class MultiSet (hdict) :
525 False 537 False
526 >>> MultiSet([1, 2, 3, 4]) > MultiSet([1, 2, 2]) 538 >>> MultiSet([1, 2, 3, 4]) > MultiSet([1, 2, 2])
527 False 539 False
528 - 540 +
529 @param other: the multiset to compare with 541 @param other: the multiset to compare with
530 @type other: `MultiSet` 542 @type other: `MultiSet`
531 @rtype: `bool` 543 @rtype: `bool`
...@@ -533,7 +545,7 @@ class MultiSet (hdict) : ...@@ -533,7 +545,7 @@ class MultiSet (hdict) :
533 return other.__lt__(self) 545 return other.__lt__(self)
534 def __ge__ (self, other) : 546 def __ge__ (self, other) :
535 """Test for inclusion. 547 """Test for inclusion.
536 - 548 +
537 >>> MultiSet([1, 2, 3, 4]) >= MultiSet([1, 2, 3]) 549 >>> MultiSet([1, 2, 3, 4]) >= MultiSet([1, 2, 3])
538 True 550 True
539 >>> MultiSet([1, 2, 3, 3]) >= MultiSet([1, 2, 3]) 551 >>> MultiSet([1, 2, 3, 3]) >= MultiSet([1, 2, 3])
...@@ -544,18 +556,19 @@ class MultiSet (hdict) : ...@@ -544,18 +556,19 @@ class MultiSet (hdict) :
544 False 556 False
545 >>> MultiSet([1, 2, 3, 4]) >= MultiSet([1, 2, 2]) 557 >>> MultiSet([1, 2, 3, 4]) >= MultiSet([1, 2, 2])
546 False 558 False
547 - 559 +
548 @param other: the multiset to compare with 560 @param other: the multiset to compare with
549 @type other: `MultiSet` 561 @type other: `MultiSet`
550 @rtype: `bool` 562 @rtype: `bool`
551 """ 563 """
552 return other.__le__(self) 564 return other.__le__(self)
553 def domain (self) : 565 def domain (self) :
554 - """Return the domain of the multiset 566 + """Return the domain of the multiset, that is, the set of
555 - 567 + elements that occurr at least once in the multiset.
568 +
556 >>> list(sorted((MultiSet([1, 2, 3, 4]) + MultiSet([1, 2, 3])).domain())) 569 >>> list(sorted((MultiSet([1, 2, 3, 4]) + MultiSet([1, 2, 3])).domain()))
557 [1, 2, 3, 4] 570 [1, 2, 3, 4]
558 - 571 +
559 @return: the set of values in the domain 572 @return: the set of values in the domain
560 @rtype: `set` 573 @rtype: `set`
561 """ 574 """
...@@ -564,21 +577,21 @@ class MultiSet (hdict) : ...@@ -564,21 +577,21 @@ class MultiSet (hdict) :
564 class Substitution (object) : 577 class Substitution (object) :
565 """Map names to values or names, equals the identity where not 578 """Map names to values or names, equals the identity where not
566 defined. 579 defined.
567 - 580 +
568 Substitutions support the `+` operation (union with consistency 581 Substitutions support the `+` operation (union with consistency
569 check between the two operands) and the `*` operation which is the 582 check between the two operands) and the `*` operation which is the
570 composition of functions (`(f*g)(x)` is `f(g(x))`). 583 composition of functions (`(f*g)(x)` is `f(g(x))`).
571 - 584 +
572 Several methods (eg, `image`) return lists instead of sets, this 585 Several methods (eg, `image`) return lists instead of sets, this
573 avoids the restriction of having only hashable values in a 586 avoids the restriction of having only hashable values in a
574 substitution image. 587 substitution image.
575 """ 588 """
576 def __init__ (self, *largs, **dargs) : 589 def __init__ (self, *largs, **dargs) :
577 """Initialise using a dictionnary as a mapping. 590 """Initialise using a dictionnary as a mapping.
578 - 591 +
579 The expected arguments are any ones acceptables for 592 The expected arguments are any ones acceptables for
580 initializing a dictionnary. 593 initializing a dictionnary.
581 - 594 +
582 >>> Substitution() 595 >>> Substitution()
583 Substitution() 596 Substitution()
584 >>> Substitution(x=1, y=2) 597 >>> Substitution(x=1, y=2)
...@@ -589,6 +602,7 @@ class Substitution (object) : ...@@ -589,6 +602,7 @@ class Substitution (object) :
589 Substitution(...) 602 Substitution(...)
590 """ 603 """
591 self._dict = dict(*largs, **dargs) 604 self._dict = dict(*largs, **dargs)
605 + # apidoc skip
592 def __hash__ (self) : 606 def __hash__ (self) :
593 """ 607 """
594 >>> hash(Substitution(x=1, y=2)) == hash(Substitution(y=2, x=1)) 608 >>> hash(Substitution(x=1, y=2)) == hash(Substitution(y=2, x=1))
...@@ -599,7 +613,8 @@ class Substitution (object) : ...@@ -599,7 +613,8 @@ class Substitution (object) :
599 (hash(i) for i in self._dict.items()), 613 (hash(i) for i in self._dict.items()),
600 153913524) 614 153913524)
601 def __eq__ (self, other) : 615 def __eq__ (self, other) :
602 - """ 616 + """Test for equality.
617 +
603 >>> Substitution(x=1, y=2) == Substitution(y=2, x=1) 618 >>> Substitution(x=1, y=2) == Substitution(y=2, x=1)
604 True 619 True
605 >>> Substitution(x=1, y=2) == Substitution(y=1, x=1) 620 >>> Substitution(x=1, y=2) == Substitution(y=1, x=1)
...@@ -610,7 +625,8 @@ class Substitution (object) : ...@@ -610,7 +625,8 @@ class Substitution (object) :
610 except : 625 except :
611 return False 626 return False
612 def __ne__ (self, other) : 627 def __ne__ (self, other) :
613 - """ 628 + """Test for inequality.
629 +
614 >>> Substitution(x=1, y=2) != Substitution(y=2, x=1) 630 >>> Substitution(x=1, y=2) != Substitution(y=2, x=1)
615 False 631 False
616 >>> Substitution(x=1, y=2) != Substitution(y=1, x=1) 632 >>> Substitution(x=1, y=2) != Substitution(y=1, x=1)
...@@ -618,9 +634,10 @@ class Substitution (object) : ...@@ -618,9 +634,10 @@ class Substitution (object) :
618 """ 634 """
619 return not self.__eq__(other) 635 return not self.__eq__(other)
620 __pnmltag__ = "substitution" 636 __pnmltag__ = "substitution"
637 + # apidoc skip
621 def __pnmldump__ (self) : 638 def __pnmldump__ (self) :
622 """Dumps a substitution to a PNML tree 639 """Dumps a substitution to a PNML tree
623 - 640 +
624 >>> Substitution(x=1, y=2).__pnmldump__() 641 >>> Substitution(x=1, y=2).__pnmldump__()
625 <?xml version="1.0" encoding="utf-8"?> 642 <?xml version="1.0" encoding="utf-8"?>
626 <pnml> 643 <pnml>
...@@ -647,7 +664,7 @@ class Substitution (object) : ...@@ -647,7 +664,7 @@ class Substitution (object) :
647 </item> 664 </item>
648 </substitution> 665 </substitution>
649 </pnml> 666 </pnml>
650 - 667 +
651 @return: PNML representation 668 @return: PNML representation
652 @rtype: `snakes.pnml.Tree` 669 @rtype: `snakes.pnml.Tree`
653 """ 670 """
...@@ -658,14 +675,15 @@ class Substitution (object) : ...@@ -658,14 +675,15 @@ class Substitution (object) :
658 Tree("value", None, 675 Tree("value", None,
659 Tree.from_obj(value)))) 676 Tree.from_obj(value))))
660 return Tree(self.__pnmltag__, None, *nodes) 677 return Tree(self.__pnmltag__, None, *nodes)
678 + # apidoc skip
661 @classmethod 679 @classmethod
662 def __pnmlload__ (cls, tree) : 680 def __pnmlload__ (cls, tree) :
663 """Load a substitution from its PNML representation 681 """Load a substitution from its PNML representation
664 - 682 +
665 >>> t = Substitution(x=1, y=2).__pnmldump__() 683 >>> t = Substitution(x=1, y=2).__pnmldump__()
666 >>> Substitution.__pnmlload__(t) 684 >>> Substitution.__pnmlload__(t)
667 Substitution(...) 685 Substitution(...)
668 - 686 +
669 @param tree: the PNML tree to load 687 @param tree: the PNML tree to load
670 @type tree: `snakes.pnml.Tree` 688 @type tree: `snakes.pnml.Tree`
671 @return: the substitution loaded 689 @return: the substitution loaded
...@@ -678,43 +696,44 @@ class Substitution (object) : ...@@ -678,43 +696,44 @@ class Substitution (object) :
678 result._dict[name] = value 696 result._dict[name] = value
679 return result 697 return result
680 def items (self) : 698 def items (self) :
681 - """Return the list of pairs (name, value). 699 + """Return the list of pairs `(name, value)` such that the
682 - 700 + substitution maps each `name` to the correspondign `value`.
701 +
683 >>> Substitution(x=1, y=2).items() 702 >>> Substitution(x=1, y=2).items()
684 [('...', ...), ('...', ...)] 703 [('...', ...), ('...', ...)]
685 - 704 +
686 @return: a list of pairs (name, value) for each mapped name 705 @return: a list of pairs (name, value) for each mapped name
687 @rtype: `list` 706 @rtype: `list`
688 """ 707 """
689 return list(self._dict.items()) 708 return list(self._dict.items())
690 def domain (self) : 709 def domain (self) :
691 """Return the set of mapped names. 710 """Return the set of mapped names.
692 - 711 +
693 >>> list(sorted(Substitution(x=1, y=2).domain())) 712 >>> list(sorted(Substitution(x=1, y=2).domain()))
694 ['x', 'y'] 713 ['x', 'y']
695 - 714 +
696 @return: the set of mapped names 715 @return: the set of mapped names
697 @rtype: `set` 716 @rtype: `set`
698 """ 717 """
699 return set(self._dict.keys()) 718 return set(self._dict.keys())
700 def image (self) : 719 def image (self) :
701 - """Return the set of values associated to the names. 720 + """Return the list of values associated to the names.
702 - 721 +
703 >>> list(sorted(Substitution(x=1, y=2).image())) 722 >>> list(sorted(Substitution(x=1, y=2).image()))
704 [1, 2] 723 [1, 2]
705 - 724 +
706 - @return: the set of values associated to names 725 + @return: the list of values associated to names
707 - @rtype: `set` 726 + @rtype: `list`
708 """ 727 """
709 - return set(self._dict.values()) 728 + return list(self._dict.values())
710 def __contains__ (self, name) : 729 def __contains__ (self, name) :
711 - """Test if a name is mapped. 730 + """Test if a name is mapped by the substitution.
712 - 731 +
713 >>> 'x' in Substitution(x=1, y=2) 732 >>> 'x' in Substitution(x=1, y=2)
714 True 733 True
715 >>> 'z' in Substitution(x=1, y=2) 734 >>> 'z' in Substitution(x=1, y=2)
716 False 735 False
717 - 736 +
718 @param name: the name to test 737 @param name: the name to test
719 @type name: `str` (usually) 738 @type name: `str` (usually)
720 @return: a Boolean indicating whether this name is in the 739 @return: a Boolean indicating whether this name is in the
...@@ -724,20 +743,20 @@ class Substitution (object) : ...@@ -724,20 +743,20 @@ class Substitution (object) :
724 return name in self._dict 743 return name in self._dict
725 def __iter__ (self) : 744 def __iter__ (self) :
726 """Iterate over the mapped names. 745 """Iterate over the mapped names.
727 - 746 +
728 >>> list(sorted(iter(Substitution(x=1, y=2)))) 747 >>> list(sorted(iter(Substitution(x=1, y=2))))
729 ['x', 'y'] 748 ['x', 'y']
730 - 749 +
731 @return: an iterator over the domain of the substitution 750 @return: an iterator over the domain of the substitution
732 @rtype: `generator` 751 @rtype: `generator`
733 """ 752 """
734 return iter(self._dict) 753 return iter(self._dict)
735 def __str__ (self) : 754 def __str__ (self) :
736 """Return a compact string representation. 755 """Return a compact string representation.
737 - 756 +
738 >>> str(Substitution(x=1, y=2)) 757 >>> str(Substitution(x=1, y=2))
739 '{... -> ..., ... -> ...}' 758 '{... -> ..., ... -> ...}'
740 - 759 +
741 @return: a simple string representation 760 @return: a simple string representation
742 @rtype: `str` 761 @rtype: `str`
743 """ 762 """
...@@ -745,10 +764,10 @@ class Substitution (object) : ...@@ -745,10 +764,10 @@ class Substitution (object) :
745 for var, val in self.items()]) 764 for var, val in self.items()])
746 def __repr__ (self) : 765 def __repr__ (self) :
747 """Return a string representation suitable for `eval`. 766 """Return a string representation suitable for `eval`.
748 - 767 +
749 >>> repr(Substitution(x=1, y=2)) 768 >>> repr(Substitution(x=1, y=2))
750 'Substitution(...)' 769 'Substitution(...)'
751 - 770 +
752 @return: a precise string representation 771 @return: a precise string representation
753 @rtype: `str` 772 @rtype: `str`
754 """ 773 """
...@@ -757,33 +776,35 @@ class Substitution (object) : ...@@ -757,33 +776,35 @@ class Substitution (object) :
757 for var, val in self.items()))) 776 for var, val in self.items())))
758 def dict (self) : 777 def dict (self) :
759 """Return the mapping as a dictionnary. 778 """Return the mapping as a dictionnary.
760 - 779 +
761 - >>> Substitution(x=1, y=2).dict() 780 + >>> Substitution(x=1, y=2).dict() == {'x': 1, 'y': 2}
762 - {'...': ..., '...': ...} 781 + True
763 - 782 +
764 @return: a dictionnary that does the same mapping as the 783 @return: a dictionnary that does the same mapping as the
765 substitution 784 substitution
766 @rtype: `dict` 785 @rtype: `dict`
767 """ 786 """
768 return self._dict.copy() 787 return self._dict.copy()
769 def copy (self) : 788 def copy (self) :
770 - """Copy the mapping. 789 + """Return a distinct copy of the mapping.
771 - 790 +
772 - >>> Substitution(x=1, y=2).copy() 791 + >>> s1 = Substitution(x=1, y=2)
773 - Substitution(...) 792 + >>> s2 = s1.copy()
774 - 793 + >>> s1 == s2 and s1 is not s2
794 + True
795 +
775 @return: a copy of the substitution 796 @return: a copy of the substitution
776 @rtype: `Substitution` 797 @rtype: `Substitution`
777 """ 798 """
778 return Substitution(self.dict()) 799 return Substitution(self.dict())
779 def __setitem__ (self, var, value) : 800 def __setitem__ (self, var, value) :
780 """Assign an entry to the substitution 801 """Assign an entry to the substitution
781 - 802 +
782 >>> s = Substitution() 803 >>> s = Substitution()
783 >>> s['x'] = 42 804 >>> s['x'] = 42
784 >>> s 805 >>> s
785 Substitution(x=42) 806 Substitution(x=42)
786 - 807 +
787 @param var: the name of the variable 808 @param var: the name of the variable
788 @type var: `str` 809 @type var: `str`
789 @param value: the value to which `var` is bound 810 @param value: the value to which `var` is bound
...@@ -792,16 +813,14 @@ class Substitution (object) : ...@@ -792,16 +813,14 @@ class Substitution (object) :
792 self._dict[var] = value 813 self._dict[var] = value
793 def __getitem__ (self, var) : 814 def __getitem__ (self, var) :
794 """Return the mapped value. 815 """Return the mapped value.
795 - 816 +
796 - Fails with `DomainError` if `var` is not mapped.
797 -
798 >>> s = Substitution(x=1, y=2) 817 >>> s = Substitution(x=1, y=2)
799 >>> s['x'] 818 >>> s['x']
800 1 819 1
801 >>> try : s['z'] 820 >>> try : s['z']
802 ... except DomainError : print(sys.exc_info()[1]) 821 ... except DomainError : print(sys.exc_info()[1])
803 unbound variable 'z' 822 unbound variable 'z'
804 - 823 +
805 @param var: the name of the variable 824 @param var: the name of the variable
806 @type var: `str` (usually) 825 @type var: `str` (usually)
807 @return: the value that `var` maps to 826 @return: the value that `var` maps to
...@@ -813,16 +832,15 @@ class Substitution (object) : ...@@ -813,16 +832,15 @@ class Substitution (object) :
813 except KeyError : 832 except KeyError :
814 raise DomainError("unbound variable '%s'" % var) 833 raise DomainError("unbound variable '%s'" % var)
815 def __call__ (self, var) : 834 def __call__ (self, var) :
816 - """Return the mapped value. 835 + """Return the mapped value or `var` itself if it is not
817 - 836 + mapped.
818 - Never fails but return `var` if it is not mapped. 837 +
819 -
820 >>> s = Substitution(x=1, y=2) 838 >>> s = Substitution(x=1, y=2)
821 >>> s('x') 839 >>> s('x')
822 1 840 1
823 >>> s('z') 841 >>> s('z')
824 'z' 842 'z'
825 - 843 +
826 @param var: the name of the variable 844 @param var: the name of the variable
827 @type var: `str` (usually) 845 @type var: `str` (usually)
828 @return: the value that `var` maps to or `var` itself if it 846 @return: the value that `var` maps to or `var` itself if it
...@@ -835,22 +853,21 @@ class Substitution (object) : ...@@ -835,22 +853,21 @@ class Substitution (object) :
835 return var 853 return var
836 def __add__ (self, other) : 854 def __add__ (self, other) :
837 """Add two substitution. 855 """Add two substitution.
838 -
839 Fails with `DomainError` if the two substitutions map a same 856 Fails with `DomainError` if the two substitutions map a same
840 name to different values. 857 name to different values.
841 - 858 +
842 >>> s = Substitution(x=1, y=2) + Substitution(y=2, z=3) 859 >>> s = Substitution(x=1, y=2) + Substitution(y=2, z=3)
843 >>> s('x'), s('y'), s('z') 860 >>> s('x'), s('y'), s('z')
844 (1, 2, 3) 861 (1, 2, 3)
845 >>> try : Substitution(x=1, y=2) + Substitution(y=4, z=3) 862 >>> try : Substitution(x=1, y=2) + Substitution(y=4, z=3)
846 ... except DomainError : print(sys.exc_info()[1]) 863 ... except DomainError : print(sys.exc_info()[1])
847 conflict on 'y' 864 conflict on 'y'
848 - 865 +
849 @param other: another substitution 866 @param other: another substitution
850 @type other: `Substitution` 867 @type other: `Substitution`
851 @return: the union of the substitutions 868 @return: the union of the substitutions
852 @rtype: `Substitution` 869 @rtype: `Substitution`
853 - @raise DomainError: when one name is mapped to distinct values 870 + @raise DomainError: when a name is inconsistently mapped
854 """ 871 """
855 for var in self : 872 for var in self :
856 if var in other and (self[var] != other[var]) : 873 if var in other and (self[var] != other[var]) :
...@@ -860,15 +877,15 @@ class Substitution (object) : ...@@ -860,15 +877,15 @@ class Substitution (object) :
860 return s 877 return s
861 def __mul__ (self, other) : 878 def __mul__ (self, other) :
862 """Compose two substitutions. 879 """Compose two substitutions.
863 - 880 + The composition of `f` and `g` is such that `(f*g)(x)` is
864 - The composition of f and g is such that (f*g)(x) = f(g(x)). 881 + `f(g(x))`.
865 - 882 +
866 >>> f = Substitution(a=1, d=3, y=5) 883 >>> f = Substitution(a=1, d=3, y=5)
867 >>> g = Substitution(b='d', c=2, e=4, y=6) 884 >>> g = Substitution(b='d', c=2, e=4, y=6)
868 >>> h = f*g 885 >>> h = f*g
869 >>> h('a'), h('b'), h('c'), h('d'), h('e'), h('y'), h('x') 886 >>> h('a'), h('b'), h('c'), h('d'), h('e'), h('y'), h('x')
870 (1, 3, 2, 3, 4, 6, 'x') 887 (1, 3, 2, 3, 4, 6, 'x')
871 - 888 +
872 @param other: another substitution 889 @param other: another substitution
873 @type other: `Substitution` 890 @type other: `Substitution`
874 @return: the composition of the substitutions 891 @return: the composition of the substitutions
...@@ -886,13 +903,14 @@ class Symbol (object) : ...@@ -886,13 +903,14 @@ class Symbol (object) :
886 """If `export` is `True`, the created symbol is exported under 903 """If `export` is `True`, the created symbol is exported under
887 its name. If `export` is `False`, no export is made. Finally, 904 its name. If `export` is `False`, no export is made. Finally,
888 if `export` is a string, it specifies the name of the exported 905 if `export` is a string, it specifies the name of the exported
889 - symbol. 906 + symbol. Exporting the name is made by adding it to the
890 - 907 + caller's global dict.
908 +
891 @param name: the name (or value of the symbol) 909 @param name: the name (or value of the symbol)
892 @type name: `str` 910 @type name: `str`
893 @param export: the name under which the symbol is exported 911 @param export: the name under which the symbol is exported
894 @type export: `str` or `bool` or `None` 912 @type export: `str` or `bool` or `None`
895 - 913 +
896 >>> Symbol('foo') 914 >>> Symbol('foo')
897 Symbol('foo') 915 Symbol('foo')
898 >>> foo 916 >>> foo
...@@ -915,6 +933,7 @@ class Symbol (object) : ...@@ -915,6 +933,7 @@ class Symbol (object) :
915 if export : 933 if export :
916 inspect.stack()[1][0].f_globals[export] = self 934 inspect.stack()[1][0].f_globals[export] = self
917 __pnmltag__ = "symbol" 935 __pnmltag__ = "symbol"
936 + # apidoc skip
918 def __pnmldump__ (self) : 937 def __pnmldump__ (self) :
919 """ 938 """
920 >>> Symbol('egg', 'spam').__pnmldump__() 939 >>> Symbol('egg', 'spam').__pnmldump__()
...@@ -946,6 +965,7 @@ class Symbol (object) : ...@@ -946,6 +965,7 @@ class Symbol (object) :
946 else : 965 else :
947 children = [Tree.from_obj(self._export)] 966 children = [Tree.from_obj(self._export)]
948 return Tree(self.__pnmltag__, None, *children, **dict(name=self.name)) 967 return Tree(self.__pnmltag__, None, *children, **dict(name=self.name))
968 + # apidoc skip
949 @classmethod 969 @classmethod
950 def __pnmlload__ (cls, tree) : 970 def __pnmlload__ (cls, tree) :
951 """ 971 """
...@@ -963,7 +983,9 @@ class Symbol (object) : ...@@ -963,7 +983,9 @@ class Symbol (object) :
963 export = name 983 export = name
964 return cls(name, export) 984 return cls(name, export)
965 def __eq__ (self, other) : 985 def __eq__ (self, other) :
966 - """ 986 + """Test for equality of two symbols, which is the equality of
987 + their names.
988 +
967 >>> Symbol('foo', 'bar') == Symbol('foo') 989 >>> Symbol('foo', 'bar') == Symbol('foo')
968 True 990 True
969 >>> Symbol('egg') == Symbol('spam') 991 >>> Symbol('egg') == Symbol('spam')
...@@ -975,13 +997,15 @@ class Symbol (object) : ...@@ -975,13 +997,15 @@ class Symbol (object) :
975 except AttributeError : 997 except AttributeError :
976 return False 998 return False
977 def __ne__ (self, other) : 999 def __ne__ (self, other) :
978 - """ 1000 + """Test for inequality.
1001 +
979 >>> Symbol('foo', 'bar') != Symbol('foo') 1002 >>> Symbol('foo', 'bar') != Symbol('foo')
980 False 1003 False
981 >>> Symbol('egg') != Symbol('spam') 1004 >>> Symbol('egg') != Symbol('spam')
982 True 1005 True
983 """ 1006 """
984 return not (self == other) 1007 return not (self == other)
1008 + # apidoc skip
985 def __hash__ (self) : 1009 def __hash__ (self) :
986 """ 1010 """
987 >>> hash(Symbol('foo', 'bar')) == hash(Symbol('foo')) 1011 >>> hash(Symbol('foo', 'bar')) == hash(Symbol('foo'))
...@@ -989,13 +1013,15 @@ class Symbol (object) : ...@@ -989,13 +1013,15 @@ class Symbol (object) :
989 """ 1013 """
990 return hash((self.__class__.__name__, self.name)) 1014 return hash((self.__class__.__name__, self.name))
991 def __str__ (self) : 1015 def __str__ (self) :
992 - """ 1016 + """Short string representation
1017 +
993 >>> str(Symbol('foo')) 1018 >>> str(Symbol('foo'))
994 'foo' 1019 'foo'
995 """ 1020 """
996 return self.name 1021 return self.name
997 def __repr__ (self) : 1022 def __repr__ (self) :
998 - """ 1023 + """String representation suitable for `eval`
1024 +
999 >>> Symbol('foo') 1025 >>> Symbol('foo')
1000 Symbol('foo') 1026 Symbol('foo')
1001 >>> Symbol('egg', 'spam') 1027 >>> Symbol('egg', 'spam')
......
1 -"""Hashable mutable objets. 1 +"""This module proposes hashable version of the mutable containers
2 -
3 -This module proposes hashable version of the mutable containers
4 `list`, `dict` and `set`, called respectively `hlist`, `hdict` and 2 `list`, `dict` and `set`, called respectively `hlist`, `hdict` and
5 -`hset`. After one object has been hashed, it becomes not mutable and 3 +`hset`.
6 -raises a ValueError if a method which changes the object (let call a 4 +
7 -_mutation_ such a method) is invoqued. The object can be then un- 5 +After one object has been hashed, it becomes not mutable and raises a
8 -hashed by calling `unhash` on it so that it becomes mutable again. 6 +`ValueError` if a method which changes the object (let call a
7 +_mutation_ such a method) is invoqued. The object can be then
8 +un-hashed by calling `unhash` on it so that it becomes mutable again.
9 Notice that this may cause troubles if the object is stored in a set 9 Notice that this may cause troubles if the object is stored in a set
10 or dict which uses its hashcode to locate it. Notice also that 10 or dict which uses its hashcode to locate it. Notice also that
11 hashable containers cannot be hashed if they contain non hashable 11 hashable containers cannot be hashed if they contain non hashable
...@@ -53,8 +53,9 @@ from operator import xor ...@@ -53,8 +53,9 @@ from operator import xor
53 from snakes.compat import * 53 from snakes.compat import *
54 54
55 def unhash (obj) : 55 def unhash (obj) :
56 - """Make the object hashable again. 56 + """Make the object mutable again. This should be used with
57 - 57 + caution, especially if the object is stored in a dict or a set.
58 +
58 >>> l = hlist(range(3)) 59 >>> l = hlist(range(3))
59 >>> _ = hash(l) 60 >>> _ = hash(l)
60 >>> l.append(3) 61 >>> l.append(3)
...@@ -63,7 +64,7 @@ def unhash (obj) : ...@@ -63,7 +64,7 @@ def unhash (obj) :
63 ValueError: hashed 'hlist' object is not mutable 64 ValueError: hashed 'hlist' object is not mutable
64 >>> unhash(l) 65 >>> unhash(l)
65 >>> l.append(3) 66 >>> l.append(3)
66 - 67 +
67 @param obj: any object 68 @param obj: any object
68 @type obj: `object` 69 @type obj: `object`
69 """ 70 """
...@@ -72,6 +73,7 @@ def unhash (obj) : ...@@ -72,6 +73,7 @@ def unhash (obj) :
72 except : 73 except :
73 pass 74 pass
74 75
76 +# apidoc skip
75 def hashable (cls) : 77 def hashable (cls) :
76 """Wrap methods in a class in order to make it hashable. 78 """Wrap methods in a class in order to make it hashable.
77 """ 79 """
...@@ -122,8 +124,8 @@ def hashable (cls) : ...@@ -122,8 +124,8 @@ def hashable (cls) :
122 return cls 124 return cls
123 125
124 class hlist (list) : 126 class hlist (list) :
125 - """Hashable lists. 127 + """Hashable lists. They support all standard methods from `list`.
126 - 128 +
127 >>> l = hlist(range(5)) 129 >>> l = hlist(range(5))
128 >>> l 130 >>> l
129 hlist([0, 1, 2, 3, 4]) 131 hlist([0, 1, 2, 3, 4])
...@@ -138,6 +140,7 @@ class hlist (list) : ...@@ -138,6 +140,7 @@ class hlist (list) :
138 >>> unhash(l) 140 >>> unhash(l)
139 >>> l.append(6) 141 >>> l.append(6)
140 """ 142 """
143 + # apidoc stop
141 def __add__ (self, other) : 144 def __add__ (self, other) :
142 return self.__class__(list.__add__(self, other)) 145 return self.__class__(list.__add__(self, other))
143 def __delitem__ (self, item) : 146 def __delitem__ (self, item) :
...@@ -200,8 +203,9 @@ class hlist (list) : ...@@ -200,8 +203,9 @@ class hlist (list) :
200 hlist = hashable(hlist) 203 hlist = hashable(hlist)
201 204
202 class hdict (dict) : 205 class hdict (dict) :
203 - """Hashable dictionnaries. 206 + """Hashable dictionnaries. They support all standard methods from
204 - 207 + `dict`.
208 +
205 >>> l = hlist(range(5)) 209 >>> l = hlist(range(5))
206 >>> d = hdict([(l, 0)]) 210 >>> d = hdict([(l, 0)])
207 >>> d 211 >>> d
...@@ -229,6 +233,7 @@ class hdict (dict) : ...@@ -229,6 +233,7 @@ class hdict (dict) :
229 >>> d.pop(l) 233 >>> d.pop(l)
230 0 234 0
231 """ 235 """
236 + # apidoc stop
232 def __delitem__ (self, key) : 237 def __delitem__ (self, key) :
233 self.__mutable__() 238 self.__mutable__()
234 dict.__delitem__(self, key) 239 dict.__delitem__(self, key)
...@@ -271,8 +276,8 @@ class hdict (dict) : ...@@ -271,8 +276,8 @@ class hdict (dict) :
271 hdict = hashable(hdict) 276 hdict = hashable(hdict)
272 277
273 class hset (set) : 278 class hset (set) :
274 - """Hashable sets. 279 + """Hashable sets. They support all standard methods from `set`.
275 - 280 +
276 >>> s = hset() 281 >>> s = hset()
277 >>> l = hlist(range(5)) 282 >>> l = hlist(range(5))
278 >>> s.add(l) 283 >>> s.add(l)
...@@ -298,6 +303,7 @@ class hset (set) : ...@@ -298,6 +303,7 @@ class hset (set) :
298 >>> unhash(s) 303 >>> unhash(s)
299 >>> s.discard(l) 304 >>> s.discard(l)
300 """ 305 """
306 + # apidoc stop
301 def __and__ (self, other) : 307 def __and__ (self, other) :
302 return self.__class__(set.__and__(self, other)) 308 return self.__class__(set.__and__(self, other))
303 def __hash__ (self) : 309 def __hash__ (self) :
......
1 +"""This package is dedicated to parse and work with various languages,
2 +in particular Python itself and ABCD. These are mainly utilities for
3 +internal use in SNAKES, however, they may be of general interest
4 +independently of SNAKES.
5 +
6 +@todo: add documentation about how to use parsing and similar services
7 +"""
8 +
1 import sys 9 import sys
2 if sys.version_info[:2] in ((2, 6), (2, 7)) : 10 if sys.version_info[:2] in ((2, 6), (2, 7)) :
3 import ast 11 import ast
...@@ -14,9 +22,34 @@ else : ...@@ -14,9 +22,34 @@ else :
14 22
15 sys.modules["snkast"] = ast 23 sys.modules["snkast"] = ast
16 24
25 +"""### Module `ast` ###
26 +
27 +The module `ast` exported by `snakes.lang` is similar to Python's
28 +standard `ast` module (starting from version 2.6) but is available and
29 +uniform on every implementation of Python supported by SNAKES: CPython
30 +from 2.5, Jython and PyPy. (In general, these modules are available in
31 +these implementation - except CPython 2.5 - but with slight
32 +differences, so using `snakes.lang.ast` can be seen as a portable
33 +implementation.)
34 +
35 +Notice that this module is _not_ available for direct import but is
36 +exposed as a member of `snakes.lang`. Moreover, when `snakes.lang` is
37 +loaded, this module `ast` is also loaded as `snkast` in `sys.modules`,
38 +this allows to have both versions from Python and SNAKES
39 +simultaneously.
40 +
41 +>>> import snakes.lang.ast as ast
42 +ImportError ...
43 + ...
44 +ImportError: No module named ast
45 +>>> from snakes.lang import ast
46 +>>> import snkast
47 +"""
48 +
17 from . import unparse as _unparse 49 from . import unparse as _unparse
18 from snakes.compat import * 50 from snakes.compat import *
19 51
52 +# apidoc skip
20 class Names (ast.NodeVisitor) : 53 class Names (ast.NodeVisitor) :
21 def __init__ (self) : 54 def __init__ (self) :
22 ast.NodeVisitor.__init__(self) 55 ast.NodeVisitor.__init__(self)
...@@ -25,16 +58,24 @@ class Names (ast.NodeVisitor) : ...@@ -25,16 +58,24 @@ class Names (ast.NodeVisitor) :
25 self.names.add(node.id) 58 self.names.add(node.id)
26 59
27 def getvars (expr) : 60 def getvars (expr) :
28 - """ 61 + """Return the set of variables (or names in general) involved in a
62 + Python expression.
63 +
29 >>> list(sorted(getvars('x+y<z'))) 64 >>> list(sorted(getvars('x+y<z')))
30 ['x', 'y', 'z'] 65 ['x', 'y', 'z']
31 >>> list(sorted(getvars('x+y<z+f(3,t)'))) 66 >>> list(sorted(getvars('x+y<z+f(3,t)')))
32 ['f', 't', 'x', 'y', 'z'] 67 ['f', 't', 'x', 'y', 'z']
68 +
69 + @param expr: a Python expression
70 + @type expr: `str`
71 + @return: the set of variable names as strings
72 + @rtype: `set`
33 """ 73 """
34 names = Names() 74 names = Names()
35 names.visit(ast.parse(expr)) 75 names.visit(ast.parse(expr))
36 return names.names - set(['None', 'True', 'False']) 76 return names.names - set(['None', 'True', 'False'])
37 77
78 +# apidoc skip
38 class Unparser(_unparse.Unparser) : 79 class Unparser(_unparse.Unparser) :
39 boolops = {"And": 'and', "Or": 'or'} 80 boolops = {"And": 'and', "Or": 'or'}
40 def _Interactive (self, tree) : 81 def _Interactive (self, tree) :
...@@ -58,11 +99,13 @@ class Unparser(_unparse.Unparser) : ...@@ -58,11 +99,13 @@ class Unparser(_unparse.Unparser) :
58 self.dispatch(tree.body) 99 self.dispatch(tree.body)
59 self.leave() 100 self.leave()
60 101
102 +# apidoc skip
61 def unparse (st) : 103 def unparse (st) :
62 output = io.StringIO() 104 output = io.StringIO()
63 Unparser(st, output) 105 Unparser(st, output)
64 return output.getvalue().strip() 106 return output.getvalue().strip()
65 107
108 +# apidoc skip
66 class Renamer (ast.NodeTransformer) : 109 class Renamer (ast.NodeTransformer) :
67 def __init__ (self, map_names) : 110 def __init__ (self, map_names) :
68 ast.NodeTransformer.__init__(self) 111 ast.NodeTransformer.__init__(self)
...@@ -96,7 +139,8 @@ class Renamer (ast.NodeTransformer) : ...@@ -96,7 +139,8 @@ class Renamer (ast.NodeTransformer) :
96 ctx=ast.Load()), node) 139 ctx=ast.Load()), node)
97 140
98 def rename (expr, map={}, **ren) : 141 def rename (expr, map={}, **ren) :
99 - """ 142 + """Rename variables (ie, names) in a Python expression
143 +
100 >>> rename('x+y<z', x='t') 144 >>> rename('x+y<z', x='t')
101 '((t + y) < z)' 145 '((t + y) < z)'
102 >>> rename('x+y<z+f(3,t)', f='g', t='z', z='t') 146 >>> rename('x+y<z+f(3,t)', f='g', t='z', z='t')
...@@ -105,12 +149,22 @@ def rename (expr, map={}, **ren) : ...@@ -105,12 +149,22 @@ def rename (expr, map={}, **ren) :
105 '[(x + y) for x in range(3)]' 149 '[(x + y) for x in range(3)]'
106 >>> rename('[x+y for x in range(3)]', y='z') 150 >>> rename('[x+y for x in range(3)]', y='z')
107 '[(x + z) for x in range(3)]' 151 '[(x + z) for x in range(3)]'
152 +
153 + @param expr: a Python expression
154 + @type expr: `str`
155 + @param map: a mapping from old to new names (`str` to `str`)
156 + @type map: `dict`
157 + @param ren: additional mapping of old to new names
158 + @type ren: `str`
159 + @return: the new expression
160 + @rtype: `str`
108 """ 161 """
109 map_names = dict(map) 162 map_names = dict(map)
110 map_names.update(ren) 163 map_names.update(ren)
111 transf = Renamer(map_names) 164 transf = Renamer(map_names)
112 return unparse(transf.visit(ast.parse(expr))) 165 return unparse(transf.visit(ast.parse(expr)))
113 166
167 +# apidoc skip
114 class Binder (Renamer) : 168 class Binder (Renamer) :
115 def visit_Name (self, node) : 169 def visit_Name (self, node) :
116 if node.id in self.map[-1] : 170 if node.id in self.map[-1] :
...@@ -119,7 +173,10 @@ class Binder (Renamer) : ...@@ -119,7 +173,10 @@ class Binder (Renamer) :
119 return node 173 return node
120 174
121 def bind (expr, map={}, **ren) : 175 def bind (expr, map={}, **ren) :
122 - """ 176 + """Replace variables (ie, names) in an expression with other
177 + expressions. The replacements should be provided as `ast` nodes,
178 + and so could be arbitrary expression.
179 +
123 >>> bind('x+y<z', x=ast.Num(n=2)) 180 >>> bind('x+y<z', x=ast.Num(n=2))
124 '((2 + y) < z)' 181 '((2 + y) < z)'
125 >>> bind('x+y<z', y=ast.Num(n=2)) 182 >>> bind('x+y<z', y=ast.Num(n=2))
...@@ -128,6 +185,15 @@ def bind (expr, map={}, **ren) : ...@@ -128,6 +185,15 @@ def bind (expr, map={}, **ren) :
128 '[(x + y) for x in range(3)]' 185 '[(x + y) for x in range(3)]'
129 >>> bind('[x+y for x in range(3)]', y=ast.Num(n=2)) 186 >>> bind('[x+y for x in range(3)]', y=ast.Num(n=2))
130 '[(x + 2) for x in range(3)]' 187 '[(x + 2) for x in range(3)]'
188 +
189 + @param expr: a Python expression
190 + @type expr: `str`
191 + @param map: a mapping from old to new names (`str` to `ast.AST`)
192 + @type map: `dict`
193 + @param ren: additional mapping of old to new names
194 + @type ren: `ast.AST`
195 + @return: the new expression
196 + @rtype: `str`
131 """ 197 """
132 map_names = dict(map) 198 map_names = dict(map)
133 map_names.update(ren) 199 map_names.update(ren)
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
2 2
3 This module holds the various Petri net elements: arcs, places, 3 This module holds the various Petri net elements: arcs, places,
4 transitions, markings, nets themselves and marking graphs. 4 transitions, markings, nets themselves and marking graphs.
5 +
6 +@todo: revise documentation
5 """ 7 """
6 8
7 import re, operator, inspect 9 import re, operator, inspect
...@@ -35,7 +37,7 @@ class Evaluator (object) : ...@@ -35,7 +37,7 @@ class Evaluator (object) :
35 exec(stmt, self._env, locals) 37 exec(stmt, self._env, locals)
36 def attach (self, other) : 38 def attach (self, other) :
37 """Make this instance use the namespaces of another 39 """Make this instance use the namespaces of another
38 - 40 +
39 All the instances attached to this will also share the same 41 All the instances attached to this will also share the same
40 namespace. 42 namespace.
41 """ 43 """
...@@ -88,20 +90,20 @@ class Evaluator (object) : ...@@ -88,20 +90,20 @@ class Evaluator (object) :
88 90
89 class NetElement (object) : 91 class NetElement (object) :
90 """The base class for Petri net elements. 92 """The base class for Petri net elements.
91 - 93 +
92 Attributes can be locked, forbidding their assignment or deletion. 94 Attributes can be locked, forbidding their assignment or deletion.
93 For instance, a PetriNet object may lock the 'name' field of its 95 For instance, a PetriNet object may lock the 'name' field of its
94 nodes in order to force the renaming through the method 96 nodes in order to force the renaming through the method
95 'PetriNet.rename'. 97 'PetriNet.rename'.
96 - 98 +
97 FIXME: attribute locking is an unnecessarry complication, this 99 FIXME: attribute locking is an unnecessarry complication, this
98 will be progressively removed. 100 will be progressively removed.
99 - 101 +
100 This class is abstract and should not be instanciated. 102 This class is abstract and should not be instanciated.
101 """ 103 """
102 def __init__ (self) : 104 def __init__ (self) :
103 """Abstract method 105 """Abstract method
104 - 106 +
105 >>> try : NetElement() 107 >>> try : NetElement()
106 ... except NotImplementedError : print(sys.exc_info()[1]) 108 ... except NotImplementedError : print(sys.exc_info()[1])
107 abstract class 109 abstract class
...@@ -110,7 +112,7 @@ class NetElement (object) : ...@@ -110,7 +112,7 @@ class NetElement (object) :
110 def lock (self, name, owner, value=None) : 112 def lock (self, name, owner, value=None) :
111 """Lock the attribute `name` that becomes writeable only by 113 """Lock the attribute `name` that becomes writeable only by
112 `owner`, set its value if `value` is not `None`. 114 `owner`, set its value if `value` is not `None`.
113 - 115 +
114 @param name: the name of the attribute 116 @param name: the name of the attribute
115 @type name: `str` 117 @type name: `str`
116 @param owner: any value, usually the object instance that 118 @param owner: any value, usually the object instance that
...@@ -131,7 +133,7 @@ class NetElement (object) : ...@@ -131,7 +133,7 @@ class NetElement (object) :
131 def unlock (self, name, owner, remove=False, value=None) : 133 def unlock (self, name, owner, remove=False, value=None) :
132 """Release a locked attribute, making it assignable or deletable 134 """Release a locked attribute, making it assignable or deletable
133 from everywhere. 135 from everywhere.
134 - 136 +
135 @param name: the name of the attribute 137 @param name: the name of the attribute
136 @type name: `str` 138 @type name: `str`
137 @param owner: the value that was used to lock the attribute 139 @param owner: the value that was used to lock the attribute
...@@ -158,11 +160,11 @@ class NetElement (object) : ...@@ -158,11 +160,11 @@ class NetElement (object) :
158 # raise ConstraintError, "'%s' not locked" % name 160 # raise ConstraintError, "'%s' not locked" % name
159 def __setattr__ (self, name, value) : 161 def __setattr__ (self, name, value) :
160 """Assign attribute, taking care of locks 162 """Assign attribute, taking care of locks
161 - 163 +
162 Check whether the assignment is allowed or not, the owner may 164 Check whether the assignment is allowed or not, the owner may
163 re-assign the attribute by using `lock` with its parameter 165 re-assign the attribute by using `lock` with its parameter
164 `value` instead of a direct assignment. 166 `value` instead of a direct assignment.
165 - 167 +
166 @param name: name of the attribute 168 @param name: name of the attribute
167 @type name: `str` 169 @type name: `str`
168 @param value: value of the attribute 170 @param value: value of the attribute
...@@ -174,11 +176,11 @@ class NetElement (object) : ...@@ -174,11 +176,11 @@ class NetElement (object) :
174 self.__dict__[name] = value 176 self.__dict__[name] = value
175 def __delattr__ (self, name) : 177 def __delattr__ (self, name) :
176 """Delete attribute, taking care of locks 178 """Delete attribute, taking care of locks
177 - 179 +
178 Check whether the deletion is allowed or not, the owner may 180 Check whether the deletion is allowed or not, the owner may
179 delete the attribute by using `unlock` with its parameter 181 delete the attribute by using `unlock` with its parameter
180 `remove` set to `True` instead of a direct deletion. 182 `remove` set to `True` instead of a direct deletion.
181 - 183 +
182 @param name: name of the attribute 184 @param name: name of the attribute
183 @type name: `str` 185 @type name: `str`
184 """ 186 """
...@@ -193,49 +195,49 @@ class NetElement (object) : ...@@ -193,49 +195,49 @@ class NetElement (object) :
193 195
194 class Token (NetElement) : 196 class Token (NetElement) :
195 """A container for one token value 197 """A container for one token value
196 - 198 +
197 This class is intended for internal use only in order to avoid 199 This class is intended for internal use only in order to avoid
198 some confusion when inserting arbitrary values in a place. End 200 some confusion when inserting arbitrary values in a place. End
199 users will probably never need to use it. 201 users will probably never need to use it.
200 """ 202 """
201 def __init__ (self, value) : 203 def __init__ (self, value) :
202 """Initialise the token 204 """Initialise the token
203 - 205 +
204 >>> Token(42) 206 >>> Token(42)
205 Token(42) 207 Token(42)
206 - 208 +
207 @param value: value of the token 209 @param value: value of the token
208 @type value: `object` 210 @type value: `object`
209 """ 211 """
210 self.value = value 212 self.value = value
211 def __str__ (self) : 213 def __str__ (self) :
212 """Simple string representation (that of the value) 214 """Simple string representation (that of the value)
213 - 215 +
214 >>> str(Token(42)) 216 >>> str(Token(42))
215 '42' 217 '42'
216 - 218 +
217 @return: simple string representation 219 @return: simple string representation
218 @rtype: `str` 220 @rtype: `str`
219 """ 221 """
220 return str(self.value) 222 return str(self.value)
221 def __repr__ (self) : 223 def __repr__ (self) :
222 """String representation suitable for `eval` 224 """String representation suitable for `eval`
223 - 225 +
224 >>> repr(Token(42)) 226 >>> repr(Token(42))
225 'Token(42)' 227 'Token(42)'
226 - 228 +
227 @return: precise string representation 229 @return: precise string representation
228 @rtype: `str` 230 @rtype: `str`
229 """ 231 """
230 return "%s(%s)" % (self.__class__.__name__, repr(self.value)) 232 return "%s(%s)" % (self.__class__.__name__, repr(self.value))
231 def __eq__ (self, other) : 233 def __eq__ (self, other) :
232 """Test token equality 234 """Test token equality
233 - 235 +
234 >>> Token(42) == Token(10) 236 >>> Token(42) == Token(10)
235 False 237 False
236 >>> Token(42) == Token(42) 238 >>> Token(42) == Token(42)
237 True 239 True
238 - 240 +
239 @param other: second argument of equality 241 @param other: second argument of equality
240 @type other: `Token` 242 @type other: `Token`
241 @return: `True` if token values are equal, `False` otherwise 243 @return: `True` if token values are equal, `False` otherwise
...@@ -247,12 +249,12 @@ class Token (NetElement) : ...@@ -247,12 +249,12 @@ class Token (NetElement) :
247 return False 249 return False
248 def __ne__ (self, other) : 250 def __ne__ (self, other) :
249 """Test token inequality 251 """Test token inequality
250 - 252 +
251 >>> Token(42) != Token(10) 253 >>> Token(42) != Token(10)
252 True 254 True
253 >>> Token(42) != Token(42) 255 >>> Token(42) != Token(42)
254 False 256 False
255 - 257 +
256 @param other: second argument of inequality 258 @param other: second argument of inequality
257 @type other: `Token` 259 @type other: `Token`
258 @return: `True` if token values differ, `False` otherwise 260 @return: `True` if token values differ, `False` otherwise
...@@ -261,12 +263,12 @@ class Token (NetElement) : ...@@ -261,12 +263,12 @@ class Token (NetElement) :
261 return not self.__eq__(other) 263 return not self.__eq__(other)
262 def __hash__ (self) : 264 def __hash__ (self) :
263 """Hash a token 265 """Hash a token
264 - 266 +
265 This is done by hashing the value stored in the token 267 This is done by hashing the value stored in the token
266 - 268 +
267 >>> hash(Token(42)) == hash(42) 269 >>> hash(Token(42)) == hash(42)
268 True 270 True
269 - 271 +
270 @return: hash value for the token 272 @return: hash value for the token
271 @rtype: `int` 273 @rtype: `int`
272 """ 274 """
...@@ -274,39 +276,39 @@ class Token (NetElement) : ...@@ -274,39 +276,39 @@ class Token (NetElement) :
274 276
275 class BlackToken (Token) : 277 class BlackToken (Token) :
276 """The usual black token 278 """The usual black token
277 - 279 +
278 >>> BlackToken() 280 >>> BlackToken()
279 dot 281 dot
280 """ 282 """
281 def __init__ (self) : 283 def __init__ (self) :
282 """Initialise the token 284 """Initialise the token
283 - 285 +
284 >>> BlackToken() 286 >>> BlackToken()
285 dot 287 dot
286 """ 288 """
287 self.value = None 289 self.value = None
288 def __repr__ (self) : 290 def __repr__ (self) :
289 """String representation suitable for `eval` 291 """String representation suitable for `eval`
290 - 292 +
291 Since `snakes.nets` sets `dot = BlackToken()`, the string 293 Since `snakes.nets` sets `dot = BlackToken()`, the string
292 representation is `'dot'`. 294 representation is `'dot'`.
293 - 295 +
294 >>> repr(BlackToken()) 296 >>> repr(BlackToken())
295 'dot' 297 'dot'
296 - 298 +
297 @return: precise string representation 299 @return: precise string representation
298 @rtype: `str` 300 @rtype: `str`
299 """ 301 """
300 return "dot" 302 return "dot"
301 def __str__ (self) : 303 def __str__ (self) :
302 """Simple string representation (that of the value) 304 """Simple string representation (that of the value)
303 - 305 +
304 Since `snakes.nets` sets `dot = BlackToken()`, the string 306 Since `snakes.nets` sets `dot = BlackToken()`, the string
305 representation is `'dot'`. 307 representation is `'dot'`.
306 - 308 +
307 >>> str(BlackToken()) 309 >>> str(BlackToken())
308 'dot' 310 'dot'
309 - 311 +
310 @return: simple string representation 312 @return: simple string representation
311 @rtype: `str` 313 @rtype: `str`
312 """ 314 """
...@@ -314,7 +316,7 @@ class BlackToken (Token) : ...@@ -314,7 +316,7 @@ class BlackToken (Token) :
314 __pnmltag__ = "token" 316 __pnmltag__ = "token"
315 def __pnmldump__ (self) : 317 def __pnmldump__ (self) :
316 """Dumps value to PNML tree 318 """Dumps value to PNML tree
317 - 319 +
318 >>> BlackToken().__pnmldump__() 320 >>> BlackToken().__pnmldump__()
319 <?xml version="1.0" encoding="utf-8"?> 321 <?xml version="1.0" encoding="utf-8"?>
320 <pnml> 322 <pnml>
...@@ -325,7 +327,7 @@ class BlackToken (Token) : ...@@ -325,7 +327,7 @@ class BlackToken (Token) :
325 @classmethod 327 @classmethod
326 def __pnmlload__ (cls, tree) : 328 def __pnmlload__ (cls, tree) :
327 """Load from a PNML tree 329 """Load from a PNML tree
328 - 330 +
329 >>> t = BlackToken().__pnmldump__() 331 >>> t = BlackToken().__pnmldump__()
330 >>> BlackToken.__pnmlload__(t) 332 >>> BlackToken.__pnmlload__(t)
331 dot 333 dot
...@@ -341,12 +343,12 @@ tBlackToken = Instance(BlackToken) ...@@ -341,12 +343,12 @@ tBlackToken = Instance(BlackToken)
341 343
342 class ArcAnnotation (NetElement) : 344 class ArcAnnotation (NetElement) :
343 """An annotation on an arc. 345 """An annotation on an arc.
344 - 346 +
345 On input arcs (from a place to a transition) annotations may be 347 On input arcs (from a place to a transition) annotations may be
346 values or variables, potentially nested in tuples. On output arcs 348 values or variables, potentially nested in tuples. On output arcs
347 (from a transition to a place), expressions are allowed, which 349 (from a transition to a place), expressions are allowed, which
348 corresponds to a computation. 350 corresponds to a computation.
349 - 351 +
350 A class attribute `input_allowed` is set to `True` or `False` 352 A class attribute `input_allowed` is set to `True` or `False`
351 according to whether an annotation is allowed on input arcs. 353 according to whether an annotation is allowed on input arcs.
352 """ 354 """
...@@ -356,7 +358,7 @@ class ArcAnnotation (NetElement) : ...@@ -356,7 +358,7 @@ class ArcAnnotation (NetElement) :
356 raise NotImplementedError("abstract method") 358 raise NotImplementedError("abstract method")
357 def substitute (self, binding) : 359 def substitute (self, binding) :
358 """Substitutes the variables in the annotation. 360 """Substitutes the variables in the annotation.
359 - 361 +
360 @param binding: the substitution to apply 362 @param binding: the substitution to apply
361 @type binding: `snakes.data.Substitution` 363 @type binding: `snakes.data.Substitution`
362 """ 364 """
...@@ -364,11 +366,11 @@ class ArcAnnotation (NetElement) : ...@@ -364,11 +366,11 @@ class ArcAnnotation (NetElement) :
364 def replace (self, old, new) : 366 def replace (self, old, new) :
365 """Returns a copy of the annotation in which the `old` annotation 367 """Returns a copy of the annotation in which the `old` annotation
366 has been replaced by the `new` one. 368 has been replaced by the `new` one.
367 - 369 +
368 With non composite annotation, this will return a copy of the 370 With non composite annotation, this will return a copy of the
369 annotation if it is not the same as `old` otherwise it returns 371 annotation if it is not the same as `old` otherwise it returns
370 `new`. Composite annotations will replace there components. 372 `new`. Composite annotations will replace there components.
371 - 373 +
372 >>> Value(2).replace(Variable('x'), Value(5)) 374 >>> Value(2).replace(Variable('x'), Value(5))
373 Value(2) 375 Value(2)
374 >>> Variable('x').replace(Variable('x'), Value(5)) 376 >>> Variable('x').replace(Variable('x'), Value(5))
...@@ -379,7 +381,7 @@ class ArcAnnotation (NetElement) : ...@@ -379,7 +381,7 @@ class ArcAnnotation (NetElement) :
379 Test(Value(2)) 381 Test(Value(2))
380 >>> Test(Variable('x')).replace(Variable('x'), Value(5)) 382 >>> Test(Variable('x')).replace(Variable('x'), Value(5))
381 Test(Value(5)) 383 Test(Value(5))
382 - 384 +
383 @param old: the annotation to be replaced 385 @param old: the annotation to be replaced
384 @type old: `ArcAnnotation` 386 @type old: `ArcAnnotation`
385 @param new: the annotation to use instead of `old` 387 @param new: the annotation to use instead of `old`
...@@ -394,21 +396,21 @@ class ArcAnnotation (NetElement) : ...@@ -394,21 +396,21 @@ class ArcAnnotation (NetElement) :
394 raise NotImplementedError("abstract method") 396 raise NotImplementedError("abstract method")
395 def __eq__ (self, other) : 397 def __eq__ (self, other) :
396 """Check for equality. 398 """Check for equality.
397 - 399 +
398 @param other: the other object to compare with 400 @param other: the other object to compare with
399 @type other: `ArcAnnotation` 401 @type other: `ArcAnnotation`
400 """ 402 """
401 raise NotImplementedError("abstract method") 403 raise NotImplementedError("abstract method")
402 def __hash__ (self) : 404 def __hash__ (self) :
403 """Computes a hash of the annotation 405 """Computes a hash of the annotation
404 - 406 +
405 Warning: must be consistent with equality, ie, two equal 407 Warning: must be consistent with equality, ie, two equal
406 values must have the same hash. 408 values must have the same hash.
407 """ 409 """
408 raise NotImplementedError("abstract method") 410 raise NotImplementedError("abstract method")
409 def __ne__ (self, other) : 411 def __ne__ (self, other) :
410 """Check for difference. 412 """Check for difference.
411 - 413 +
412 @param other: the other object to compare with 414 @param other: the other object to compare with
413 @type other: `ArcAnnotation` 415 @type other: `ArcAnnotation`
414 """ 416 """
...@@ -416,10 +418,10 @@ class ArcAnnotation (NetElement) : ...@@ -416,10 +418,10 @@ class ArcAnnotation (NetElement) :
416 def bind (self, binding) : 418 def bind (self, binding) :
417 """Return the value of the annotation evaluated through 419 """Return the value of the annotation evaluated through
418 `binding`. 420 `binding`.
419 - 421 +
420 >>> Expression('x+1').bind(Substitution(x=2)) 422 >>> Expression('x+1').bind(Substitution(x=2))
421 Token(3) 423 Token(3)
422 - 424 +
423 @param binding: a substitution 425 @param binding: a substitution
424 @type binding: `snakes.data.Substitution` 426 @type binding: `snakes.data.Substitution`
425 @return: a value 427 @return: a value
...@@ -428,10 +430,10 @@ class ArcAnnotation (NetElement) : ...@@ -428,10 +430,10 @@ class ArcAnnotation (NetElement) :
428 def flow (self, binding) : 430 def flow (self, binding) :
429 """Return the flow of tokens implied by the annotation evaluated 431 """Return the flow of tokens implied by the annotation evaluated
430 through `binding`. 432 through `binding`.
431 - 433 +
432 >>> Value(1).flow(Substitution(x=2)) 434 >>> Value(1).flow(Substitution(x=2))
433 MultiSet([1]) 435 MultiSet([1])
434 - 436 +
435 @param binding: a substitution 437 @param binding: a substitution
436 @type binding: `snakes.data.Substitution` 438 @type binding: `snakes.data.Substitution`
437 @return: a multiset of values 439 @return: a multiset of values
...@@ -441,10 +443,10 @@ class ArcAnnotation (NetElement) : ...@@ -441,10 +443,10 @@ class ArcAnnotation (NetElement) :
441 """Return the list of modes under which an arc with the 443 """Return the list of modes under which an arc with the
442 annotation may flow the tokens in `values`. Each mode is a 444 annotation may flow the tokens in `values`. Each mode is a
443 substitution indicating how to bind the annotation. 445 substitution indicating how to bind the annotation.
444 - 446 +
445 >>> Variable('x').modes([1, 2, 3]) 447 >>> Variable('x').modes([1, 2, 3])
446 [Substitution(x=1), Substitution(x=2), Substitution(x=3)] 448 [Substitution(x=1), Substitution(x=2), Substitution(x=3)]
447 - 449 +
448 @param values: a collection of values 450 @param values: a collection of values
449 @type values: any collection like `set`, `list`, `tuple`, ... 451 @type values: any collection like `set`, `list`, `tuple`, ...
450 @return: a list of substitutions 452 @return: a list of substitutions
...@@ -457,17 +459,17 @@ class Value (ArcAnnotation) : ...@@ -457,17 +459,17 @@ class Value (ArcAnnotation) :
457 input_allowed = True 459 input_allowed = True
458 def __init__ (self, value) : 460 def __init__ (self, value) :
459 """Initialise with the encapsulated value. 461 """Initialise with the encapsulated value.
460 - 462 +
461 @param value: the value of the token. 463 @param value: the value of the token.
462 @type value: any object 464 @type value: any object
463 """ 465 """
464 self.value = value 466 self.value = value
465 def copy (self) : 467 def copy (self) :
466 """Return a copy of the value. 468 """Return a copy of the value.
467 - 469 +
468 >>> Value(5).copy() 470 >>> Value(5).copy()
469 Value(5) 471 Value(5)
470 - 472 +
471 @return: a copy of the value 473 @return: a copy of the value
472 @rtype: `Value` 474 @rtype: `Value`
473 """ 475 """
...@@ -475,7 +477,7 @@ class Value (ArcAnnotation) : ...@@ -475,7 +477,7 @@ class Value (ArcAnnotation) :
475 __pnmltag__ = "value" 477 __pnmltag__ = "value"
476 def __pnmldump__ (self) : 478 def __pnmldump__ (self) :
477 """Dump a `Value` as a PNML tree 479 """Dump a `Value` as a PNML tree
478 - 480 +
479 >>> Value(3).__pnmldump__() 481 >>> Value(3).__pnmldump__()
480 <?xml version="1.0" encoding="utf-8"?> 482 <?xml version="1.0" encoding="utf-8"?>
481 <pnml> 483 <pnml>
...@@ -485,7 +487,7 @@ class Value (ArcAnnotation) : ...@@ -485,7 +487,7 @@ class Value (ArcAnnotation) :
485 </object> 487 </object>
486 </value> 488 </value>
487 </pnml> 489 </pnml>
488 - 490 +
489 @return: PNML tree 491 @return: PNML tree
490 @rtype: `pnml.Tree` 492 @rtype: `pnml.Tree`
491 """ 493 """
...@@ -493,11 +495,11 @@ class Value (ArcAnnotation) : ...@@ -493,11 +495,11 @@ class Value (ArcAnnotation) :
493 @classmethod 495 @classmethod
494 def __pnmlload__ (cls, tree) : 496 def __pnmlload__ (cls, tree) :
495 """Create a `Value` from a PNML tree 497 """Create a `Value` from a PNML tree
496 - 498 +
497 >>> t = Value(3).__pnmldump__() 499 >>> t = Value(3).__pnmldump__()
498 >>> Value.__pnmlload__(t) 500 >>> Value.__pnmlload__(t)
499 Value(3) 501 Value(3)
500 - 502 +
501 @param tree: the tree to convert 503 @param tree: the tree to convert
502 @type tree: `pnml.Tree` 504 @type tree: `pnml.Tree`
503 @return: the value built 505 @return: the value built
...@@ -506,14 +508,14 @@ class Value (ArcAnnotation) : ...@@ -506,14 +508,14 @@ class Value (ArcAnnotation) :
506 return cls(tree.child().to_obj()) 508 return cls(tree.child().to_obj())
507 def __str__ (self) : 509 def __str__ (self) :
508 """Concise string representation 510 """Concise string representation
509 - 511 +
510 This is the string representation of the encapsulated value. 512 This is the string representation of the encapsulated value.
511 - 513 +
512 >>> print(str(Value(42))) 514 >>> print(str(Value(42)))
513 42 515 42
514 >>> print(str(Value('hello'))) 516 >>> print(str(Value('hello')))
515 'hello' 517 'hello'
516 - 518 +
517 @return: concise string representation 519 @return: concise string representation
518 @rtype: `str` 520 @rtype: `str`
519 """ 521 """
...@@ -523,36 +525,36 @@ class Value (ArcAnnotation) : ...@@ -523,36 +525,36 @@ class Value (ArcAnnotation) :
523 return str(self.value) 525 return str(self.value)
524 def __repr__ (self) : 526 def __repr__ (self) :
525 """String representation suitable for `eval` 527 """String representation suitable for `eval`
526 - 528 +
527 >>> print(repr(Value(42))) 529 >>> print(repr(Value(42)))
528 Value(42) 530 Value(42)
529 >>> print(repr(Value('hello'))) 531 >>> print(repr(Value('hello')))
530 Value('hello') 532 Value('hello')
531 - 533 +
532 @return: precise string representation 534 @return: precise string representation
533 @rtype: `str` 535 @rtype: `str`
534 """ 536 """
535 return "%s(%s)" % (self.__class__.__name__, repr(self.value)) 537 return "%s(%s)" % (self.__class__.__name__, repr(self.value))
536 def vars (self) : 538 def vars (self) :
537 """Return the list of variables involved in the value (empty). 539 """Return the list of variables involved in the value (empty).
538 - 540 +
539 >>> Value(42).vars() 541 >>> Value(42).vars()
540 [] 542 []
541 - 543 +
542 @return: empty list 544 @return: empty list
543 @rtype: `list` 545 @rtype: `list`
544 """ 546 """
545 return [] 547 return []
546 def __eq__ (self, other) : 548 def __eq__ (self, other) :
547 """Test for equality 549 """Test for equality
548 - 550 +
549 This tests the equality of encapsulated values 551 This tests the equality of encapsulated values
550 - 552 +
551 >>> Value(42) == Value(42) 553 >>> Value(42) == Value(42)
552 True 554 True
553 >>> Value(42) == Value(4) 555 >>> Value(42) == Value(4)
554 False 556 False
555 - 557 +
556 @param other: second operand of the equality 558 @param other: second operand of the equality
557 @type other: `Value` 559 @type other: `Value`
558 @return: `True` if encapsulated values are equal, `False` 560 @return: `True` if encapsulated values are equal, `False`
...@@ -568,10 +570,10 @@ class Value (ArcAnnotation) : ...@@ -568,10 +570,10 @@ class Value (ArcAnnotation) :
568 def bind (self, binding) : 570 def bind (self, binding) :
569 """Return the value evaluated through `binding` (which is the 571 """Return the value evaluated through `binding` (which is the
570 value itself). 572 value itself).
571 - 573 +
572 >>> Value(1).bind(Substitution(x=2)) 574 >>> Value(1).bind(Substitution(x=2))
573 Token(1) 575 Token(1)
574 - 576 +
575 @param binding: a substitution 577 @param binding: a substitution
576 @type binding: `snakes.data.Substitution` 578 @type binding: `snakes.data.Substitution`
577 @return: a value 579 @return: a value
...@@ -580,13 +582,13 @@ class Value (ArcAnnotation) : ...@@ -580,13 +582,13 @@ class Value (ArcAnnotation) :
580 def modes (self, values) : 582 def modes (self, values) :
581 """Return an empty binding (no binding needed for values) iff the 583 """Return an empty binding (no binding needed for values) iff the
582 value is in `values`, raise ModeError otherwise. 584 value is in `values`, raise ModeError otherwise.
583 - 585 +
584 >>> Value(1).modes([1, 2, 3]) 586 >>> Value(1).modes([1, 2, 3])
585 [Substitution()] 587 [Substitution()]
586 >>> try : Value(1).modes([2, 3, 4]) 588 >>> try : Value(1).modes([2, 3, 4])
587 ... except ModeError : print(sys.exc_info()[1]) 589 ... except ModeError : print(sys.exc_info()[1])
588 no match for value 590 no match for value
589 - 591 +
590 @param values: a collection of values 592 @param values: a collection of values
591 @type values: any collection like `set`, `list`, `tuple`, ... 593 @type values: any collection like `set`, `list`, `tuple`, ...
592 @return: a list of substitutions 594 @return: a list of substitutions
...@@ -597,12 +599,12 @@ class Value (ArcAnnotation) : ...@@ -597,12 +599,12 @@ class Value (ArcAnnotation) :
597 raise ModeError("no match for value") 599 raise ModeError("no match for value")
598 def substitute (self, binding) : 600 def substitute (self, binding) :
599 """Bind the value (nothing to do). 601 """Bind the value (nothing to do).
600 - 602 +
601 >>> v = Value(5) 603 >>> v = Value(5)
602 >>> v.substitute(Substitution(x=5)) 604 >>> v.substitute(Substitution(x=5))
603 >>> v 605 >>> v
604 Value(5) 606 Value(5)
605 - 607 +
606 @param binding: a substitution 608 @param binding: a substitution
607 @type binding: `snakes.data.Substitution` 609 @type binding: `snakes.data.Substitution`
608 """ 610 """
...@@ -615,13 +617,13 @@ class Variable (ArcAnnotation) : ...@@ -615,13 +617,13 @@ class Variable (ArcAnnotation) :
615 def __init__ (self, name) : 617 def __init__ (self, name) :
616 """Variable names must start with a letter and may continue with 618 """Variable names must start with a letter and may continue with
617 any alphanumeric character. 619 any alphanumeric character.
618 - 620 +
619 >>> Variable('x') 621 >>> Variable('x')
620 Variable('x') 622 Variable('x')
621 >>> try : Variable('_test') 623 >>> try : Variable('_test')
622 ... except ValueError: print(sys.exc_info()[1]) 624 ... except ValueError: print(sys.exc_info()[1])
623 not a variable name '_test' 625 not a variable name '_test'
624 - 626 +
625 @param name: the name of the variable 627 @param name: the name of the variable
626 @type name: `str` 628 @type name: `str`
627 """ 629 """
...@@ -630,10 +632,10 @@ class Variable (ArcAnnotation) : ...@@ -630,10 +632,10 @@ class Variable (ArcAnnotation) :
630 self.name = name 632 self.name = name
631 def copy (self) : 633 def copy (self) :
632 """Return a copy of the variable. 634 """Return a copy of the variable.
633 - 635 +
634 >>> Variable('x').copy() 636 >>> Variable('x').copy()
635 Variable('x') 637 Variable('x')
636 - 638 +
637 @return: a copy of the variable 639 @return: a copy of the variable
638 @rtype: `Variable` 640 @rtype: `Variable`
639 """ 641 """
...@@ -641,7 +643,7 @@ class Variable (ArcAnnotation) : ...@@ -641,7 +643,7 @@ class Variable (ArcAnnotation) :
641 __pnmltag__ = "variable" 643 __pnmltag__ = "variable"
642 def __pnmldump__ (self) : 644 def __pnmldump__ (self) :
643 """Dump a `Variable` as a PNML tree 645 """Dump a `Variable` as a PNML tree
644 - 646 +
645 >>> Variable('x').__pnmldump__() 647 >>> Variable('x').__pnmldump__()
646 <?xml version="1.0" encoding="utf-8"?> 648 <?xml version="1.0" encoding="utf-8"?>
647 <pnml> 649 <pnml>
...@@ -649,7 +651,7 @@ class Variable (ArcAnnotation) : ...@@ -649,7 +651,7 @@ class Variable (ArcAnnotation) :
649 x 651 x
650 </variable> 652 </variable>
651 </pnml> 653 </pnml>
652 - 654 +
653 @return: PNML tree 655 @return: PNML tree
654 @rtype: `pnml.Tree` 656 @rtype: `pnml.Tree`
655 """ 657 """
...@@ -657,11 +659,11 @@ class Variable (ArcAnnotation) : ...@@ -657,11 +659,11 @@ class Variable (ArcAnnotation) :
657 @classmethod 659 @classmethod
658 def __pnmlload__ (cls, tree) : 660 def __pnmlload__ (cls, tree) :
659 """Create a `Variable` from a PNML tree 661 """Create a `Variable` from a PNML tree
660 - 662 +
661 >>> t = Variable('x').__pnmldump__() 663 >>> t = Variable('x').__pnmldump__()
662 >>> Variable.__pnmlload__(t) 664 >>> Variable.__pnmlload__(t)
663 Variable('x') 665 Variable('x')
664 - 666 +
665 @param tree: the tree to convert 667 @param tree: the tree to convert
666 @type tree: `pnml.Tree` 668 @type tree: `pnml.Tree`
667 @return: the variable built 669 @return: the variable built
...@@ -670,34 +672,34 @@ class Variable (ArcAnnotation) : ...@@ -670,34 +672,34 @@ class Variable (ArcAnnotation) :
670 return cls(tree.data) 672 return cls(tree.data)
671 def rename (self, name) : 673 def rename (self, name) :
672 """Change the name of the variable. 674 """Change the name of the variable.
673 - 675 +
674 >>> v = Variable('x') 676 >>> v = Variable('x')
675 >>> v.rename('y') 677 >>> v.rename('y')
676 >>> v 678 >>> v
677 Variable('y') 679 Variable('y')
678 - 680 +
679 @param name: the new name of the variable 681 @param name: the new name of the variable
680 @type name: `str` 682 @type name: `str`
681 """ 683 """
682 self.__init__(name) 684 self.__init__(name)
683 def __str__ (self) : 685 def __str__ (self) :
684 """Concise string representation 686 """Concise string representation
685 - 687 +
686 This is the name of the variable 688 This is the name of the variable
687 - 689 +
688 >>> str(Variable('x')) 690 >>> str(Variable('x'))
689 'x' 691 'x'
690 - 692 +
691 @return: concise string representation 693 @return: concise string representation
692 @rtype: `str` 694 @rtype: `str`
693 """ 695 """
694 return self.name 696 return self.name
695 def __repr__ (self) : 697 def __repr__ (self) :
696 """String representation suitable for `eval` 698 """String representation suitable for `eval`
697 - 699 +
698 >>> Variable('x') 700 >>> Variable('x')
699 Variable('x') 701 Variable('x')
700 - 702 +
701 @return: precise string representation 703 @return: precise string representation
702 @rtype: `str` 704 @rtype: `str`
703 """ 705 """
...@@ -705,10 +707,10 @@ class Variable (ArcAnnotation) : ...@@ -705,10 +707,10 @@ class Variable (ArcAnnotation) :
705 def modes (self, values) : 707 def modes (self, values) :
706 """Return the the list of substitutions mapping the name of the 708 """Return the the list of substitutions mapping the name of the
707 variable to each value in `values`. 709 variable to each value in `values`.
708 - 710 +
709 >>> Variable('x').modes(range(3)) 711 >>> Variable('x').modes(range(3))
710 [Substitution(x=0), Substitution(x=1), Substitution(x=2)] 712 [Substitution(x=0), Substitution(x=1), Substitution(x=2)]
711 - 713 +
712 @param values: a collection of values 714 @param values: a collection of values
713 @type values: any collection like `set`, `list`, `tuple`, ... 715 @type values: any collection like `set`, `list`, `tuple`, ...
714 @return: a list of substitutions 716 @return: a list of substitutions
...@@ -719,13 +721,13 @@ class Variable (ArcAnnotation) : ...@@ -719,13 +721,13 @@ class Variable (ArcAnnotation) :
719 return result 721 return result
720 def bind (self, binding) : 722 def bind (self, binding) :
721 """Return the value of the variable evaluated through `binding`. 723 """Return the value of the variable evaluated through `binding`.
722 - 724 +
723 >>> Variable('x').bind(Substitution(x=3)) 725 >>> Variable('x').bind(Substitution(x=3))
724 Token(3) 726 Token(3)
725 >>> try : Variable('x').bind(Substitution(z=3)) 727 >>> try : Variable('x').bind(Substitution(z=3))
726 ... except DomainError : print(sys.exc_info()[1]) 728 ... except DomainError : print(sys.exc_info()[1])
727 unbound variable 'x' 729 unbound variable 'x'
728 - 730 +
729 @param binding: a substitution 731 @param binding: a substitution
730 @type binding: `snakes.data.Substitution` 732 @type binding: `snakes.data.Substitution`
731 @return: a value 733 @return: a value
...@@ -733,7 +735,7 @@ class Variable (ArcAnnotation) : ...@@ -733,7 +735,7 @@ class Variable (ArcAnnotation) :
733 return Token(binding[self.name]) 735 return Token(binding[self.name])
734 def substitute (self, binding) : 736 def substitute (self, binding) :
735 """Change the name according to `binding`. 737 """Change the name according to `binding`.
736 - 738 +
737 >>> v = Variable('x') 739 >>> v = Variable('x')
738 >>> v.substitute(Substitution(x='y')) 740 >>> v.substitute(Substitution(x='y'))
739 >>> v 741 >>> v
...@@ -744,29 +746,29 @@ class Variable (ArcAnnotation) : ...@@ -744,29 +746,29 @@ class Variable (ArcAnnotation) :
744 >>> v.rename('z') 746 >>> v.rename('z')
745 >>> v 747 >>> v
746 Variable('z') 748 Variable('z')
747 - 749 +
748 @param binding: a substitution 750 @param binding: a substitution
749 @type binding: `snakes.data.Substitution` 751 @type binding: `snakes.data.Substitution`
750 """ 752 """
751 self.rename(binding(self.name)) 753 self.rename(binding(self.name))
752 def vars (self) : 754 def vars (self) :
753 """Return variable name in a list. 755 """Return variable name in a list.
754 - 756 +
755 >>> Variable('x').vars() 757 >>> Variable('x').vars()
756 ['x'] 758 ['x']
757 - 759 +
758 @return: a list holding the name of the variable 760 @return: a list holding the name of the variable
759 @rtype: `list` holding a single `str` 761 @rtype: `list` holding a single `str`
760 """ 762 """
761 return [self.name] 763 return [self.name]
762 def __eq__ (self, other) : 764 def __eq__ (self, other) :
763 """Test for equality 765 """Test for equality
764 - 766 +
765 >>> Variable('x') == Variable('x') 767 >>> Variable('x') == Variable('x')
766 True 768 True
767 >>> Variable('x') == Variable('y') 769 >>> Variable('x') == Variable('y')
768 False 770 False
769 - 771 +
770 @param other: right operand of equality 772 @param other: right operand of equality
771 @type other: `Variable` 773 @type other: `Variable`
772 @return: `True` if variables have the same name, `False` 774 @return: `True` if variables have the same name, `False`
...@@ -782,14 +784,14 @@ class Variable (ArcAnnotation) : ...@@ -782,14 +784,14 @@ class Variable (ArcAnnotation) :
782 784
783 class Expression (ArcAnnotation) : 785 class Expression (ArcAnnotation) :
784 """An arbitrary Python expression which may be evaluated. 786 """An arbitrary Python expression which may be evaluated.
785 - 787 +
786 Each expression has its private namespace which can be extended or 788 Each expression has its private namespace which can be extended or
787 changed. 789 changed.
788 """ 790 """
789 input_allowed = False 791 input_allowed = False
790 def __init__ (self, expr) : 792 def __init__ (self, expr) :
791 """The expression is compiled so its syntax is checked. 793 """The expression is compiled so its syntax is checked.
792 - 794 +
793 @param expr: a Python expression suitable for `eval` 795 @param expr: a Python expression suitable for `eval`
794 @type expr: `str` 796 @type expr: `str`
795 """ 797 """
...@@ -803,10 +805,10 @@ class Expression (ArcAnnotation) : ...@@ -803,10 +805,10 @@ class Expression (ArcAnnotation) :
803 __pnmltag__ = "expression" 805 __pnmltag__ = "expression"
804 def __pnmldump__ (self) : 806 def __pnmldump__ (self) :
805 """Dump an `Expression` as a PNML tree 807 """Dump an `Expression` as a PNML tree
806 - 808 +
807 @return: PNML tree 809 @return: PNML tree
808 @rtype: `pnml.Tree` 810 @rtype: `pnml.Tree`
809 - 811 +
810 >>> Expression('x+1').__pnmldump__() 812 >>> Expression('x+1').__pnmldump__()
811 <?xml version="1.0" encoding="utf-8"?> 813 <?xml version="1.0" encoding="utf-8"?>
812 <pnml> 814 <pnml>
...@@ -819,12 +821,12 @@ class Expression (ArcAnnotation) : ...@@ -819,12 +821,12 @@ class Expression (ArcAnnotation) :
819 @classmethod 821 @classmethod
820 def __pnmlload__ (cls, tree) : 822 def __pnmlload__ (cls, tree) :
821 """Create an `Expression` from a PNML tree 823 """Create an `Expression` from a PNML tree
822 - 824 +
823 @param tree: the tree to convert 825 @param tree: the tree to convert
824 @type tree: `pnml.Tree` 826 @type tree: `pnml.Tree`
825 @return: the expression built 827 @return: the expression built
826 @rtype: `Expression` 828 @rtype: `Expression`
827 - 829 +
828 >>> t = Expression('x+1').__pnmldump__() 830 >>> t = Expression('x+1').__pnmldump__()
829 >>> Expression.__pnmlload__(t) 831 >>> Expression.__pnmlload__(t)
830 Expression('x+1') 832 Expression('x+1')
...@@ -836,7 +838,7 @@ class Expression (ArcAnnotation) : ...@@ -836,7 +838,7 @@ class Expression (ArcAnnotation) :
836 return "%s(%s)" % (self.__class__.__name__, repr(self._str)) 838 return "%s(%s)" % (self.__class__.__name__, repr(self._str))
837 def bind (self, binding) : 839 def bind (self, binding) :
838 """Evaluate the expression through `binding`. 840 """Evaluate the expression through `binding`.
839 - 841 +
840 >>> e = Expression('x*(y-1)') 842 >>> e = Expression('x*(y-1)')
841 >>> e.bind(Substitution(x=2, y=3)) 843 >>> e.bind(Substitution(x=2, y=3))
842 Token(4) 844 Token(4)
...@@ -845,7 +847,7 @@ class Expression (ArcAnnotation) : ...@@ -845,7 +847,7 @@ class Expression (ArcAnnotation) :
845 ... raise Exception() 847 ... raise Exception()
846 ... except NameError: 848 ... except NameError:
847 ... pass 849 ... pass
848 - 850 +
849 @param binding: a substitution 851 @param binding: a substitution
850 @type binding: `snakes.data.Substitution` 852 @type binding: `snakes.data.Substitution`
851 @return: a value 853 @return: a value
...@@ -861,15 +863,15 @@ class Expression (ArcAnnotation) : ...@@ -861,15 +863,15 @@ class Expression (ArcAnnotation) :
861 return self.bind(binding).value 863 return self.bind(binding).value
862 def substitute (self, binding) : 864 def substitute (self, binding) :
863 """Substitute the variables according to 'binding'. 865 """Substitute the variables according to 'binding'.
864 - 866 +
865 >>> e = Expression('x+1*xy') 867 >>> e = Expression('x+1*xy')
866 >>> e.substitute(Substitution(x='y')) 868 >>> e.substitute(Substitution(x='y'))
867 >>> e 869 >>> e
868 Expression('(y + (1 * xy))') 870 Expression('(y + (1 * xy))')
869 - 871 +
870 As one can see, the substitution adds a lot of parentheses and 872 As one can see, the substitution adds a lot of parentheses and
871 spaces to the expression. 873 spaces to the expression.
872 - 874 +
873 @param binding: a substitution 875 @param binding: a substitution
874 @type binding: `snakes.data.Substitution` 876 @type binding: `snakes.data.Substitution`
875 """ 877 """
...@@ -879,30 +881,30 @@ class Expression (ArcAnnotation) : ...@@ -879,30 +881,30 @@ class Expression (ArcAnnotation) :
879 self._str = expr.strip() 881 self._str = expr.strip()
880 def vars (self) : 882 def vars (self) :
881 """Return the list of variable names involved in the expression. 883 """Return the list of variable names involved in the expression.
882 - 884 +
883 >>> list(sorted(Expression('x+y').vars())) 885 >>> list(sorted(Expression('x+y').vars()))
884 ['x', 'y'] 886 ['x', 'y']
885 - 887 +
886 @return: a list holding the names of the variables 888 @return: a list holding the names of the variables
887 @rtype: `list` of `str` 889 @rtype: `list` of `str`
888 """ 890 """
889 return list(n for n in getvars(self._str) if n not in self.globals) 891 return list(n for n in getvars(self._str) if n not in self.globals)
890 def __and__ (self, other) : 892 def __and__ (self, other) :
891 """Implement `&` to perform `and` between two expressions. 893 """Implement `&` to perform `and` between two expressions.
892 - 894 +
893 It is not checked whether we combine boolean expressions or 895 It is not checked whether we combine boolean expressions or
894 not. 896 not.
895 - 897 +
896 >>> Expression('x==1') & Expression('y==2') 898 >>> Expression('x==1') & Expression('y==2')
897 Expression('(x==1) and (y==2)') 899 Expression('(x==1) and (y==2)')
898 - 900 +
899 Minor optimisation are implemented: 901 Minor optimisation are implemented:
900 - 902 +
901 >>> Expression('True') & Expression('x==y') 903 >>> Expression('True') & Expression('x==y')
902 Expression('x==y') 904 Expression('x==y')
903 >>> Expression('x==y') & Expression('True') 905 >>> Expression('x==y') & Expression('True')
904 Expression('x==y') 906 Expression('x==y')
905 - 907 +
906 @param other: an expression 908 @param other: an expression
907 @type other: `snakes.nets.Expression` 909 @type other: `snakes.nets.Expression`
908 @return: an expression 910 @return: an expression
...@@ -919,20 +921,20 @@ class Expression (ArcAnnotation) : ...@@ -919,20 +921,20 @@ class Expression (ArcAnnotation) :
919 return result 921 return result
920 def __or__ (self, other) : 922 def __or__ (self, other) :
921 """Implement `|` to perform `or` between two expressions. 923 """Implement `|` to perform `or` between two expressions.
922 - 924 +
923 It is not checked whether we combine boolean expressions or 925 It is not checked whether we combine boolean expressions or
924 not. 926 not.
925 - 927 +
926 >>> Expression('x==1') | Expression('y==2') 928 >>> Expression('x==1') | Expression('y==2')
927 Expression('(x==1) or (y==2)') 929 Expression('(x==1) or (y==2)')
928 - 930 +
929 Minor optimisation are implemented: 931 Minor optimisation are implemented:
930 - 932 +
931 >>> Expression('True') | Expression('x==y') 933 >>> Expression('True') | Expression('x==y')
932 Expression('True') 934 Expression('True')
933 >>> Expression('x==y') | Expression('True') 935 >>> Expression('x==y') | Expression('True')
934 Expression('True') 936 Expression('True')
935 - 937 +
936 @param other: an expression 938 @param other: an expression
937 @type other: `snakes.nets.Expression` 939 @type other: `snakes.nets.Expression`
938 @return: an expression 940 @return: an expression
...@@ -947,20 +949,20 @@ class Expression (ArcAnnotation) : ...@@ -947,20 +949,20 @@ class Expression (ArcAnnotation) :
947 return result 949 return result
948 def __invert__ (self) : 950 def __invert__ (self) :
949 """Implement `~` to perform `not`. 951 """Implement `~` to perform `not`.
950 - 952 +
951 It is not checked whether we negate a boolean expression or 953 It is not checked whether we negate a boolean expression or
952 not. 954 not.
953 - 955 +
954 >>> ~Expression('x==1') 956 >>> ~Expression('x==1')
955 Expression('not (x==1)') 957 Expression('not (x==1)')
956 - 958 +
957 Minor optimisation are implemented: 959 Minor optimisation are implemented:
958 - 960 +
959 >>> ~Expression('True') 961 >>> ~Expression('True')
960 Expression('False') 962 Expression('False')
961 >>> ~Expression('False') 963 >>> ~Expression('False')
962 Expression('True') 964 Expression('True')
963 - 965 +
964 @return: an expression 966 @return: an expression
965 @rtype: `snakes.nets.Expression` 967 @rtype: `snakes.nets.Expression`
966 """ 968 """
...@@ -980,14 +982,14 @@ class Expression (ArcAnnotation) : ...@@ -980,14 +982,14 @@ class Expression (ArcAnnotation) :
980 982
981 def let (**args) : 983 def let (**args) :
982 """A dirty trick to allow side effects in expressions 984 """A dirty trick to allow side effects in expressions
983 - 985 +
984 Assignement takes place when method `bind` of the expression is 986 Assignement takes place when method `bind` of the expression is
985 called. Assigned variables are then stored in the `Substitution` 987 called. Assigned variables are then stored in the `Substitution`
986 passed to `bind`. 988 passed to `bind`.
987 - 989 +
988 Use with care: sides effects are nasty tricks, you may get 990 Use with care: sides effects are nasty tricks, you may get
989 unexpected results while playing with `let`. 991 unexpected results while playing with `let`.
990 - 992 +
991 >>> n = PetriNet('n') 993 >>> n = PetriNet('n')
992 >>> n.globals['let'] = let 994 >>> n.globals['let'] = let
993 >>> t = Transition('t', Expression('x is not None and let(foo=42)')) 995 >>> t = Transition('t', Expression('x is not None and let(foo=42)'))
...@@ -996,7 +998,7 @@ def let (**args) : ...@@ -996,7 +998,7 @@ def let (**args) :
996 >>> n.add_input('p', 't', Variable('x')) 998 >>> n.add_input('p', 't', Variable('x'))
997 >>> t.modes() == [Substitution(x=dot, foo=42)] 999 >>> t.modes() == [Substitution(x=dot, foo=42)]
998 True 1000 True
999 - 1001 +
1000 @param args: a list of `name=value` to assign 1002 @param args: a list of `name=value` to assign
1001 @return: `True` if assignment could be made, `False` otherwise 1003 @return: `True` if assignment could be made, `False` otherwise
1002 @rtype: `bool` 1004 @rtype: `bool`
...@@ -1012,21 +1014,21 @@ def let (**args) : ...@@ -1012,21 +1014,21 @@ def let (**args) :
1012 class MultiArc (ArcAnnotation) : 1014 class MultiArc (ArcAnnotation) :
1013 """A collection of other annotations, allowing to consume or produce 1015 """A collection of other annotations, allowing to consume or produce
1014 several tokens at once. 1016 several tokens at once.
1015 - 1017 +
1016 Such a collection is allowed on input arcs only if so are all its 1018 Such a collection is allowed on input arcs only if so are all its
1017 components. 1019 components.
1018 """ 1020 """
1019 input_allowed = False 1021 input_allowed = False
1020 def __init__ (self, components) : 1022 def __init__ (self, components) :
1021 """Initialise with the components of the tuple. 1023 """Initialise with the components of the tuple.
1022 - 1024 +
1023 >>> MultiArc((Value(1), Expression('x+1'))) 1025 >>> MultiArc((Value(1), Expression('x+1')))
1024 MultiArc((Value(1), Expression('x+1'))) 1026 MultiArc((Value(1), Expression('x+1')))
1025 >>> MultiArc((Value(1), Expression('x+1'))).input_allowed 1027 >>> MultiArc((Value(1), Expression('x+1'))).input_allowed
1026 False 1028 False
1027 >>> MultiArc((Value(1), Variable('x'))).input_allowed 1029 >>> MultiArc((Value(1), Variable('x'))).input_allowed
1028 True 1030 True
1029 - 1031 +
1030 @param components: a list of components 1032 @param components: a list of components
1031 @type components: an iterable collection of `ArcAnnotation` 1033 @type components: an iterable collection of `ArcAnnotation`
1032 """ 1034 """
...@@ -1046,10 +1048,10 @@ class MultiArc (ArcAnnotation) : ...@@ -1046,10 +1048,10 @@ class MultiArc (ArcAnnotation) :
1046 __pnmltag__ = "multiarc" 1048 __pnmltag__ = "multiarc"
1047 def __pnmldump__ (self) : 1049 def __pnmldump__ (self) :
1048 """Dump a `MultiArc` as a PNML tree 1050 """Dump a `MultiArc` as a PNML tree
1049 - 1051 +
1050 @return: PNML tree 1052 @return: PNML tree
1051 @rtype: `pnml.Tree` 1053 @rtype: `pnml.Tree`
1052 - 1054 +
1053 >>> MultiArc([Value(3), Expression('x+1')]).__pnmldump__() 1055 >>> MultiArc([Value(3), Expression('x+1')]).__pnmldump__()
1054 <?xml version="1.0" encoding="utf-8"?> 1056 <?xml version="1.0" encoding="utf-8"?>
1055 <pnml> 1057 <pnml>
...@@ -1070,12 +1072,12 @@ class MultiArc (ArcAnnotation) : ...@@ -1070,12 +1072,12 @@ class MultiArc (ArcAnnotation) :
1070 @classmethod 1072 @classmethod
1071 def __pnmlload__ (cls, tree) : 1073 def __pnmlload__ (cls, tree) :
1072 """Create a `MultiArc` from a PNML tree 1074 """Create a `MultiArc` from a PNML tree
1073 - 1075 +
1074 @param tree: the tree to convert 1076 @param tree: the tree to convert
1075 @type tree: `pnml.Tree` 1077 @type tree: `pnml.Tree`
1076 @return: the multiarc built 1078 @return: the multiarc built
1077 @rtype: `MultiArc` 1079 @rtype: `MultiArc`
1078 - 1080 +
1079 >>> t = MultiArc((Value(3), Expression('x+1'))).__pnmldump__() 1081 >>> t = MultiArc((Value(3), Expression('x+1'))).__pnmldump__()
1080 >>> MultiArc.__pnmlload__(t) 1082 >>> MultiArc.__pnmlload__(t)
1081 MultiArc((Value(3), Expression('x+1'))) 1083 MultiArc((Value(3), Expression('x+1')))
...@@ -1133,11 +1135,11 @@ class MultiArc (ArcAnnotation) : ...@@ -1133,11 +1135,11 @@ class MultiArc (ArcAnnotation) :
1133 def bind (self, binding) : 1135 def bind (self, binding) :
1134 """Return the value of the annotation evaluated through 1136 """Return the value of the annotation evaluated through
1135 `binding`. 1137 `binding`.
1136 - 1138 +
1137 >>> t = MultiArc((Value(1), Variable('x'), Variable('y'))) 1139 >>> t = MultiArc((Value(1), Variable('x'), Variable('y')))
1138 >>> t.bind(Substitution(x=2, y=3)) 1140 >>> t.bind(Substitution(x=2, y=3))
1139 (Token(1), Token(2), Token(3)) 1141 (Token(1), Token(2), Token(3))
1140 - 1142 +
1141 @param binding: a substitution 1143 @param binding: a substitution
1142 @type binding: `snakes.data.Substitution` 1144 @type binding: `snakes.data.Substitution`
1143 @return: a tuple of value 1145 @return: a tuple of value
...@@ -1147,7 +1149,7 @@ class MultiArc (ArcAnnotation) : ...@@ -1147,7 +1149,7 @@ class MultiArc (ArcAnnotation) :
1147 """Return the list of modes under which an arc with the 1149 """Return the list of modes under which an arc with the
1148 annotation may flow the tokens in `values`. Each mode is a 1150 annotation may flow the tokens in `values`. Each mode is a
1149 substitution indicating how to bind the annotation. 1151 substitution indicating how to bind the annotation.
1150 - 1152 +
1151 >>> t = MultiArc((Value(1), Variable('x'), Variable('y'))) 1153 >>> t = MultiArc((Value(1), Variable('x'), Variable('y')))
1152 >>> m = t.modes([1, 2, 3]) 1154 >>> m = t.modes([1, 2, 3])
1153 >>> len(m) 1155 >>> len(m)
...@@ -1156,7 +1158,7 @@ class MultiArc (ArcAnnotation) : ...@@ -1156,7 +1158,7 @@ class MultiArc (ArcAnnotation) :
1156 True 1158 True
1157 >>> Substitution(y=2, x=3) in m 1159 >>> Substitution(y=2, x=3) in m
1158 True 1160 True
1159 - 1161 +
1160 @param values: a collection of values 1162 @param values: a collection of values
1161 @type values: any collection like `set`, `list`, `tuple`, ... 1163 @type values: any collection like `set`, `list`, `tuple`, ...
1162 @return: a list of substitutions 1164 @return: a list of substitutions
...@@ -1179,12 +1181,12 @@ class MultiArc (ArcAnnotation) : ...@@ -1179,12 +1181,12 @@ class MultiArc (ArcAnnotation) :
1179 return result 1181 return result
1180 def substitute (self, binding) : 1182 def substitute (self, binding) :
1181 """Substitute each component according to 'binding'. 1183 """Substitute each component according to 'binding'.
1182 - 1184 +
1183 >>> t = MultiArc((Value(1), Variable('x'), Variable('y'))) 1185 >>> t = MultiArc((Value(1), Variable('x'), Variable('y')))
1184 >>> t.substitute(Substitution(x='z')) 1186 >>> t.substitute(Substitution(x='z'))
1185 >>> t 1187 >>> t
1186 MultiArc((Value(1), Variable('z'), Variable('y'))) 1188 MultiArc((Value(1), Variable('z'), Variable('y')))
1187 - 1189 +
1188 @param binding: a substitution 1190 @param binding: a substitution
1189 @type binding: `snakes.data.Substitution` 1191 @type binding: `snakes.data.Substitution`
1190 """ 1192 """
...@@ -1192,11 +1194,11 @@ class MultiArc (ArcAnnotation) : ...@@ -1192,11 +1194,11 @@ class MultiArc (ArcAnnotation) :
1192 x.substitute(binding) 1194 x.substitute(binding)
1193 def vars (self) : 1195 def vars (self) :
1194 """Return the list of variables involved in the components. 1196 """Return the list of variables involved in the components.
1195 - 1197 +
1196 >>> t = MultiArc((Value(1), Variable('x'), Variable('y'))) 1198 >>> t = MultiArc((Value(1), Variable('x'), Variable('y')))
1197 >>> list(sorted(t.vars())) 1199 >>> list(sorted(t.vars()))
1198 ['x', 'y'] 1200 ['x', 'y']
1199 - 1201 +
1200 @return: the set of variables 1202 @return: the set of variables
1201 @rtype: `set` 1203 @rtype: `set`
1202 """ 1204 """
...@@ -1207,9 +1209,9 @@ class MultiArc (ArcAnnotation) : ...@@ -1207,9 +1209,9 @@ class MultiArc (ArcAnnotation) :
1207 def replace (self, old, new) : 1209 def replace (self, old, new) :
1208 """Returns a copy of the annotation in which the `old` annotation 1210 """Returns a copy of the annotation in which the `old` annotation
1209 has been replaced by the `new` one. 1211 has been replaced by the `new` one.
1210 - 1212 +
1211 With `MultiArc`, replaces each occurrence of `old` by `new` 1213 With `MultiArc`, replaces each occurrence of `old` by `new`
1212 - 1214 +
1213 @param old: the annotation to be replaced 1215 @param old: the annotation to be replaced
1214 @type old: `ArcAnnotation` 1216 @type old: `ArcAnnotation`
1215 @param new: the annotation to use instead of `old` 1217 @param new: the annotation to use instead of `old`
...@@ -1276,11 +1278,11 @@ class Tuple (MultiArc) : ...@@ -1276,11 +1278,11 @@ class Tuple (MultiArc) :
1276 def bind (self, binding) : 1278 def bind (self, binding) :
1277 """Return the value of the annotation evaluated through 1279 """Return the value of the annotation evaluated through
1278 `binding`. 1280 `binding`.
1279 - 1281 +
1280 >>> t = Tuple((Value(1), Variable('x'), Variable('y'))) 1282 >>> t = Tuple((Value(1), Variable('x'), Variable('y')))
1281 >>> t.bind(Substitution(x=2, y=3)) 1283 >>> t.bind(Substitution(x=2, y=3))
1282 Token((1, 2, 3)) 1284 Token((1, 2, 3))
1283 - 1285 +
1284 @param binding: a substitution 1286 @param binding: a substitution
1285 @type binding: `snakes.data.Substitution` 1287 @type binding: `snakes.data.Substitution`
1286 @return: a tuple of value 1288 @return: a tuple of value
...@@ -1312,10 +1314,10 @@ class Test (ArcAnnotation) : ...@@ -1312,10 +1314,10 @@ class Test (ArcAnnotation) :
1312 __pnmltag__ = "test" 1314 __pnmltag__ = "test"
1313 def __pnmldump__ (self) : 1315 def __pnmldump__ (self) :
1314 """Dump a `Test` as a PNML tree 1316 """Dump a `Test` as a PNML tree
1315 - 1317 +
1316 @return: PNML tree 1318 @return: PNML tree
1317 @rtype: `pnml.Tree` 1319 @rtype: `pnml.Tree`
1318 - 1320 +
1319 >>> Test(Value(3)).__pnmldump__() 1321 >>> Test(Value(3)).__pnmldump__()
1320 <?xml version="1.0" encoding="utf-8"?> 1322 <?xml version="1.0" encoding="utf-8"?>
1321 <pnml> 1323 <pnml>
...@@ -1332,12 +1334,12 @@ class Test (ArcAnnotation) : ...@@ -1332,12 +1334,12 @@ class Test (ArcAnnotation) :
1332 @classmethod 1334 @classmethod
1333 def __pnmlload__ (cls, tree) : 1335 def __pnmlload__ (cls, tree) :
1334 """Create a `Test` from a PNML tree 1336 """Create a `Test` from a PNML tree
1335 - 1337 +
1336 @param tree: the tree to convert 1338 @param tree: the tree to convert
1337 @type tree: `pnml.Tree` 1339 @type tree: `pnml.Tree`
1338 @return: the test arc built 1340 @return: the test arc built
1339 @rtype: `Test` 1341 @rtype: `Test`
1340 - 1342 +
1341 >>> t = Test(Value(3)).__pnmldump__() 1343 >>> t = Test(Value(3)).__pnmldump__()
1342 >>> Test.__pnmlload__(t) 1344 >>> Test.__pnmlload__(t)
1343 Test(Value(3)) 1345 Test(Value(3))
...@@ -1369,7 +1371,7 @@ class Test (ArcAnnotation) : ...@@ -1369,7 +1371,7 @@ class Test (ArcAnnotation) :
1369 return "%s(%s)" % (self.__class__.__name__, repr(self._annotation)) 1371 return "%s(%s)" % (self.__class__.__name__, repr(self._annotation))
1370 def substitute (self, binding) : 1372 def substitute (self, binding) :
1371 """Substitutes the variables in the annotation. 1373 """Substitutes the variables in the annotation.
1372 - 1374 +
1373 @param binding: the substitution to apply 1375 @param binding: the substitution to apply
1374 @type binding: `snakes.data.Substitution` 1376 @type binding: `snakes.data.Substitution`
1375 """ 1377 """
...@@ -1388,10 +1390,10 @@ class Test (ArcAnnotation) : ...@@ -1388,10 +1390,10 @@ class Test (ArcAnnotation) :
1388 def bind (self, binding) : 1390 def bind (self, binding) :
1389 """Return the value of the annotation evaluated through 1391 """Return the value of the annotation evaluated through
1390 `binding`. 1392 `binding`.
1391 - 1393 +
1392 >>> Test(Expression('x+1')).bind(Substitution(x=2)) 1394 >>> Test(Expression('x+1')).bind(Substitution(x=2))
1393 Token(3) 1395 Token(3)
1394 - 1396 +
1395 @param binding: a substitution 1397 @param binding: a substitution
1396 @type binding: `snakes.data.Substitution` 1398 @type binding: `snakes.data.Substitution`
1397 @return: a value 1399 @return: a value
...@@ -1400,10 +1402,10 @@ class Test (ArcAnnotation) : ...@@ -1400,10 +1402,10 @@ class Test (ArcAnnotation) :
1400 def flow (self, binding) : 1402 def flow (self, binding) :
1401 """Return the flow of tokens implied by the annotation evaluated 1403 """Return the flow of tokens implied by the annotation evaluated
1402 through `binding`. 1404 through `binding`.
1403 - 1405 +
1404 >>> Test(Value(1)).flow(Substitution()) 1406 >>> Test(Value(1)).flow(Substitution())
1405 MultiSet([]) 1407 MultiSet([])
1406 - 1408 +
1407 @param binding: a substitution 1409 @param binding: a substitution
1408 @type binding: `snakes.data.Substitution` 1410 @type binding: `snakes.data.Substitution`
1409 @return: an empty multiset 1411 @return: an empty multiset
...@@ -1413,10 +1415,10 @@ class Test (ArcAnnotation) : ...@@ -1413,10 +1415,10 @@ class Test (ArcAnnotation) :
1413 """Return the list of modes under which an arc with the 1415 """Return the list of modes under which an arc with the
1414 annotation may flow the tokens in `values`. Each mode is a 1416 annotation may flow the tokens in `values`. Each mode is a
1415 substitution indicating how to bind the annotation. 1417 substitution indicating how to bind the annotation.
1416 - 1418 +
1417 >>> Test(Variable('x')).modes([1, 2, 3]) 1419 >>> Test(Variable('x')).modes([1, 2, 3])
1418 [Substitution(x=1), Substitution(x=2), Substitution(x=3)] 1420 [Substitution(x=1), Substitution(x=2), Substitution(x=3)]
1419 - 1421 +
1420 @param values: a collection of values 1422 @param values: a collection of values
1421 @type values: any collection like `set`, `list`, `tuple`, ... 1423 @type values: any collection like `set`, `list`, `tuple`, ...
1422 @return: a list of substitutions 1424 @return: a list of substitutions
...@@ -1425,9 +1427,9 @@ class Test (ArcAnnotation) : ...@@ -1425,9 +1427,9 @@ class Test (ArcAnnotation) :
1425 def replace (self, old, new) : 1427 def replace (self, old, new) :
1426 """Returns a copy of the annotation in which the `old` annotation 1428 """Returns a copy of the annotation in which the `old` annotation
1427 has been replaced by the `new` one. 1429 has been replaced by the `new` one.
1428 - 1430 +
1429 With `Test`, always returns `Test(new)` 1431 With `Test`, always returns `Test(new)`
1430 - 1432 +
1431 @param old: the annotation to be replaced 1433 @param old: the annotation to be replaced
1432 @type old: `ArcAnnotation` 1434 @type old: `ArcAnnotation`
1433 @param new: the annotation to use instead of `old` 1435 @param new: the annotation to use instead of `old`
...@@ -1451,10 +1453,10 @@ class Inhibitor (Test) : ...@@ -1451,10 +1453,10 @@ class Inhibitor (Test) :
1451 __pnmltag__ = "inhibitor" 1453 __pnmltag__ = "inhibitor"
1452 def __pnmldump__ (self) : 1454 def __pnmldump__ (self) :
1453 """Dump a `Inhibitor` as a PNML tree 1455 """Dump a `Inhibitor` as a PNML tree
1454 - 1456 +
1455 @return: PNML tree 1457 @return: PNML tree
1456 @rtype: `pnml.Tree` 1458 @rtype: `pnml.Tree`
1457 - 1459 +
1458 >>> Inhibitor(Value(3)).__pnmldump__() 1460 >>> Inhibitor(Value(3)).__pnmldump__()
1459 <?xml version="1.0" encoding="utf-8"?> 1461 <?xml version="1.0" encoding="utf-8"?>
1460 <pnml> 1462 <pnml>
...@@ -1480,12 +1482,12 @@ class Inhibitor (Test) : ...@@ -1480,12 +1482,12 @@ class Inhibitor (Test) :
1480 @classmethod 1482 @classmethod
1481 def __pnmlload__ (cls, tree) : 1483 def __pnmlload__ (cls, tree) :
1482 """Create a `Inhibitor` from a PNML tree 1484 """Create a `Inhibitor` from a PNML tree
1483 - 1485 +
1484 @param tree: the tree to convert 1486 @param tree: the tree to convert
1485 @type tree: `pnml.Tree` 1487 @type tree: `pnml.Tree`
1486 @return: the inhibitor arc built 1488 @return: the inhibitor arc built
1487 @rtype: `Inhibitor` 1489 @rtype: `Inhibitor`
1488 - 1490 +
1489 >>> t = Inhibitor(Value(3)).__pnmldump__() 1491 >>> t = Inhibitor(Value(3)).__pnmldump__()
1490 >>> Inhibitor.__pnmlload__(t) 1492 >>> Inhibitor.__pnmlload__(t)
1491 Inhibitor(Value(3)) 1493 Inhibitor(Value(3))
...@@ -1520,7 +1522,7 @@ class Inhibitor (Test) : ...@@ -1520,7 +1522,7 @@ class Inhibitor (Test) :
1520 self._annotation, self._condition) 1522 self._annotation, self._condition)
1521 def substitute (self, binding) : 1523 def substitute (self, binding) :
1522 """Substitutes the variables in the annotation. 1524 """Substitutes the variables in the annotation.
1523 - 1525 +
1524 @param binding: the substitution to apply 1526 @param binding: the substitution to apply
1525 @type binding: `snakes.data.Substitution` 1527 @type binding: `snakes.data.Substitution`
1526 """ 1528 """
...@@ -1542,13 +1544,13 @@ class Inhibitor (Test) : ...@@ -1542,13 +1544,13 @@ class Inhibitor (Test) :
1542 """Return the value of the annotation evaluated through 1544 """Return the value of the annotation evaluated through
1543 `binding`, or raise `ValueError` of this binding does not 1545 `binding`, or raise `ValueError` of this binding does not
1544 validate the condition. 1546 validate the condition.
1545 - 1547 +
1546 >>> Inhibitor(Expression('x+1'), Expression('x>0')).bind(Substitution(x=2)) 1548 >>> Inhibitor(Expression('x+1'), Expression('x>0')).bind(Substitution(x=2))
1547 Token(3) 1549 Token(3)
1548 >>> try : Inhibitor(Expression('x+1'), Expression('x<0')).bind(Substitution(x=2)) 1550 >>> try : Inhibitor(Expression('x+1'), Expression('x<0')).bind(Substitution(x=2))
1549 ... except ValueError : print(sys.exc_info()[1]) 1551 ... except ValueError : print(sys.exc_info()[1])
1550 condition not True for {x -> 2} 1552 condition not True for {x -> 2}
1551 - 1553 +
1552 @param binding: a substitution 1554 @param binding: a substitution
1553 @type binding: `snakes.data.Substitution` 1555 @type binding: `snakes.data.Substitution`
1554 @return: a value 1556 @return: a value
...@@ -1561,7 +1563,7 @@ class Inhibitor (Test) : ...@@ -1561,7 +1563,7 @@ class Inhibitor (Test) :
1561 """Return the list of modes under which an arc with the 1563 """Return the list of modes under which an arc with the
1562 annotation may flow the tokens in `values`. Each mode is a 1564 annotation may flow the tokens in `values`. Each mode is a
1563 substitution indicating how to bind the annotation. 1565 substitution indicating how to bind the annotation.
1564 - 1566 +
1565 >>> try : Inhibitor(Value(1)).modes([1, 2, 3]) 1567 >>> try : Inhibitor(Value(1)).modes([1, 2, 3])
1566 ... except ModeError : print(sys.exc_info()[1]) 1568 ... except ModeError : print(sys.exc_info()[1])
1567 inhibited by {} 1569 inhibited by {}
...@@ -1579,7 +1581,7 @@ class Inhibitor (Test) : ...@@ -1579,7 +1581,7 @@ class Inhibitor (Test) :
1579 >>> Inhibitor(MultiArc([Variable('x'), Variable('y')]), 1581 >>> Inhibitor(MultiArc([Variable('x'), Variable('y')]),
1580 ... Expression('x==y')).modes([1, 2, 3]) 1582 ... Expression('x==y')).modes([1, 2, 3])
1581 [Substitution()] 1583 [Substitution()]
1582 - 1584 +
1583 @param values: a collection of values 1585 @param values: a collection of values
1584 @type values: any collection like `set`, `list`, `tuple`, ... 1586 @type values: any collection like `set`, `list`, `tuple`, ...
1585 @return: a list of substitutions 1587 @return: a list of substitutions
...@@ -1595,7 +1597,7 @@ class Inhibitor (Test) : ...@@ -1595,7 +1597,7 @@ class Inhibitor (Test) :
1595 def replace (self, old, new) : 1597 def replace (self, old, new) :
1596 """Returns a copy of the annotation in which the `old` annotation 1598 """Returns a copy of the annotation in which the `old` annotation
1597 has been replaced by the `new` one. 1599 has been replaced by the `new` one.
1598 - 1600 +
1599 @param old: the annotation to be replaced 1601 @param old: the annotation to be replaced
1600 @type old: `ArcAnnotation` 1602 @type old: `ArcAnnotation`
1601 @param new: the annotation to use instead of `old` 1603 @param new: the annotation to use instead of `old`
...@@ -1666,13 +1668,13 @@ class Flush (ArcAnnotation) : ...@@ -1666,13 +1668,13 @@ class Flush (ArcAnnotation) :
1666 """Return the list of modes under which an arc with the 1668 """Return the list of modes under which an arc with the
1667 annotation may flow the tokens in `values`. Each mode is a 1669 annotation may flow the tokens in `values`. Each mode is a
1668 substitution indicating how to bind the annotation. 1670 substitution indicating how to bind the annotation.
1669 - 1671 +
1670 >>> m = Flush('x').modes([1, 2, 3]) 1672 >>> m = Flush('x').modes([1, 2, 3])
1671 >>> m 1673 >>> m
1672 [Substitution(x=MultiSet([...]))] 1674 [Substitution(x=MultiSet([...]))]
1673 >>> m[0]['x'] == MultiSet([1, 2, 3]) 1675 >>> m[0]['x'] == MultiSet([1, 2, 3])
1674 True 1676 True
1675 - 1677 +
1676 @param values: a collection of values 1678 @param values: a collection of values
1677 @type values: any collection like `set`, `list`, `tuple`, ... 1679 @type values: any collection like `set`, `list`, `tuple`, ...
1678 @return: a list of substitutions 1680 @return: a list of substitutions
...@@ -1681,10 +1683,10 @@ class Flush (ArcAnnotation) : ...@@ -1681,10 +1683,10 @@ class Flush (ArcAnnotation) :
1681 def flow (self, binding) : 1683 def flow (self, binding) :
1682 """Return the flow of tokens implied by the annotation evaluated 1684 """Return the flow of tokens implied by the annotation evaluated
1683 through `binding`. 1685 through `binding`.
1684 - 1686 +
1685 >>> Flush('x').flow(Substitution(x=MultiSet([1, 2, 3]))) == MultiSet([1, 2, 3]) 1687 >>> Flush('x').flow(Substitution(x=MultiSet([1, 2, 3]))) == MultiSet([1, 2, 3])
1686 True 1688 True
1687 - 1689 +
1688 @param binding: a substitution 1690 @param binding: a substitution
1689 @type binding: `snakes.data.Substitution` 1691 @type binding: `snakes.data.Substitution`
1690 @return: a multiset 1692 @return: a multiset
...@@ -1693,10 +1695,10 @@ class Flush (ArcAnnotation) : ...@@ -1693,10 +1695,10 @@ class Flush (ArcAnnotation) :
1693 def bind (self, binding) : 1695 def bind (self, binding) :
1694 """Return the value of the annotation evaluated through 1696 """Return the value of the annotation evaluated through
1695 `binding`. 1697 `binding`.
1696 - 1698 +
1697 >>> set(Flush('x').bind(Substitution(x=MultiSet([1, 2, 3])))) == set([Token(1), Token(2), Token(3)]) 1699 >>> set(Flush('x').bind(Substitution(x=MultiSet([1, 2, 3])))) == set([Token(1), Token(2), Token(3)])
1698 True 1700 True
1699 - 1701 +
1700 @param binding: a substitution 1702 @param binding: a substitution
1701 @type binding: `snakes.data.Substitution` 1703 @type binding: `snakes.data.Substitution`
1702 @return: a value 1704 @return: a value
...@@ -1704,10 +1706,10 @@ class Flush (ArcAnnotation) : ...@@ -1704,10 +1706,10 @@ class Flush (ArcAnnotation) :
1704 return tuple(Token(v) for v in self._annotation.bind(binding).value) 1706 return tuple(Token(v) for v in self._annotation.bind(binding).value)
1705 def vars (self) : 1707 def vars (self) :
1706 """Return the list of variable names involved in the arc. 1708 """Return the list of variable names involved in the arc.
1707 - 1709 +
1708 >>> Flush('x').vars() 1710 >>> Flush('x').vars()
1709 ['x'] 1711 ['x']
1710 - 1712 +
1711 @return: list of variable names 1713 @return: list of variable names
1712 @rtype: `list` of `str` 1714 @rtype: `list` of `str`
1713 """ 1715 """
...@@ -1719,13 +1721,13 @@ class Flush (ArcAnnotation) : ...@@ -1719,13 +1721,13 @@ class Flush (ArcAnnotation) :
1719 1721
1720 class Node (NetElement) : 1722 class Node (NetElement) :
1721 """A node in a Petri net. 1723 """A node in a Petri net.
1722 - 1724 +
1723 This class is abstract and should not be instanciated, use `Place` 1725 This class is abstract and should not be instanciated, use `Place`
1724 or `Transition` as concrete nodes. 1726 or `Transition` as concrete nodes.
1725 """ 1727 """
1726 def rename (self, name) : 1728 def rename (self, name) :
1727 """Change the name of the node. 1729 """Change the name of the node.
1728 - 1730 +
1729 >>> p = Place('p', range(3)) 1731 >>> p = Place('p', range(3))
1730 >>> p.rename('egg') 1732 >>> p.rename('egg')
1731 >>> p 1733 >>> p
...@@ -1734,7 +1736,7 @@ class Node (NetElement) : ...@@ -1734,7 +1736,7 @@ class Node (NetElement) :
1734 >>> t.rename('spam') 1736 >>> t.rename('spam')
1735 >>> t 1737 >>> t
1736 Transition('spam', Expression('x==1')) 1738 Transition('spam', Expression('x==1'))
1737 - 1739 +
1738 @param name: the new name of the node 1740 @param name: the new name of the node
1739 @type name: `str` 1741 @type name: `str`
1740 """ 1742 """
...@@ -1744,14 +1746,14 @@ class Place (Node) : ...@@ -1744,14 +1746,14 @@ class Place (Node) :
1744 "A place of a Petri net." 1746 "A place of a Petri net."
1745 def __init__ (self, name, tokens=[], check=None) : 1747 def __init__ (self, name, tokens=[], check=None) :
1746 """Initialise with name, tokens and typecheck. 1748 """Initialise with name, tokens and typecheck.
1747 - 1749 +
1748 `tokens` may be a single value or an iterable object. `check` 1750 `tokens` may be a single value or an iterable object. `check`
1749 may be `None` (any token allowed) or a type from the module 1751 may be `None` (any token allowed) or a type from the module
1750 typing. 1752 typing.
1751 - 1753 +
1752 >>> Place('p', range(3), tInteger) 1754 >>> Place('p', range(3), tInteger)
1753 Place('p', MultiSet([...]), Instance(int)) 1755 Place('p', MultiSet([...]), Instance(int))
1754 - 1756 +
1755 @param name: the name of the place 1757 @param name: the name of the place
1756 @type name: `str` 1758 @type name: `str`
1757 @param tokens: a collection of tokens that mark the place 1759 @param tokens: a collection of tokens that mark the place
...@@ -1770,7 +1772,7 @@ class Place (Node) : ...@@ -1770,7 +1772,7 @@ class Place (Node) :
1770 self.add(tokens) 1772 self.add(tokens)
1771 def copy (self, name=None) : 1773 def copy (self, name=None) :
1772 """Return a copy of the place, with no arc attached. 1774 """Return a copy of the place, with no arc attached.
1773 - 1775 +
1774 >>> p = Place('p', range(3), tInteger) 1776 >>> p = Place('p', range(3), tInteger)
1775 >>> n = p.copy() 1777 >>> n = p.copy()
1776 >>> n.name == 'p' 1778 >>> n.name == 'p'
...@@ -1786,7 +1788,7 @@ class Place (Node) : ...@@ -1786,7 +1788,7 @@ class Place (Node) :
1786 True 1788 True
1787 >>> n.checker() 1789 >>> n.checker()
1788 Instance(int) 1790 Instance(int)
1789 - 1791 +
1790 @param name: if not `None`, the name of the copy 1792 @param name: if not `None`, the name of the copy
1791 @type name: `str` 1793 @type name: `str`
1792 @return: a copy of the place. 1794 @return: a copy of the place.
...@@ -1798,10 +1800,10 @@ class Place (Node) : ...@@ -1798,10 +1800,10 @@ class Place (Node) :
1798 __pnmltag__ = "place" 1800 __pnmltag__ = "place"
1799 def __pnmldump__ (self) : 1801 def __pnmldump__ (self) :
1800 """Dump a `Place` as a PNML tree 1802 """Dump a `Place` as a PNML tree
1801 - 1803 +
1802 @return: PNML tree 1804 @return: PNML tree
1803 @rtype: `pnml.Tree` 1805 @rtype: `pnml.Tree`
1804 - 1806 +
1805 >>> Place('p', [dot, dot], tBlackToken).__pnmldump__() 1807 >>> Place('p', [dot, dot], tBlackToken).__pnmldump__()
1806 <?xml version="1.0" encoding="utf-8"?> 1808 <?xml version="1.0" encoding="utf-8"?>
1807 <pnml> 1809 <pnml>
...@@ -1873,14 +1875,14 @@ class Place (Node) : ...@@ -1873,14 +1875,14 @@ class Place (Node) :
1873 return cls(tree["id"], [dot] * toks, tBlackToken) 1875 return cls(tree["id"], [dot] * toks, tBlackToken)
1874 def checker (self, check=None) : 1876 def checker (self, check=None) :
1875 """Change or return the type of the place. 1877 """Change or return the type of the place.
1876 - 1878 +
1877 >>> p = Place('p', range(3), tInteger) 1879 >>> p = Place('p', range(3), tInteger)
1878 >>> p.checker() 1880 >>> p.checker()
1879 Instance(int) 1881 Instance(int)
1880 >>> p.checker(tAll) 1882 >>> p.checker(tAll)
1881 >>> p.checker() 1883 >>> p.checker()
1882 tAll 1884 tAll
1883 - 1885 +
1884 @param check: the new constraint for the place or `None` to 1886 @param check: the new constraint for the place or `None` to
1885 retreive the current constraint 1887 retreive the current constraint
1886 @type check: `None` or a type from the module `typing` 1888 @type check: `None` or a type from the module `typing`
...@@ -1894,13 +1896,13 @@ class Place (Node) : ...@@ -1894,13 +1896,13 @@ class Place (Node) :
1894 self._check = check 1896 self._check = check
1895 def __contains__ (self, token) : 1897 def __contains__ (self, token) :
1896 """Check if a token is in the place. 1898 """Check if a token is in the place.
1897 - 1899 +
1898 >>> p = Place('p', range(3)) 1900 >>> p = Place('p', range(3))
1899 >>> 1 in p 1901 >>> 1 in p
1900 True 1902 True
1901 >>> 5 in p 1903 >>> 5 in p
1902 False 1904 False
1903 - 1905 +
1904 @param token: a token value 1906 @param token: a token value
1905 @type token: any type 1907 @type token: any type
1906 @return: `True` if `token` is held by the place, `False` 1908 @return: `True` if `token` is held by the place, `False`
...@@ -1910,17 +1912,17 @@ class Place (Node) : ...@@ -1910,17 +1912,17 @@ class Place (Node) :
1910 return token in self.tokens 1912 return token in self.tokens
1911 def __iter__ (self) : 1913 def __iter__ (self) :
1912 """Iterate over the tokens in the place. 1914 """Iterate over the tokens in the place.
1913 - 1915 +
1914 >>> p = Place('p', range(3)) 1916 >>> p = Place('p', range(3))
1915 >>> list(sorted([tok for tok in p])) 1917 >>> list(sorted([tok for tok in p]))
1916 [0, 1, 2] 1918 [0, 1, 2]
1917 - 1919 +
1918 @return: an iterator object 1920 @return: an iterator object
1919 """ 1921 """
1920 return iter(self.tokens) 1922 return iter(self.tokens)
1921 def is_empty (self) : 1923 def is_empty (self) :
1922 """Check is the place is empty. 1924 """Check is the place is empty.
1923 - 1925 +
1924 >>> p = Place('p', range(3)) 1926 >>> p = Place('p', range(3))
1925 >>> p.tokens == MultiSet([0, 1, 2]) 1927 >>> p.tokens == MultiSet([0, 1, 2])
1926 True 1928 True
...@@ -1929,7 +1931,7 @@ class Place (Node) : ...@@ -1929,7 +1931,7 @@ class Place (Node) :
1929 >>> p.tokens = MultiSet() 1931 >>> p.tokens = MultiSet()
1930 >>> p.is_empty() 1932 >>> p.is_empty()
1931 True 1933 True
1932 - 1934 +
1933 @return: `True` if the place is empty, `False` otherwise 1935 @return: `True` if the place is empty, `False` otherwise
1934 @rtype: `bool` 1936 @rtype: `bool`
1935 """ 1937 """
...@@ -1938,13 +1940,13 @@ class Place (Node) : ...@@ -1938,13 +1940,13 @@ class Place (Node) :
1938 """Check if the 'tokens' are allowed in the place. An exception 1940 """Check if the 'tokens' are allowed in the place. An exception
1939 `ValueError` is raised whenever a forbidden token is 1941 `ValueError` is raised whenever a forbidden token is
1940 encountered. 1942 encountered.
1941 - 1943 +
1942 >>> p = Place('p', [], tInteger) 1944 >>> p = Place('p', [], tInteger)
1943 >>> p.check([1, 2, 3]) 1945 >>> p.check([1, 2, 3])
1944 >>> try : p.check(['forbidden!']) 1946 >>> try : p.check(['forbidden!'])
1945 ... except ValueError : print(sys.exc_info()[1]) 1947 ... except ValueError : print(sys.exc_info()[1])
1946 forbidden token 'forbidden!' 1948 forbidden token 'forbidden!'
1947 - 1949 +
1948 @param tokens: an iterable collection of tokens or a single 1950 @param tokens: an iterable collection of tokens or a single
1949 value 1951 value
1950 @type tokens: any type, iterable types (except `str`) will be 1952 @type tokens: any type, iterable types (except `str`) will be
...@@ -1955,21 +1957,21 @@ class Place (Node) : ...@@ -1955,21 +1957,21 @@ class Place (Node) :
1955 raise ValueError("forbidden token '%s'" % (t,)) 1957 raise ValueError("forbidden token '%s'" % (t,))
1956 def __str__ (self) : 1958 def __str__ (self) :
1957 """Return the name of the place. 1959 """Return the name of the place.
1958 - 1960 +
1959 >>> p = Place('Great place!') 1961 >>> p = Place('Great place!')
1960 >>> str(p) 1962 >>> str(p)
1961 'Great place!' 1963 'Great place!'
1962 - 1964 +
1963 @return: the name of the place 1965 @return: the name of the place
1964 @rtype: `str` 1966 @rtype: `str`
1965 """ 1967 """
1966 return self.name 1968 return self.name
1967 def __repr__ (self) : 1969 def __repr__ (self) :
1968 """Return a textual representation of the place. 1970 """Return a textual representation of the place.
1969 - 1971 +
1970 >>> repr(Place('p', range(3), tInteger)) 1972 >>> repr(Place('p', range(3), tInteger))
1971 "Place('p', MultiSet([...]), Instance(int))" 1973 "Place('p', MultiSet([...]), Instance(int))"
1972 - 1974 +
1973 @return: a string suitable to `eval` representing the place 1975 @return: a string suitable to `eval` representing the place
1974 @rtype: `str` 1976 @rtype: `str`
1975 """ 1977 """
...@@ -1979,14 +1981,14 @@ class Place (Node) : ...@@ -1979,14 +1981,14 @@ class Place (Node) :
1979 repr(self._check)) 1981 repr(self._check))
1980 def add (self, tokens) : 1982 def add (self, tokens) :
1981 """Add tokens to the place. 1983 """Add tokens to the place.
1982 - 1984 +
1983 >>> p = Place('p') 1985 >>> p = Place('p')
1984 >>> p.tokens == MultiSet([]) 1986 >>> p.tokens == MultiSet([])
1985 True 1987 True
1986 >>> p.add(range(3)) 1988 >>> p.add(range(3))
1987 >>> p.tokens == MultiSet([0, 1, 2]) 1989 >>> p.tokens == MultiSet([0, 1, 2])
1988 True 1990 True
1989 - 1991 +
1990 @param tokens: a collection of tokens to be added to the place 1992 @param tokens: a collection of tokens to be added to the place
1991 @type tokens: any type, iterable types (except `str`) will be 1993 @type tokens: any type, iterable types (except `str`) will be
1992 considered as collections of values 1994 considered as collections of values
...@@ -1995,14 +1997,14 @@ class Place (Node) : ...@@ -1995,14 +1997,14 @@ class Place (Node) :
1995 self.tokens.add(iterate(tokens)) 1997 self.tokens.add(iterate(tokens))
1996 def remove (self, tokens) : 1998 def remove (self, tokens) :
1997 """Remove tokens from the place. 1999 """Remove tokens from the place.
1998 - 2000 +
1999 >>> p = Place('p', list(range(3)) * 2) 2001 >>> p = Place('p', list(range(3)) * 2)
2000 >>> p.tokens == MultiSet([0, 0, 1, 1, 2, 2]) 2002 >>> p.tokens == MultiSet([0, 0, 1, 1, 2, 2])
2001 True 2003 True
2002 >>> p.remove(range(3)) 2004 >>> p.remove(range(3))
2003 >>> p.tokens == MultiSet([0, 1, 2]) 2005 >>> p.tokens == MultiSet([0, 1, 2])
2004 True 2006 True
2005 - 2007 +
2006 @param tokens: a collection of tokens to be removed from the 2008 @param tokens: a collection of tokens to be removed from the
2007 place 2009 place
2008 @type tokens: any type, iterable types (except `str`) will be 2010 @type tokens: any type, iterable types (except `str`) will be
...@@ -2011,7 +2013,7 @@ class Place (Node) : ...@@ -2011,7 +2013,7 @@ class Place (Node) :
2011 self.tokens.remove(tokens) 2013 self.tokens.remove(tokens)
2012 def empty (self) : 2014 def empty (self) :
2013 """Remove all the tokens. 2015 """Remove all the tokens.
2014 - 2016 +
2015 >>> p = Place('p', list(range(3)) * 2) 2017 >>> p = Place('p', list(range(3)) * 2)
2016 >>> p.tokens == MultiSet([0, 0, 1, 1, 2, 2]) 2018 >>> p.tokens == MultiSet([0, 0, 1, 1, 2, 2])
2017 True 2019 True
...@@ -2022,7 +2024,7 @@ class Place (Node) : ...@@ -2022,7 +2024,7 @@ class Place (Node) :
2022 self.tokens = MultiSet() 2024 self.tokens = MultiSet()
2023 def reset (self, tokens) : 2025 def reset (self, tokens) :
2024 """Replace the marking with 'tokens'. 2026 """Replace the marking with 'tokens'.
2025 - 2027 +
2026 >>> p = Place('p', list(range(3)) * 2) 2028 >>> p = Place('p', list(range(3)) * 2)
2027 >>> p.tokens == MultiSet([0, 0, 1, 1, 2, 2]) 2029 >>> p.tokens == MultiSet([0, 0, 1, 1, 2, 2])
2028 True 2030 True
...@@ -2037,12 +2039,12 @@ class Transition (Node) : ...@@ -2037,12 +2039,12 @@ class Transition (Node) :
2037 "A transition in a Petri net." 2039 "A transition in a Petri net."
2038 def __init__ (self, name, guard=None) : 2040 def __init__ (self, name, guard=None) :
2039 """Initialise with the name and the guard. 2041 """Initialise with the name and the guard.
2040 - 2042 +
2041 If `guard` is `None`, `True` is assumed instead. 2043 If `guard` is `None`, `True` is assumed instead.
2042 - 2044 +
2043 >>> Transition('t') 2045 >>> Transition('t')
2044 Transition('t', Expression('True')) 2046 Transition('t', Expression('True'))
2045 - 2047 +
2046 @param name: the name of the transition 2048 @param name: the name of the transition
2047 @type name: `str` 2049 @type name: `str`
2048 @param guard: the guard of the transition 2050 @param guard: the guard of the transition
...@@ -2057,13 +2059,13 @@ class Transition (Node) : ...@@ -2057,13 +2059,13 @@ class Transition (Node) :
2057 self.name = name 2059 self.name = name
2058 def copy (self, name=None) : 2060 def copy (self, name=None) :
2059 """Return a copy of the transition, with no arc attached. 2061 """Return a copy of the transition, with no arc attached.
2060 - 2062 +
2061 >>> t = Transition('t', Expression('x==1')) 2063 >>> t = Transition('t', Expression('x==1'))
2062 >>> t.copy() 2064 >>> t.copy()
2063 Transition('t', Expression('x==1')) 2065 Transition('t', Expression('x==1'))
2064 >>> t.copy('x') 2066 >>> t.copy('x')
2065 Transition('x', Expression('x==1')) 2067 Transition('x', Expression('x==1'))
2066 - 2068 +
2067 @param name: if not `None`, the name if of the copy 2069 @param name: if not `None`, the name if of the copy
2068 @type name: `str` 2070 @type name: `str`
2069 @return: a copy of the transition. 2071 @return: a copy of the transition.
...@@ -2114,21 +2116,21 @@ class Transition (Node) : ...@@ -2114,21 +2116,21 @@ class Transition (Node) :
2114 return cls(tree["id"]) 2116 return cls(tree["id"])
2115 def __str__ (self) : 2117 def __str__ (self) :
2116 """Return the name of the transition. 2118 """Return the name of the transition.
2117 - 2119 +
2118 >>> t = Transition('What a transition!') 2120 >>> t = Transition('What a transition!')
2119 >>> str(t) 2121 >>> str(t)
2120 'What a transition!' 2122 'What a transition!'
2121 - 2123 +
2122 @return: the name of the transition 2124 @return: the name of the transition
2123 @rtype: `str` 2125 @rtype: `str`
2124 """ 2126 """
2125 return self.name 2127 return self.name
2126 def __repr__ (self) : 2128 def __repr__ (self) :
2127 """Return a textual representation of the transition. 2129 """Return a textual representation of the transition.
2128 - 2130 +
2129 >>> repr(Transition('t', Expression('x==1'))) 2131 >>> repr(Transition('t', Expression('x==1')))
2130 "Transition('t', Expression('x==1'))" 2132 "Transition('t', Expression('x==1'))"
2131 - 2133 +
2132 @return: a string suitable to `eval` representing the 2134 @return: a string suitable to `eval` representing the
2133 transition 2135 transition
2134 @rtype: `str` 2136 @rtype: `str`
...@@ -2138,7 +2140,7 @@ class Transition (Node) : ...@@ -2138,7 +2140,7 @@ class Transition (Node) :
2138 repr(self.guard)) 2140 repr(self.guard))
2139 def add_input (self, place, label) : 2141 def add_input (self, place, label) :
2140 """Add an input arc from `place` labelled by `label`. 2142 """Add an input arc from `place` labelled by `label`.
2141 - 2143 +
2142 >>> t = Transition('t') 2144 >>> t = Transition('t')
2143 >>> t.add_input(Place('p'), Variable('x')) 2145 >>> t.add_input(Place('p'), Variable('x'))
2144 >>> t.input() 2146 >>> t.input()
...@@ -2146,7 +2148,7 @@ class Transition (Node) : ...@@ -2146,7 +2148,7 @@ class Transition (Node) :
2146 >>> try : t.add_input(Place('x'), Expression('x+y')) 2148 >>> try : t.add_input(Place('x'), Expression('x+y'))
2147 ... except ConstraintError : print(sys.exc_info()[1]) 2149 ... except ConstraintError : print(sys.exc_info()[1])
2148 'Expression' objects not allowed on input arcs 2150 'Expression' objects not allowed on input arcs
2149 - 2151 +
2150 @param place: the input place 2152 @param place: the input place
2151 @type place: `Place` 2153 @type place: `Place`
2152 @param label: the arc label 2154 @param label: the arc label
...@@ -2161,7 +2163,7 @@ class Transition (Node) : ...@@ -2161,7 +2163,7 @@ class Transition (Node) :
2161 self._input[place] = label 2163 self._input[place] = label
2162 def remove_input (self, place) : 2164 def remove_input (self, place) :
2163 """Remove the input arc from `place`. 2165 """Remove the input arc from `place`.
2164 - 2166 +
2165 >>> t = Transition('t') 2167 >>> t = Transition('t')
2166 >>> p = Place('p') 2168 >>> p = Place('p')
2167 >>> t.add_input(p, Variable('x')) 2169 >>> t.add_input(p, Variable('x'))
...@@ -2170,7 +2172,7 @@ class Transition (Node) : ...@@ -2170,7 +2172,7 @@ class Transition (Node) :
2170 >>> t.remove_input(p) 2172 >>> t.remove_input(p)
2171 >>> t.input() 2173 >>> t.input()
2172 [] 2174 []
2173 - 2175 +
2174 @param place: the input place 2176 @param place: the input place
2175 @type place: `Place` 2177 @type place: `Place`
2176 """ 2178 """
...@@ -2180,24 +2182,24 @@ class Transition (Node) : ...@@ -2180,24 +2182,24 @@ class Transition (Node) :
2180 raise ConstraintError("not connected to '%s'" % place.name) 2182 raise ConstraintError("not connected to '%s'" % place.name)
2181 def input (self) : 2183 def input (self) :
2182 """Return the list of input arcs. 2184 """Return the list of input arcs.
2183 - 2185 +
2184 >>> t = Transition('t') 2186 >>> t = Transition('t')
2185 >>> t.add_input(Place('p'), Variable('x')) 2187 >>> t.add_input(Place('p'), Variable('x'))
2186 >>> t.input() 2188 >>> t.input()
2187 [(Place('p', MultiSet([]), tAll), Variable('x'))] 2189 [(Place('p', MultiSet([]), tAll), Variable('x'))]
2188 - 2190 +
2189 @return: a list of pairs (place, label). 2191 @return: a list of pairs (place, label).
2190 @rtype: `[(Place, ArcAnnotation)]` 2192 @rtype: `[(Place, ArcAnnotation)]`
2191 """ 2193 """
2192 return list(self._input.items()) 2194 return list(self._input.items())
2193 def add_output (self, place, label) : 2195 def add_output (self, place, label) :
2194 """Add an output arc from `place` labelled by `label`. 2196 """Add an output arc from `place` labelled by `label`.
2195 - 2197 +
2196 >>> t = Transition('t') 2198 >>> t = Transition('t')
2197 >>> t.add_output(Place('p'), Expression('x+1')) 2199 >>> t.add_output(Place('p'), Expression('x+1'))
2198 >>> t.output() 2200 >>> t.output()
2199 [(Place('p', MultiSet([]), tAll), Expression('x+1'))] 2201 [(Place('p', MultiSet([]), tAll), Expression('x+1'))]
2200 - 2202 +
2201 @param place: the output place 2203 @param place: the output place
2202 @type place: `Place` 2204 @type place: `Place`
2203 @param label: the arc label 2205 @param label: the arc label
...@@ -2209,7 +2211,7 @@ class Transition (Node) : ...@@ -2209,7 +2211,7 @@ class Transition (Node) :
2209 self._output[place] = label 2211 self._output[place] = label
2210 def remove_output (self, place) : 2212 def remove_output (self, place) :
2211 """Remove the output arc to `place`. 2213 """Remove the output arc to `place`.
2212 - 2214 +
2213 >>> t = Transition('t') 2215 >>> t = Transition('t')
2214 >>> p = Place('p') 2216 >>> p = Place('p')
2215 >>> t.add_output(p, Variable('x')) 2217 >>> t.add_output(p, Variable('x'))
...@@ -2218,7 +2220,7 @@ class Transition (Node) : ...@@ -2218,7 +2220,7 @@ class Transition (Node) :
2218 >>> t.remove_output(p) 2220 >>> t.remove_output(p)
2219 >>> t.output() 2221 >>> t.output()
2220 [] 2222 []
2221 - 2223 +
2222 @param place: the output place 2224 @param place: the output place
2223 @type place: `Place` 2225 @type place: `Place`
2224 """ 2226 """
...@@ -2228,25 +2230,25 @@ class Transition (Node) : ...@@ -2228,25 +2230,25 @@ class Transition (Node) :
2228 raise ConstraintError("not connected to '%s'" % place.name) 2230 raise ConstraintError("not connected to '%s'" % place.name)
2229 def output (self) : 2231 def output (self) :
2230 """Return the list of output arcs. 2232 """Return the list of output arcs.
2231 - 2233 +
2232 >>> t = Transition('t') 2234 >>> t = Transition('t')
2233 >>> t.add_output(Place('p'), Expression('x+1')) 2235 >>> t.add_output(Place('p'), Expression('x+1'))
2234 >>> t.output() 2236 >>> t.output()
2235 [(Place('p', MultiSet([]), tAll), Expression('x+1'))] 2237 [(Place('p', MultiSet([]), tAll), Expression('x+1'))]
2236 - 2238 +
2237 @return: a list of pairs (place, label). 2239 @return: a list of pairs (place, label).
2238 @rtype: `[(Place, ArcAnnotation)]` 2240 @rtype: `[(Place, ArcAnnotation)]`
2239 """ 2241 """
2240 return list(self._output.items()) 2242 return list(self._output.items())
2241 def _check (self, binding, tokens, input) : 2243 def _check (self, binding, tokens, input) :
2242 """Check `binding` against the guard and tokens flow. 2244 """Check `binding` against the guard and tokens flow.
2243 - 2245 +
2244 If `tokens` is `True`, check whether the flow of tokens can be 2246 If `tokens` is `True`, check whether the flow of tokens can be
2245 provided by the input places. If `input` is `True`, check if 2247 provided by the input places. If `input` is `True`, check if
2246 the evaluation of the inputs arcs respect the types of the 2248 the evaluation of the inputs arcs respect the types of the
2247 input places. In any case, this is checked for the output 2249 input places. In any case, this is checked for the output
2248 places. 2250 places.
2249 - 2251 +
2250 >>> t = Transition('t', Expression('x>0')) 2252 >>> t = Transition('t', Expression('x>0'))
2251 >>> p = Place('p', [], tInteger) 2253 >>> p = Place('p', [], tInteger)
2252 >>> t.add_input(p, Variable('x')) 2254 >>> t.add_input(p, Variable('x'))
...@@ -2268,7 +2270,7 @@ class Transition (Node) : ...@@ -2268,7 +2270,7 @@ class Transition (Node) :
2268 False 2270 False
2269 >>> t._check(s, True, True) 2271 >>> t._check(s, True, True)
2270 False 2272 False
2271 - 2273 +
2272 @param binding: a valuation of the variables on the transition 2274 @param binding: a valuation of the variables on the transition
2273 @type binding: `data.Substitution` 2275 @type binding: `data.Substitution`
2274 @param tokens: whether it should be checked that input places 2276 @param tokens: whether it should be checked that input places
...@@ -2299,11 +2301,11 @@ class Transition (Node) : ...@@ -2299,11 +2301,11 @@ class Transition (Node) :
2299 return True 2301 return True
2300 def activated (self, binding) : 2302 def activated (self, binding) :
2301 """Check if `binding` activates the transition. 2303 """Check if `binding` activates the transition.
2302 - 2304 +
2303 This is the case if the guard evaluates to `True` and if the 2305 This is the case if the guard evaluates to `True` and if the
2304 types of the places are respected. Note that the presence of 2306 types of the places are respected. Note that the presence of
2305 enough tokens is not required. 2307 enough tokens is not required.
2306 - 2308 +
2307 >>> t = Transition('t', Expression('x>0')) 2309 >>> t = Transition('t', Expression('x>0'))
2308 >>> p = Place('p', [], tInteger) 2310 >>> p = Place('p', [], tInteger)
2309 >>> t.add_input(p, Variable('x')) 2311 >>> t.add_input(p, Variable('x'))
...@@ -2313,17 +2315,17 @@ class Transition (Node) : ...@@ -2313,17 +2315,17 @@ class Transition (Node) :
2313 False 2315 False
2314 >>> t.activated(Substitution(x=3.14)) 2316 >>> t.activated(Substitution(x=3.14))
2315 False 2317 False
2316 - 2318 +
2317 @param binding: a valuation of the variables on the transition 2319 @param binding: a valuation of the variables on the transition
2318 @type binding: `Substitution` 2320 @type binding: `Substitution`
2319 """ 2321 """
2320 return self._check(binding, False, True) 2322 return self._check(binding, False, True)
2321 def enabled (self, binding) : 2323 def enabled (self, binding) :
2322 """Check if `binding` enables the transition. 2324 """Check if `binding` enables the transition.
2323 - 2325 +
2324 This is the case if the transition is activated and if the 2326 This is the case if the transition is activated and if the
2325 input places hold enough tokens to allow the firing. 2327 input places hold enough tokens to allow the firing.
2326 - 2328 +
2327 >>> t = Transition('t', Expression('x>0')) 2329 >>> t = Transition('t', Expression('x>0'))
2328 >>> p = Place('p', [0], tInteger) 2330 >>> p = Place('p', [0], tInteger)
2329 >>> t.add_input(p, Variable('x')) 2331 >>> t.add_input(p, Variable('x'))
...@@ -2332,7 +2334,7 @@ class Transition (Node) : ...@@ -2332,7 +2334,7 @@ class Transition (Node) :
2332 >>> p.add(1) 2334 >>> p.add(1)
2333 >>> t.enabled(Substitution(x=1)) 2335 >>> t.enabled(Substitution(x=1))
2334 True 2336 True
2335 - 2337 +
2336 @param binding: a valuation of the variables on the transition 2338 @param binding: a valuation of the variables on the transition
2337 @type binding: `Substitution` 2339 @type binding: `Substitution`
2338 """ 2340 """
...@@ -2340,7 +2342,7 @@ class Transition (Node) : ...@@ -2340,7 +2342,7 @@ class Transition (Node) :
2340 def vars (self) : 2342 def vars (self) :
2341 """Return the set of variables involved in the guard, input and 2343 """Return the set of variables involved in the guard, input and
2342 output arcs of the transition. 2344 output arcs of the transition.
2343 - 2345 +
2344 >>> t = Transition('t', Expression('z is not None')) 2346 >>> t = Transition('t', Expression('z is not None'))
2345 >>> px = Place('px') 2347 >>> px = Place('px')
2346 >>> t.add_input(px, Variable('x')) 2348 >>> t.add_input(px, Variable('x'))
...@@ -2348,7 +2350,7 @@ class Transition (Node) : ...@@ -2348,7 +2350,7 @@ class Transition (Node) :
2348 >>> t.add_output(py, Variable('y')) 2350 >>> t.add_output(py, Variable('y'))
2349 >>> list(sorted(t.vars())) 2351 >>> list(sorted(t.vars()))
2350 ['x', 'y', 'z'] 2352 ['x', 'y', 'z']
2351 - 2353 +
2352 @return: the set of variables names 2354 @return: the set of variables names
2353 @rtype: a `set` of `str` 2355 @rtype: a `set` of `str`
2354 """ 2356 """
...@@ -2360,10 +2362,10 @@ class Transition (Node) : ...@@ -2360,10 +2362,10 @@ class Transition (Node) :
2360 return v 2362 return v
2361 def substitute (self, binding) : 2363 def substitute (self, binding) :
2362 """Substite all the annotations arround the transition. 2364 """Substite all the annotations arround the transition.
2363 - 2365 +
2364 `binding` is used to substitute the guard and all the labels 2366 `binding` is used to substitute the guard and all the labels
2365 on the arcs attached to the transition. 2367 on the arcs attached to the transition.
2366 - 2368 +
2367 >>> t = Transition('t', Expression('z is not None')) 2369 >>> t = Transition('t', Expression('z is not None'))
2368 >>> px = Place('px') 2370 >>> px = Place('px')
2369 >>> t.add_input(px, Variable('x')) 2371 >>> t.add_input(px, Variable('x'))
...@@ -2376,7 +2378,7 @@ class Transition (Node) : ...@@ -2376,7 +2378,7 @@ class Transition (Node) :
2376 [(Place('px', MultiSet([]), tAll), Variable('a'))] 2378 [(Place('px', MultiSet([]), tAll), Variable('a'))]
2377 >>> t.output() 2379 >>> t.output()
2378 [(Place('py', MultiSet([]), tAll), Variable('b'))] 2380 [(Place('py', MultiSet([]), tAll), Variable('b'))]
2379 - 2381 +
2380 @param binding: a substitution from variables to variables 2382 @param binding: a substitution from variables to variables
2381 (not values) 2383 (not values)
2382 @type binding: `Substitution` 2384 @type binding: `Substitution`
...@@ -2388,12 +2390,12 @@ class Transition (Node) : ...@@ -2388,12 +2390,12 @@ class Transition (Node) :
2388 label.substitute(binding) 2390 label.substitute(binding)
2389 def modes (self) : 2391 def modes (self) :
2390 """Return the list of bindings which enable the transition. 2392 """Return the list of bindings which enable the transition.
2391 - 2393 +
2392 Note that the modes are usually considered to be the list of 2394 Note that the modes are usually considered to be the list of
2393 bindings that _activate_ a transitions. However, this list is 2395 bindings that _activate_ a transitions. However, this list is
2394 generally infinite so we retricted ourselves to _actual 2396 generally infinite so we retricted ourselves to _actual
2395 modes_, taking into account the present tokens. 2397 modes_, taking into account the present tokens.
2396 - 2398 +
2397 >>> t = Transition('t', Expression('x!=y')) 2399 >>> t = Transition('t', Expression('x!=y'))
2398 >>> px = Place('px', range(2)) 2400 >>> px = Place('px', range(2))
2399 >>> t.add_input(px, Variable('x')) 2401 >>> t.add_input(px, Variable('x'))
...@@ -2406,12 +2408,12 @@ class Transition (Node) : ...@@ -2406,12 +2408,12 @@ class Transition (Node) :
2406 True 2408 True
2407 >>> Substitution(y=1, x=0) in m 2409 >>> Substitution(y=1, x=0) in m
2408 True 2410 True
2409 - 2411 +
2410 Note also that modes cannot be computed with respect to output 2412 Note also that modes cannot be computed with respect to output
2411 arcs: indeed, only input arcs allow for concretely associate 2413 arcs: indeed, only input arcs allow for concretely associate
2412 values to variables; on the other hand, binding an output arc 2414 values to variables; on the other hand, binding an output arc
2413 would require to solve the equation provided by the guard. 2415 would require to solve the equation provided by the guard.
2414 - 2416 +
2415 >>> t = Transition('t', Expression('x!=y')) 2417 >>> t = Transition('t', Expression('x!=y'))
2416 >>> px = Place('px', range(2)) 2418 >>> px = Place('px', range(2))
2417 >>> t.add_input(px, Variable('x')) 2419 >>> t.add_input(px, Variable('x'))
...@@ -2422,7 +2424,7 @@ class Transition (Node) : ...@@ -2422,7 +2424,7 @@ class Transition (Node) :
2422 ... raise Exception() 2424 ... raise Exception()
2423 ... except NameError : 2425 ... except NameError :
2424 ... pass 2426 ... pass
2425 - 2427 +
2426 @return: a list of substitutions usable to fire the transition 2428 @return: a list of substitutions usable to fire the transition
2427 @rtype: a `list` of `Substitution` 2429 @rtype: a `list` of `Substitution`
2428 """ 2430 """
...@@ -2447,10 +2449,10 @@ class Transition (Node) : ...@@ -2447,10 +2449,10 @@ class Transition (Node) :
2447 return result 2449 return result
2448 def flow (self, binding) : 2450 def flow (self, binding) :
2449 """Return the token flow for a firing with `binding`. 2451 """Return the token flow for a firing with `binding`.
2450 - 2452 +
2451 The flow is represented by a pair `(in, out)`, both being 2453 The flow is represented by a pair `(in, out)`, both being
2452 instances of the class `Marking`. 2454 instances of the class `Marking`.
2453 - 2455 +
2454 >>> t = Transition('t', Expression('x!=1')) 2456 >>> t = Transition('t', Expression('x!=1'))
2455 >>> px = Place('px', range(3)) 2457 >>> px = Place('px', range(3))
2456 >>> t.add_input(px, Variable('x')) 2458 >>> t.add_input(px, Variable('x'))
...@@ -2463,7 +2465,7 @@ class Transition (Node) : ...@@ -2463,7 +2465,7 @@ class Transition (Node) :
2463 transition not enabled for {x -> 1} 2465 transition not enabled for {x -> 1}
2464 >>> t.flow(Substitution(x=2)) 2466 >>> t.flow(Substitution(x=2))
2465 (Marking({'px': MultiSet([2])}), Marking({'py': MultiSet([3])})) 2467 (Marking({'px': MultiSet([2])}), Marking({'py': MultiSet([3])}))
2466 - 2468 +
2467 @param binding: a substitution from variables to values (not 2469 @param binding: a substitution from variables to values (not
2468 variables) 2470 variables)
2469 @type binding: `Substitution` 2471 @type binding: `Substitution`
...@@ -2480,7 +2482,7 @@ class Transition (Node) : ...@@ -2480,7 +2482,7 @@ class Transition (Node) :
2480 raise ValueError("transition not enabled for %s" % binding) 2482 raise ValueError("transition not enabled for %s" % binding)
2481 def fire (self, binding) : 2483 def fire (self, binding) :
2482 """Fire the transition with `binding`. 2484 """Fire the transition with `binding`.
2483 - 2485 +
2484 >>> t = Transition('t', Expression('x!=1')) 2486 >>> t = Transition('t', Expression('x!=1'))
2485 >>> px = Place('px', range(3)) 2487 >>> px = Place('px', range(3))
2486 >>> t.add_input(px, Variable('x')) 2488 >>> t.add_input(px, Variable('x'))
...@@ -2499,7 +2501,7 @@ class Transition (Node) : ...@@ -2499,7 +2501,7 @@ class Transition (Node) :
2499 True 2501 True
2500 >>> py.tokens == MultiSet([1, 3]) 2502 >>> py.tokens == MultiSet([1, 3])
2501 True 2503 True
2502 - 2504 +
2503 @param binding: a substitution from variables to values (not 2505 @param binding: a substitution from variables to values (not
2504 variables) 2506 variables)
2505 @type binding: `Substitution` 2507 @type binding: `Substitution`
...@@ -2518,10 +2520,10 @@ class Transition (Node) : ...@@ -2518,10 +2520,10 @@ class Transition (Node) :
2518 2520
2519 class Marking (hdict) : 2521 class Marking (hdict) :
2520 """A marking of a Petri net. 2522 """A marking of a Petri net.
2521 - 2523 +
2522 This is basically a `snakes.hashables.hdict` mapping place names 2524 This is basically a `snakes.hashables.hdict` mapping place names
2523 to multisets of tokens. 2525 to multisets of tokens.
2524 - 2526 +
2525 The parameters for the constructor must be given in a form 2527 The parameters for the constructor must be given in a form
2526 suitable for initialising a `hdict` with place names as keys and 2528 suitable for initialising a `hdict` with place names as keys and
2527 multisets as values. Places not given in the marking are assumed 2529 multisets as values. Places not given in the marking are assumed
...@@ -2587,16 +2589,16 @@ class Marking (hdict) : ...@@ -2587,16 +2589,16 @@ class Marking (hdict) :
2587 for child in tree.get_children("place"))) 2589 for child in tree.get_children("place")))
2588 def __call__ (self, place) : 2590 def __call__ (self, place) :
2589 """Return the marking of `place`. 2591 """Return the marking of `place`.
2590 - 2592 +
2591 The empty multiset is returned if `place` is not explicitely 2593 The empty multiset is returned if `place` is not explicitely
2592 given in the marking. 2594 given in the marking.
2593 - 2595 +
2594 >>> m = Marking(p1=MultiSet([1]), p2=MultiSet([2])) 2596 >>> m = Marking(p1=MultiSet([1]), p2=MultiSet([2]))
2595 >>> m('p1') 2597 >>> m('p1')
2596 MultiSet([1]) 2598 MultiSet([1])
2597 >>> m('p') 2599 >>> m('p')
2598 MultiSet([]) 2600 MultiSet([])
2599 - 2601 +
2600 @param place: a place name 2602 @param place: a place name
2601 @type place: `str` 2603 @type place: `str`
2602 @return: the marking of `place` 2604 @return: the marking of `place`
...@@ -2608,7 +2610,7 @@ class Marking (hdict) : ...@@ -2608,7 +2610,7 @@ class Marking (hdict) :
2608 return MultiSet() 2610 return MultiSet()
2609 def copy (self) : 2611 def copy (self) :
2610 """Copy a marking 2612 """Copy a marking
2611 - 2613 +
2612 >>> m = Marking(p1=MultiSet([1]), p2=MultiSet([2])) 2614 >>> m = Marking(p1=MultiSet([1]), p2=MultiSet([2]))
2613 >>> m.copy() == Marking({'p2': MultiSet([2]), 'p1': MultiSet([1])}) 2615 >>> m.copy() == Marking({'p2': MultiSet([2]), 'p1': MultiSet([1])})
2614 True 2616 True
...@@ -2619,10 +2621,10 @@ class Marking (hdict) : ...@@ -2619,10 +2621,10 @@ class Marking (hdict) :
2619 return result 2621 return result
2620 def __add__ (self, other) : 2622 def __add__ (self, other) :
2621 """Addition of markings. 2623 """Addition of markings.
2622 - 2624 +
2623 >>> Marking(p1=MultiSet([1]), p2=MultiSet([2])) + Marking(p2=MultiSet([2]), p3=MultiSet([3])) == Marking({'p2': MultiSet([2, 2]), 'p3': MultiSet([3]), 'p1': MultiSet([1])}) 2625 >>> Marking(p1=MultiSet([1]), p2=MultiSet([2])) + Marking(p2=MultiSet([2]), p3=MultiSet([3])) == Marking({'p2': MultiSet([2, 2]), 'p3': MultiSet([3]), 'p1': MultiSet([1])})
2624 True 2626 True
2625 - 2627 +
2626 @param other: another marking 2628 @param other: another marking
2627 @type other: `Marking` 2629 @type other: `Marking`
2628 @return: the addition of the two markings 2630 @return: the addition of the two markings
...@@ -2637,7 +2639,7 @@ class Marking (hdict) : ...@@ -2637,7 +2639,7 @@ class Marking (hdict) :
2637 return result 2639 return result
2638 def __sub__ (self, other) : 2640 def __sub__ (self, other) :
2639 """Substraction of markings. 2641 """Substraction of markings.
2640 - 2642 +
2641 >>> Marking(p1=MultiSet([1]), p2=MultiSet([2, 2])) - Marking(p2=MultiSet([2])) 2643 >>> Marking(p1=MultiSet([1]), p2=MultiSet([2, 2])) - Marking(p2=MultiSet([2]))
2642 Marking({'p2': MultiSet([2]), 'p1': MultiSet([1])}) 2644 Marking({'p2': MultiSet([2]), 'p1': MultiSet([1])})
2643 >>> try : Marking(p1=MultiSet([1]), p2=MultiSet([2])) - Marking(p2=MultiSet([2, 2])) 2645 >>> try : Marking(p1=MultiSet([1]), p2=MultiSet([2])) - Marking(p2=MultiSet([2, 2]))
...@@ -2646,7 +2648,7 @@ class Marking (hdict) : ...@@ -2646,7 +2648,7 @@ class Marking (hdict) :
2646 >>> try : Marking(p1=MultiSet([1]), p2=MultiSet([2])) - Marking(p3=MultiSet([3])) 2648 >>> try : Marking(p1=MultiSet([1]), p2=MultiSet([2])) - Marking(p3=MultiSet([3]))
2647 ... except DomainError : print(sys.exc_info()[1]) 2649 ... except DomainError : print(sys.exc_info()[1])
2648 'p3' absent from the marking 2650 'p3' absent from the marking
2649 - 2651 +
2650 @param other: another marking 2652 @param other: another marking
2651 @type other: `Marking` 2653 @type other: `Marking`
2652 @return: the difference of the two markings 2654 @return: the difference of the two markings
...@@ -2664,14 +2666,14 @@ class Marking (hdict) : ...@@ -2664,14 +2666,14 @@ class Marking (hdict) :
2664 __hash__ = hdict.__hash__ 2666 __hash__ = hdict.__hash__
2665 def __eq__ (self, other) : 2667 def __eq__ (self, other) :
2666 """Test for equality (same places with the same tokens). 2668 """Test for equality (same places with the same tokens).
2667 - 2669 +
2668 >>> Marking(p=MultiSet([1])) == Marking(p=MultiSet([1])) 2670 >>> Marking(p=MultiSet([1])) == Marking(p=MultiSet([1]))
2669 True 2671 True
2670 >>> Marking(p=MultiSet([1])) == Marking(p=MultiSet([1, 1])) 2672 >>> Marking(p=MultiSet([1])) == Marking(p=MultiSet([1, 1]))
2671 False 2673 False
2672 >>> Marking(p1=MultiSet([1])) == Marking(p2=MultiSet([1])) 2674 >>> Marking(p1=MultiSet([1])) == Marking(p2=MultiSet([1]))
2673 False 2675 False
2674 - 2676 +
2675 @param other: another marking 2677 @param other: another marking
2676 @type other: `Marking` 2678 @type other: `Marking`
2677 @return: `True` if the markings are equal, `False` otherwise 2679 @return: `True` if the markings are equal, `False` otherwise
...@@ -2688,14 +2690,14 @@ class Marking (hdict) : ...@@ -2688,14 +2690,14 @@ class Marking (hdict) :
2688 return True 2690 return True
2689 def __ne__ (self, other) : 2691 def __ne__ (self, other) :
2690 """Test if two markings differ. 2692 """Test if two markings differ.
2691 - 2693 +
2692 >>> Marking(p=MultiSet([1])) != Marking(p=MultiSet([1])) 2694 >>> Marking(p=MultiSet([1])) != Marking(p=MultiSet([1]))
2693 False 2695 False
2694 >>> Marking(p=MultiSet([1])) != Marking(p=MultiSet([1, 1])) 2696 >>> Marking(p=MultiSet([1])) != Marking(p=MultiSet([1, 1]))
2695 True 2697 True
2696 >>> Marking(p1=MultiSet([1])) != Marking(p2=MultiSet([1])) 2698 >>> Marking(p1=MultiSet([1])) != Marking(p2=MultiSet([1]))
2697 True 2699 True
2698 - 2700 +
2699 @param other: another marking 2701 @param other: another marking
2700 @type other: `Marking` 2702 @type other: `Marking`
2701 @return: `True` if the markings differ, `False` otherwise 2703 @return: `True` if the markings differ, `False` otherwise
...@@ -2705,10 +2707,10 @@ class Marking (hdict) : ...@@ -2705,10 +2707,10 @@ class Marking (hdict) :
2705 def __ge__ (self, other) : 2707 def __ge__ (self, other) :
2706 """Test if the marking `self` is greater than or equal to 2708 """Test if the marking `self` is greater than or equal to
2707 `other`. 2709 `other`.
2708 - 2710 +
2709 This is the case when any place in `other` is also in `self` 2711 This is the case when any place in `other` is also in `self`
2710 and is marked with a smaller or equal multiset of tokens. 2712 and is marked with a smaller or equal multiset of tokens.
2711 - 2713 +
2712 >>> Marking(p=MultiSet([1])) >= Marking(p=MultiSet([1])) 2714 >>> Marking(p=MultiSet([1])) >= Marking(p=MultiSet([1]))
2713 True 2715 True
2714 >>> Marking(p=MultiSet([1, 1])) >= Marking(p=MultiSet([1])) 2716 >>> Marking(p=MultiSet([1, 1])) >= Marking(p=MultiSet([1]))
...@@ -2719,7 +2721,7 @@ class Marking (hdict) : ...@@ -2719,7 +2721,7 @@ class Marking (hdict) :
2719 False 2721 False
2720 >>> Marking(p=MultiSet([1]), r=MultiSet([2])) >= Marking(p=MultiSet([1, 1])) 2722 >>> Marking(p=MultiSet([1]), r=MultiSet([2])) >= Marking(p=MultiSet([1, 1]))
2721 False 2723 False
2722 - 2724 +
2723 @param other: another marking 2725 @param other: another marking
2724 @type other: `Marking` 2726 @type other: `Marking`
2725 @return: `True` if `self >= other`, `False` otherwise 2727 @return: `True` if `self >= other`, `False` otherwise
...@@ -2733,11 +2735,11 @@ class Marking (hdict) : ...@@ -2733,11 +2735,11 @@ class Marking (hdict) :
2733 return True 2735 return True
2734 def __gt__ (self, other) : 2736 def __gt__ (self, other) :
2735 """Test if the marking `self` is strictly greater than `other`. 2737 """Test if the marking `self` is strictly greater than `other`.
2736 - 2738 +
2737 This is the case when any place in `other` is also in `self` 2739 This is the case when any place in `other` is also in `self`
2738 and either one place in `other` is marked with a smaller 2740 and either one place in `other` is marked with a smaller
2739 multiset of tokens or `slef` has more places than `other`. 2741 multiset of tokens or `slef` has more places than `other`.
2740 - 2742 +
2741 >>> Marking(p=MultiSet([1])) > Marking(p=MultiSet([1])) 2743 >>> Marking(p=MultiSet([1])) > Marking(p=MultiSet([1]))
2742 False 2744 False
2743 >>> Marking(p=MultiSet([1, 1])) > Marking(p=MultiSet([1])) 2745 >>> Marking(p=MultiSet([1, 1])) > Marking(p=MultiSet([1]))
...@@ -2748,7 +2750,7 @@ class Marking (hdict) : ...@@ -2748,7 +2750,7 @@ class Marking (hdict) :
2748 False 2750 False
2749 >>> Marking(p=MultiSet([1]), r=MultiSet([2])) > Marking(p=MultiSet([1, 1])) 2751 >>> Marking(p=MultiSet([1]), r=MultiSet([2])) > Marking(p=MultiSet([1, 1]))
2750 False 2752 False
2751 - 2753 +
2752 @param other: another marking 2754 @param other: another marking
2753 @type other: `Marking` 2755 @type other: `Marking`
2754 @return: `True` if `self > other`, `False` otherwise 2756 @return: `True` if `self > other`, `False` otherwise
...@@ -2766,10 +2768,10 @@ class Marking (hdict) : ...@@ -2766,10 +2768,10 @@ class Marking (hdict) :
2766 def __le__ (self, other) : 2768 def __le__ (self, other) :
2767 """Test if the marking `self` is smaller than or equal to 2769 """Test if the marking `self` is smaller than or equal to
2768 `other`. 2770 `other`.
2769 - 2771 +
2770 This is the case when any place in `self` is also in `other` 2772 This is the case when any place in `self` is also in `other`
2771 and is marked with a smaller or equal multiset of tokens. 2773 and is marked with a smaller or equal multiset of tokens.
2772 - 2774 +
2773 >>> Marking(p=MultiSet([1])) <= Marking(p=MultiSet([1])) 2775 >>> Marking(p=MultiSet([1])) <= Marking(p=MultiSet([1]))
2774 True 2776 True
2775 >>> Marking(p=MultiSet([1])) <= Marking(p=MultiSet([1, 1])) 2777 >>> Marking(p=MultiSet([1])) <= Marking(p=MultiSet([1, 1]))
...@@ -2780,7 +2782,7 @@ class Marking (hdict) : ...@@ -2780,7 +2782,7 @@ class Marking (hdict) :
2780 False 2782 False
2781 >>> Marking(p=MultiSet([1, 1])) <= Marking(p=MultiSet([1]), r=MultiSet([2])) 2783 >>> Marking(p=MultiSet([1, 1])) <= Marking(p=MultiSet([1]), r=MultiSet([2]))
2782 False 2784 False
2783 - 2785 +
2784 @param other: another marking 2786 @param other: another marking
2785 @type other: `Marking` 2787 @type other: `Marking`
2786 @return: `True` if `self <= other`, `False` otherwise 2788 @return: `True` if `self <= other`, `False` otherwise
...@@ -2794,12 +2796,12 @@ class Marking (hdict) : ...@@ -2794,12 +2796,12 @@ class Marking (hdict) :
2794 return True 2796 return True
2795 def __lt__ (self, other) : 2797 def __lt__ (self, other) :
2796 """Test if the marking `self` is strictly smaller than `other`. 2798 """Test if the marking `self` is strictly smaller than `other`.
2797 - 2799 +
2798 This is the case when any place in `self` is also in `other` 2800 This is the case when any place in `self` is also in `other`
2799 and either one place in `self` marked in self with a strictly 2801 and either one place in `self` marked in self with a strictly
2800 smaller multiset of tokens or `other` has more places than 2802 smaller multiset of tokens or `other` has more places than
2801 `self`. 2803 `self`.
2802 - 2804 +
2803 >>> Marking(p=MultiSet([1])) < Marking(p=MultiSet([1])) 2805 >>> Marking(p=MultiSet([1])) < Marking(p=MultiSet([1]))
2804 False 2806 False
2805 >>> Marking(p=MultiSet([1])) < Marking(p=MultiSet([1, 1])) 2807 >>> Marking(p=MultiSet([1])) < Marking(p=MultiSet([1, 1]))
...@@ -2810,7 +2812,7 @@ class Marking (hdict) : ...@@ -2810,7 +2812,7 @@ class Marking (hdict) :
2810 False 2812 False
2811 >>> Marking(p=MultiSet([1, 1])) < Marking(p=MultiSet([1]), r=MultiSet([2])) 2813 >>> Marking(p=MultiSet([1, 1])) < Marking(p=MultiSet([1]), r=MultiSet([2]))
2812 False 2814 False
2813 - 2815 +
2814 @param other: another marking 2816 @param other: another marking
2815 @type other: `Marking` 2817 @type other: `Marking`
2816 @return: `True` if `self < other`, `False` otherwise 2818 @return: `True` if `self < other`, `False` otherwise
...@@ -2832,20 +2834,20 @@ class Marking (hdict) : ...@@ -2832,20 +2834,20 @@ class Marking (hdict) :
2832 2834
2833 class PetriNet (object) : 2835 class PetriNet (object) :
2834 """A Petri net. 2836 """A Petri net.
2835 - 2837 +
2836 As soon as nodes are added to a `PetriNet`, they are handled by 2838 As soon as nodes are added to a `PetriNet`, they are handled by
2837 name instead of by the `Place` or `Transition` instance. For 2839 name instead of by the `Place` or `Transition` instance. For
2838 instance: 2840 instance:
2839 - 2841 +
2840 >>> n = PetriNet('N') 2842 >>> n = PetriNet('N')
2841 >>> t = Transition('t') 2843 >>> t = Transition('t')
2842 >>> n.add_transition(t) 2844 >>> n.add_transition(t)
2843 >>> n.has_transition('t') # use 't' and not t 2845 >>> n.has_transition('t') # use 't' and not t
2844 True 2846 True
2845 - 2847 +
2846 Because nodes can be retreived through their name, this allows to 2848 Because nodes can be retreived through their name, this allows to
2847 rewrite the above code as: 2849 rewrite the above code as:
2848 - 2850 +
2849 >>> n = PetriNet('N') 2851 >>> n = PetriNet('N')
2850 >>> n.add_transition(Transition('t')) 2852 >>> n.add_transition(Transition('t'))
2851 >>> n.has_transition('t') # use 't' and not t 2853 >>> n.has_transition('t') # use 't' and not t
...@@ -2855,10 +2857,10 @@ class PetriNet (object) : ...@@ -2855,10 +2857,10 @@ class PetriNet (object) :
2855 """ 2857 """
2856 def __init__ (self, name) : 2858 def __init__ (self, name) :
2857 """Initialise with the name. 2859 """Initialise with the name.
2858 - 2860 +
2859 >>> PetriNet('N') 2861 >>> PetriNet('N')
2860 PetriNet('N') 2862 PetriNet('N')
2861 - 2863 +
2862 @param name: the name of the net 2864 @param name: the name of the net
2863 @type name: `str` 2865 @type name: `str`
2864 """ 2866 """
...@@ -2876,12 +2878,12 @@ class PetriNet (object) : ...@@ -2876,12 +2878,12 @@ class PetriNet (object) :
2876 def copy (self, name=None) : 2878 def copy (self, name=None) :
2877 """Return a complete copy of the net, including places, 2879 """Return a complete copy of the net, including places,
2878 transitions, arcs and declarations. 2880 transitions, arcs and declarations.
2879 - 2881 +
2880 >>> PetriNet('N').copy() 2882 >>> PetriNet('N').copy()
2881 PetriNet('N') 2883 PetriNet('N')
2882 >>> PetriNet('N').copy('x') 2884 >>> PetriNet('N').copy('x')
2883 PetriNet('x') 2885 PetriNet('x')
2884 - 2886 +
2885 @param name: if not `None`, the name of the copy 2887 @param name: if not `None`, the name of the copy
2886 @type name: `str` 2888 @type name: `str`
2887 @return: a copy of the net 2889 @return: a copy of the net
...@@ -2905,12 +2907,12 @@ class PetriNet (object) : ...@@ -2905,12 +2907,12 @@ class PetriNet (object) :
2905 @classmethod 2907 @classmethod
2906 def _pnml_dump_arc (cls, label) : 2908 def _pnml_dump_arc (cls, label) :
2907 """Dump an arc to PNML 2909 """Dump an arc to PNML
2908 - 2910 +
2909 @param label: the arc label to dump 2911 @param label: the arc label to dump
2910 @type label: `ArcAnnotation` 2912 @type label: `ArcAnnotation`
2911 @return: a tuple of PNML trees 2913 @return: a tuple of PNML trees
2912 @rtype: `tuple` of `pnml.Tree` 2914 @rtype: `tuple` of `pnml.Tree`
2913 - 2915 +
2914 >>> PetriNet._pnml_dump_arc(Value(dot)) 2916 >>> PetriNet._pnml_dump_arc(Value(dot))
2915 (<?xml version="1.0" encoding="utf-8"?> 2917 (<?xml version="1.0" encoding="utf-8"?>
2916 <pnml> 2918 <pnml>
...@@ -2969,10 +2971,10 @@ class PetriNet (object) : ...@@ -2969,10 +2971,10 @@ class PetriNet (object) :
2969 return (Tree("inscription", None, Tree.from_obj(label)),) 2971 return (Tree("inscription", None, Tree.from_obj(label)),)
2970 def __pnmldump__ (self) : 2972 def __pnmldump__ (self) :
2971 """Dump a `PetriNet` as a PNML tree 2973 """Dump a `PetriNet` as a PNML tree
2972 - 2974 +
2973 @return: PNML tree 2975 @return: PNML tree
2974 @rtype: `pnml.Tree` 2976 @rtype: `pnml.Tree`
2975 - 2977 +
2976 >>> n = PetriNet('N') 2978 >>> n = PetriNet('N')
2977 >>> n.declare('x = "foo" + "bar"') 2979 >>> n.declare('x = "foo" + "bar"')
2978 >>> n.globals['y'] = 'egg' 2980 >>> n.globals['y'] = 'egg'
...@@ -3057,12 +3059,12 @@ class PetriNet (object) : ...@@ -3057,12 +3059,12 @@ class PetriNet (object) :
3057 @classmethod 3059 @classmethod
3058 def __pnmlload__ (cls, tree) : 3060 def __pnmlload__ (cls, tree) :
3059 """Create a `PetriNet` from a PNML tree 3061 """Create a `PetriNet` from a PNML tree
3060 - 3062 +
3061 @param tree: the tree to convert 3063 @param tree: the tree to convert
3062 @type tree: `pnml.Tree` 3064 @type tree: `pnml.Tree`
3063 @return: the net built 3065 @return: the net built
3064 @rtype: `PetriNet` 3066 @rtype: `PetriNet`
3065 - 3067 +
3066 >>> old = PetriNet('N') 3068 >>> old = PetriNet('N')
3067 >>> old.declare('x = "foo" + "bar"') 3069 >>> old.declare('x = "foo" + "bar"')
3068 >>> old.globals['y'] = 'egg' 3070 >>> old.globals['y'] = 'egg'
...@@ -3124,46 +3126,46 @@ class PetriNet (object) : ...@@ -3124,46 +3126,46 @@ class PetriNet (object) :
3124 return result 3126 return result
3125 def __repr__ (self) : 3127 def __repr__ (self) :
3126 """Return a string suitable for `eval` to represent the net. 3128 """Return a string suitable for `eval` to represent the net.
3127 - 3129 +
3128 >>> repr(PetriNet('N')) 3130 >>> repr(PetriNet('N'))
3129 "PetriNet('N')" 3131 "PetriNet('N')"
3130 - 3132 +
3131 @return: the textual representation of the net 3133 @return: the textual representation of the net
3132 @rtype: `str` 3134 @rtype: `str`
3133 """ 3135 """
3134 return "%s(%s)" % (self.__class__.__name__, repr(self.name)) 3136 return "%s(%s)" % (self.__class__.__name__, repr(self.name))
3135 def __str__ (self) : 3137 def __str__ (self) :
3136 """Return the name of the net. 3138 """Return the name of the net.
3137 - 3139 +
3138 >>> str(PetriNet('N')) 3140 >>> str(PetriNet('N'))
3139 'N' 3141 'N'
3140 - 3142 +
3141 @return: the name if the net 3143 @return: the name if the net
3142 @rtype: `str` 3144 @rtype: `str`
3143 """ 3145 """
3144 return self.name 3146 return self.name
3145 def rename (self, name) : 3147 def rename (self, name) :
3146 """Change the name of the net. 3148 """Change the name of the net.
3147 - 3149 +
3148 >>> n = PetriNet('N') 3150 >>> n = PetriNet('N')
3149 >>> n.rename('Long name!') 3151 >>> n.rename('Long name!')
3150 >>> n 3152 >>> n
3151 PetriNet('Long name!') 3153 PetriNet('Long name!')
3152 - 3154 +
3153 @param name: the new name 3155 @param name: the new name
3154 @type name: `str` 3156 @type name: `str`
3155 """ 3157 """
3156 self.name = name 3158 self.name = name
3157 def has_place (self, name) : 3159 def has_place (self, name) :
3158 """Check if there is a place called `name` in the net. 3160 """Check if there is a place called `name` in the net.
3159 - 3161 +
3160 >>> n = PetriNet('N') 3162 >>> n = PetriNet('N')
3161 >>> n.has_place('p') 3163 >>> n.has_place('p')
3162 False 3164 False
3163 >>> n.add_place(Place('p')) 3165 >>> n.add_place(Place('p'))
3164 >>> n.has_place('p') 3166 >>> n.has_place('p')
3165 True 3167 True
3166 - 3168 +
3167 @param name: the name of the searched place 3169 @param name: the name of the searched place
3168 @type name: `str` 3170 @type name: `str`
3169 @return: `True` if a place called `name` is present in the 3171 @return: `True` if a place called `name` is present in the
...@@ -3173,14 +3175,14 @@ class PetriNet (object) : ...@@ -3173,14 +3175,14 @@ class PetriNet (object) :
3173 return name in self._place 3175 return name in self._place
3174 def has_transition (self, name) : 3176 def has_transition (self, name) :
3175 """Check if there is a transition called `name` in the net. 3177 """Check if there is a transition called `name` in the net.
3176 - 3178 +
3177 >>> n = PetriNet('N') 3179 >>> n = PetriNet('N')
3178 >>> n.has_transition('t') 3180 >>> n.has_transition('t')
3179 False 3181 False
3180 >>> n.add_transition(Transition('t')) 3182 >>> n.add_transition(Transition('t'))
3181 >>> n.has_transition('t') 3183 >>> n.has_transition('t')
3182 True 3184 True
3183 - 3185 +
3184 @param name: the name of the searched transition 3186 @param name: the name of the searched transition
3185 @type name: `str` 3187 @type name: `str`
3186 @return: `True` if a transition called `name` is present in 3188 @return: `True` if a transition called `name` is present in
...@@ -3190,7 +3192,7 @@ class PetriNet (object) : ...@@ -3190,7 +3192,7 @@ class PetriNet (object) :
3190 return name in self._trans 3192 return name in self._trans
3191 def has_node (self, name) : 3193 def has_node (self, name) :
3192 """Check if there is a transition called `name` in the net. 3194 """Check if there is a transition called `name` in the net.
3193 - 3195 +
3194 >>> n = PetriNet('N') 3196 >>> n = PetriNet('N')
3195 >>> n.has_node('t') 3197 >>> n.has_node('t')
3196 False 3198 False
...@@ -3202,7 +3204,7 @@ class PetriNet (object) : ...@@ -3202,7 +3204,7 @@ class PetriNet (object) :
3202 True 3204 True
3203 >>> n.has_node('p') 3205 >>> n.has_node('p')
3204 True 3206 True
3205 - 3207 +
3206 @param name: the name of the searched node 3208 @param name: the name of the searched node
3207 @type name: `str` 3209 @type name: `str`
3208 @return: `True` if a node called `name` is present in the net, 3210 @return: `True` if a node called `name` is present in the net,
...@@ -3212,7 +3214,7 @@ class PetriNet (object) : ...@@ -3212,7 +3214,7 @@ class PetriNet (object) :
3212 return name in self._node 3214 return name in self._node
3213 def __contains__ (self, name) : 3215 def __contains__ (self, name) :
3214 """`name in net` is a shortcut for `net.has_node(name)` 3216 """`name in net` is a shortcut for `net.has_node(name)`
3215 - 3217 +
3216 >>> n = PetriNet('N') 3218 >>> n = PetriNet('N')
3217 >>> 't' in n 3219 >>> 't' in n
3218 False 3220 False
...@@ -3224,7 +3226,7 @@ class PetriNet (object) : ...@@ -3224,7 +3226,7 @@ class PetriNet (object) :
3224 True 3226 True
3225 >>> 'p' in n 3227 >>> 'p' in n
3226 True 3228 True
3227 - 3229 +
3228 @param name: the name of the searched node 3230 @param name: the name of the searched node
3229 @type name: `str` 3231 @type name: `str`
3230 @return: `True` if a node called `name` is present in the net, 3232 @return: `True` if a node called `name` is present in the net,
...@@ -3234,7 +3236,7 @@ class PetriNet (object) : ...@@ -3234,7 +3236,7 @@ class PetriNet (object) :
3234 return name in self._node 3236 return name in self._node
3235 def declare (self, statements, locals=None) : 3237 def declare (self, statements, locals=None) :
3236 """Execute `statements` in the global dictionnary of the net. 3238 """Execute `statements` in the global dictionnary of the net.
3237 - 3239 +
3238 This has also on the dictionnarie of the instances of 3240 This has also on the dictionnarie of the instances of
3239 `Expression` in the net (guards of the transitions and labels 3241 `Expression` in the net (guards of the transitions and labels
3240 on the arcs) so the declarations have an influence over the 3242 on the arcs) so the declarations have an influence over the
...@@ -3242,7 +3244,7 @@ class PetriNet (object) : ...@@ -3242,7 +3244,7 @@ class PetriNet (object) :
3242 the declared objects will be placed in it instead of the 3244 the declared objects will be placed in it instead of the
3243 global dictionnary, see the documentation of Python for more 3245 global dictionnary, see the documentation of Python for more
3244 details about local and global environments. 3246 details about local and global environments.
3245 - 3247 +
3246 >>> n = PetriNet('N') 3248 >>> n = PetriNet('N')
3247 >>> t = Transition('t', Expression('x==0')) 3249 >>> t = Transition('t', Expression('x==0'))
3248 >>> n.add_transition(t) 3250 >>> n.add_transition(t)
...@@ -3265,7 +3267,7 @@ class PetriNet (object) : ...@@ -3265,7 +3267,7 @@ class PetriNet (object) :
3265 >>> t.fire(Substitution()) 3267 >>> t.fire(Substitution())
3266 >>> n.place('p') 3268 >>> n.place('p')
3267 Place('p', MultiSet([0...]), tAll) 3269 Place('p', MultiSet([0...]), tAll)
3268 - 3270 +
3269 @param statements: a Python instruction suitable to `exec` 3271 @param statements: a Python instruction suitable to `exec`
3270 @type statements: `str` or `code` 3272 @type statements: `str` or `code`
3271 @param locals: a `dict` used as locals when `statements` is 3273 @param locals: a `dict` used as locals when `statements` is
...@@ -3276,10 +3278,10 @@ class PetriNet (object) : ...@@ -3276,10 +3278,10 @@ class PetriNet (object) :
3276 self._declare.append(statements) 3278 self._declare.append(statements)
3277 def add_place (self, place) : 3279 def add_place (self, place) :
3278 """Add a place to the net. 3280 """Add a place to the net.
3279 - 3281 +
3280 Each node in a net must have a name unique to this net, which 3282 Each node in a net must have a name unique to this net, which
3281 is checked when it is added. 3283 is checked when it is added.
3282 - 3284 +
3283 >>> n = PetriNet('N') 3285 >>> n = PetriNet('N')
3284 >>> try : n.place('p') 3286 >>> try : n.place('p')
3285 ... except ConstraintError : print(sys.exc_info()[1]) 3287 ... except ConstraintError : print(sys.exc_info()[1])
...@@ -3292,7 +3294,7 @@ class PetriNet (object) : ...@@ -3292,7 +3294,7 @@ class PetriNet (object) :
3292 >>> try : n.add_place(Place('p')) 3294 >>> try : n.add_place(Place('p'))
3293 ... except ConstraintError : print(sys.exc_info()[1]) 3295 ... except ConstraintError : print(sys.exc_info()[1])
3294 place 'p' exists 3296 place 'p' exists
3295 - 3297 +
3296 @param place: the place to add 3298 @param place: the place to add
3297 @type place: `Place` 3299 @type place: `Place`
3298 """ 3300 """
...@@ -3308,7 +3310,7 @@ class PetriNet (object) : ...@@ -3308,7 +3310,7 @@ class PetriNet (object) :
3308 place.lock("post", self, {}) 3310 place.lock("post", self, {})
3309 def remove_place (self, name) : 3311 def remove_place (self, name) :
3310 """Remove a place (given by its name) from the net. 3312 """Remove a place (given by its name) from the net.
3311 - 3313 +
3312 >>> n = PetriNet('N') 3314 >>> n = PetriNet('N')
3313 >>> try : n.remove_place('p') 3315 >>> try : n.remove_place('p')
3314 ... except ConstraintError : print(sys.exc_info()[1]) 3316 ... except ConstraintError : print(sys.exc_info()[1])
...@@ -3320,7 +3322,7 @@ class PetriNet (object) : ...@@ -3320,7 +3322,7 @@ class PetriNet (object) :
3320 >>> try : n.place('p') 3322 >>> try : n.place('p')
3321 ... except ConstraintError : print(sys.exc_info()[1]) 3323 ... except ConstraintError : print(sys.exc_info()[1])
3322 place 'p' not found 3324 place 'p' not found
3323 - 3325 +
3324 @param name: the name of the place to remove 3326 @param name: the name of the place to remove
3325 @type name: `str` 3327 @type name: `str`
3326 """ 3328 """
...@@ -3340,10 +3342,10 @@ class PetriNet (object) : ...@@ -3340,10 +3342,10 @@ class PetriNet (object) :
3340 place.unlock("net", self, remove=True) 3342 place.unlock("net", self, remove=True)
3341 def add_transition (self, trans) : 3343 def add_transition (self, trans) :
3342 """Add a transition to the net. 3344 """Add a transition to the net.
3343 - 3345 +
3344 Each node in a net must have a name unique to this net, which 3346 Each node in a net must have a name unique to this net, which
3345 is checked when it is added. 3347 is checked when it is added.
3346 - 3348 +
3347 >>> n = PetriNet('N') 3349 >>> n = PetriNet('N')
3348 >>> try : n.transition('t') 3350 >>> try : n.transition('t')
3349 ... except ConstraintError : print(sys.exc_info()[1]) 3351 ... except ConstraintError : print(sys.exc_info()[1])
...@@ -3354,7 +3356,7 @@ class PetriNet (object) : ...@@ -3354,7 +3356,7 @@ class PetriNet (object) :
3354 >>> try : n.add_transition(Transition('t')) 3356 >>> try : n.add_transition(Transition('t'))
3355 ... except ConstraintError : print(sys.exc_info()[1]) 3357 ... except ConstraintError : print(sys.exc_info()[1])
3356 transition 't' exists 3358 transition 't' exists
3357 - 3359 +
3358 @param trans: the transition to add 3360 @param trans: the transition to add
3359 @type trans: `Transition` 3361 @type trans: `Transition`
3360 """ 3362 """
...@@ -3371,7 +3373,7 @@ class PetriNet (object) : ...@@ -3371,7 +3373,7 @@ class PetriNet (object) :
3371 trans.guard.globals.attach(self.globals) 3373 trans.guard.globals.attach(self.globals)
3372 def remove_transition (self, name) : 3374 def remove_transition (self, name) :
3373 """Remove a transition (given by its name) from the net. 3375 """Remove a transition (given by its name) from the net.
3374 - 3376 +
3375 >>> n = PetriNet('N') 3377 >>> n = PetriNet('N')
3376 >>> try : n.remove_transition('t') 3378 >>> try : n.remove_transition('t')
3377 ... except ConstraintError : print(sys.exc_info()[1]) 3379 ... except ConstraintError : print(sys.exc_info()[1])
...@@ -3383,7 +3385,7 @@ class PetriNet (object) : ...@@ -3383,7 +3385,7 @@ class PetriNet (object) :
3383 >>> try : n.transition('t') 3385 >>> try : n.transition('t')
3384 ... except ConstraintError : print(sys.exc_info()[1]) 3386 ... except ConstraintError : print(sys.exc_info()[1])
3385 transition 't' not found 3387 transition 't' not found
3386 - 3388 +
3387 @param name: the name of the transition to remove 3389 @param name: the name of the transition to remove
3388 @type name: `str` 3390 @type name: `str`
3389 """ 3391 """
...@@ -3404,7 +3406,7 @@ class PetriNet (object) : ...@@ -3404,7 +3406,7 @@ class PetriNet (object) :
3404 trans.guard.globals.detach(self.globals) 3406 trans.guard.globals.detach(self.globals)
3405 def place (self, name=None) : 3407 def place (self, name=None) :
3406 """Return one (if `name` is not `None`) or all the places. 3408 """Return one (if `name` is not `None`) or all the places.
3407 - 3409 +
3408 >>> n = PetriNet('N') 3410 >>> n = PetriNet('N')
3409 >>> n.add_place(Place('p1')) 3411 >>> n.add_place(Place('p1'))
3410 >>> n.add_place(Place('p2')) 3412 >>> n.add_place(Place('p2'))
...@@ -3415,7 +3417,7 @@ class PetriNet (object) : ...@@ -3415,7 +3417,7 @@ class PetriNet (object) :
3415 place 'p' not found 3417 place 'p' not found
3416 >>> n.place() 3418 >>> n.place()
3417 [Place('p2', MultiSet([]), tAll), Place('p1', MultiSet([]), tAll)] 3419 [Place('p2', MultiSet([]), tAll), Place('p1', MultiSet([]), tAll)]
3418 - 3420 +
3419 @param name: the name of the place to retrieve or `None` to 3421 @param name: the name of the place to retrieve or `None` to
3420 get the list of all the places in the net 3422 get the list of all the places in the net
3421 @type name: `str` or `None` 3423 @type name: `str` or `None`
...@@ -3431,7 +3433,7 @@ class PetriNet (object) : ...@@ -3431,7 +3433,7 @@ class PetriNet (object) :
3431 raise ConstraintError("place '%s' not found" % name) 3433 raise ConstraintError("place '%s' not found" % name)
3432 def transition (self, name=None) : 3434 def transition (self, name=None) :
3433 """Return one (if `name` is not `None`) or all the transitions. 3435 """Return one (if `name` is not `None`) or all the transitions.
3434 - 3436 +
3435 >>> n = PetriNet('N') 3437 >>> n = PetriNet('N')
3436 >>> n.add_transition(Transition('t1')) 3438 >>> n.add_transition(Transition('t1'))
3437 >>> n.add_transition(Transition('t2')) 3439 >>> n.add_transition(Transition('t2'))
...@@ -3442,7 +3444,7 @@ class PetriNet (object) : ...@@ -3442,7 +3444,7 @@ class PetriNet (object) :
3442 transition 't' not found 3444 transition 't' not found
3443 >>> n.transition() 3445 >>> n.transition()
3444 [Transition('t2', Expression('True')), Transition('t1', Expression('True'))] 3446 [Transition('t2', Expression('True')), Transition('t1', Expression('True'))]
3445 - 3447 +
3446 @param name: the name of the transition to retrieve or `None` 3448 @param name: the name of the transition to retrieve or `None`
3447 to get the list of all the transitions in the net 3449 to get the list of all the transitions in the net
3448 @type name: `str` or `None` 3450 @type name: `str` or `None`
...@@ -3459,7 +3461,7 @@ class PetriNet (object) : ...@@ -3459,7 +3461,7 @@ class PetriNet (object) :
3459 raise ConstraintError("transition '%s' not found" % name) 3461 raise ConstraintError("transition '%s' not found" % name)
3460 def node (self, name=None) : 3462 def node (self, name=None) :
3461 """Return one (if `name` is not `None`) or all the nodes. 3463 """Return one (if `name` is not `None`) or all the nodes.
3462 - 3464 +
3463 >>> n = PetriNet('N') 3465 >>> n = PetriNet('N')
3464 >>> n.add_transition(Transition('t')) 3466 >>> n.add_transition(Transition('t'))
3465 >>> n.add_place(Place('p')) 3467 >>> n.add_place(Place('p'))
...@@ -3470,7 +3472,7 @@ class PetriNet (object) : ...@@ -3470,7 +3472,7 @@ class PetriNet (object) :
3470 node 'x' not found 3472 node 'x' not found
3471 >>> list(sorted(n.node(), key=str)) 3473 >>> list(sorted(n.node(), key=str))
3472 [Place('p', MultiSet([]), tAll), Transition('t', Expression('True'))] 3474 [Place('p', MultiSet([]), tAll), Transition('t', Expression('True'))]
3473 - 3475 +
3474 @param name: the name of the node to retrieve or `None` to get 3476 @param name: the name of the node to retrieve or `None` to get
3475 the list of all the nodes in the net 3477 the list of all the nodes in the net
3476 @type name: `str` or `None` 3478 @type name: `str` or `None`
...@@ -3487,9 +3489,9 @@ class PetriNet (object) : ...@@ -3487,9 +3489,9 @@ class PetriNet (object) :
3487 raise ConstraintError("node '%s' not found" % name) 3489 raise ConstraintError("node '%s' not found" % name)
3488 def add_input (self, place, trans, label) : 3490 def add_input (self, place, trans, label) :
3489 """Add an input arc between `place` and `trans` (nodes names). 3491 """Add an input arc between `place` and `trans` (nodes names).
3490 - 3492 +
3491 An input arc is directed from a place toward a transition. 3493 An input arc is directed from a place toward a transition.
3492 - 3494 +
3493 >>> n = PetriNet('N') 3495 >>> n = PetriNet('N')
3494 >>> n.add_place(Place('p', range(3))) 3496 >>> n.add_place(Place('p', range(3)))
3495 >>> n.add_transition(Transition('t', Expression('x!=1'))) 3497 >>> n.add_transition(Transition('t', Expression('x!=1')))
...@@ -3509,7 +3511,7 @@ class PetriNet (object) : ...@@ -3509,7 +3511,7 @@ class PetriNet (object) :
3509 >>> try : n.add_input('p', 't', Value(42)) 3511 >>> try : n.add_input('p', 't', Value(42))
3510 ... except ConstraintError: print(sys.exc_info()[1]) 3512 ... except ConstraintError: print(sys.exc_info()[1])
3511 already connected to 'p' 3513 already connected to 'p'
3512 - 3514 +
3513 @param place: the name of the place to connect 3515 @param place: the name of the place to connect
3514 @type place: `str` 3516 @type place: `str`
3515 @param trans: the name of the transition to connect 3517 @param trans: the name of the transition to connect
...@@ -3534,7 +3536,7 @@ class PetriNet (object) : ...@@ -3534,7 +3536,7 @@ class PetriNet (object) :
3534 label.globals.attach(self.globals) 3536 label.globals.attach(self.globals)
3535 def remove_input (self, place, trans) : 3537 def remove_input (self, place, trans) :
3536 """Remove an input arc between `place` and `trans` (nodes names). 3538 """Remove an input arc between `place` and `trans` (nodes names).
3537 - 3539 +
3538 >>> n = PetriNet('N') 3540 >>> n = PetriNet('N')
3539 >>> n.add_place(Place('p', range(3))) 3541 >>> n.add_place(Place('p', range(3)))
3540 >>> n.add_transition(Transition('t', Expression('x!=1'))) 3542 >>> n.add_transition(Transition('t', Expression('x!=1')))
...@@ -3547,7 +3549,7 @@ class PetriNet (object) : ...@@ -3547,7 +3549,7 @@ class PetriNet (object) :
3547 >>> try : n.remove_input('p', 't') 3549 >>> try : n.remove_input('p', 't')
3548 ... except ConstraintError : print(sys.exc_info()[1]) 3550 ... except ConstraintError : print(sys.exc_info()[1])
3549 not connected to 'p' 3551 not connected to 'p'
3550 - 3552 +
3551 @param place: the name of the place to disconnect 3553 @param place: the name of the place to disconnect
3552 @type place: `str` 3554 @type place: `str`
3553 @param trans: the name of the transition to disconnect 3555 @param trans: the name of the transition to disconnect
...@@ -3569,9 +3571,9 @@ class PetriNet (object) : ...@@ -3569,9 +3571,9 @@ class PetriNet (object) :
3569 l.globals.detach(self.globals) 3571 l.globals.detach(self.globals)
3570 def add_output (self, place, trans, label) : 3572 def add_output (self, place, trans, label) :
3571 """Add an output arc between `place` and `trans` (nodes names). 3573 """Add an output arc between `place` and `trans` (nodes names).
3572 - 3574 +
3573 An output arc is directed from a transition toward a place. 3575 An output arc is directed from a transition toward a place.
3574 - 3576 +
3575 >>> n = PetriNet('N') 3577 >>> n = PetriNet('N')
3576 >>> n.add_place(Place('p')) 3578 >>> n.add_place(Place('p'))
3577 >>> n.add_transition(Transition('t')) 3579 >>> n.add_transition(Transition('t'))
...@@ -3586,7 +3588,7 @@ class PetriNet (object) : ...@@ -3586,7 +3588,7 @@ class PetriNet (object) :
3586 >>> try : n.add_output('p', 't', Value(42)) 3588 >>> try : n.add_output('p', 't', Value(42))
3587 ... except ConstraintError : print(sys.exc_info()[1]) 3589 ... except ConstraintError : print(sys.exc_info()[1])
3588 already connected to 'p' 3590 already connected to 'p'
3589 - 3591 +
3590 @param place: the name of the place to connect 3592 @param place: the name of the place to connect
3591 @type place: `str` 3593 @type place: `str`
3592 @param trans: the name of the transition to connect 3594 @param trans: the name of the transition to connect
...@@ -3610,7 +3612,7 @@ class PetriNet (object) : ...@@ -3610,7 +3612,7 @@ class PetriNet (object) :
3610 def remove_output (self, place, trans) : 3612 def remove_output (self, place, trans) :
3611 """Remove an output arc between `place` and `trans` (nodes 3613 """Remove an output arc between `place` and `trans` (nodes
3612 names). 3614 names).
3613 - 3615 +
3614 >>> n = PetriNet('N') 3616 >>> n = PetriNet('N')
3615 >>> n.add_place(Place('p')) 3617 >>> n.add_place(Place('p'))
3616 >>> n.add_transition(Transition('t')) 3618 >>> n.add_transition(Transition('t'))
...@@ -3623,7 +3625,7 @@ class PetriNet (object) : ...@@ -3623,7 +3625,7 @@ class PetriNet (object) :
3623 >>> try : n.remove_output('p', 't') 3625 >>> try : n.remove_output('p', 't')
3624 ... except ConstraintError : print(sys.exc_info()[1]) 3626 ... except ConstraintError : print(sys.exc_info()[1])
3625 not connected to 'p' 3627 not connected to 'p'
3626 - 3628 +
3627 @param place: the name of the place to disconnect 3629 @param place: the name of the place to disconnect
3628 @type place: `str` 3630 @type place: `str`
3629 @param trans: the name of the transition to disconnect 3631 @param trans: the name of the transition to disconnect
...@@ -3645,9 +3647,9 @@ class PetriNet (object) : ...@@ -3645,9 +3647,9 @@ class PetriNet (object) :
3645 l.globals.detach(self.globals) 3647 l.globals.detach(self.globals)
3646 def pre (self, nodes) : 3648 def pre (self, nodes) :
3647 """Return the set of nodes names preceeding `nodes`. 3649 """Return the set of nodes names preceeding `nodes`.
3648 - 3650 +
3649 `nodes` can be a single node name ot a list of nodes names. 3651 `nodes` can be a single node name ot a list of nodes names.
3650 - 3652 +
3651 >>> n = PetriNet('N') 3653 >>> n = PetriNet('N')
3652 >>> n.add_place(Place('p1')) 3654 >>> n.add_place(Place('p1'))
3653 >>> n.add_place(Place('p2')) 3655 >>> n.add_place(Place('p2'))
...@@ -3659,7 +3661,7 @@ class PetriNet (object) : ...@@ -3659,7 +3661,7 @@ class PetriNet (object) :
3659 True 3661 True
3660 >>> n.pre(['p1', 'p2']) == set(['t2', 't1']) 3662 >>> n.pre(['p1', 'p2']) == set(['t2', 't1'])
3661 True 3663 True
3662 - 3664 +
3663 @param nodes: a single node name or a list of node names 3665 @param nodes: a single node name or a list of node names
3664 @type nodes: `str` or a `list` of `str` 3666 @type nodes: `str` or a `list` of `str`
3665 @return: a set of node names 3667 @return: a set of node names
...@@ -3674,9 +3676,9 @@ class PetriNet (object) : ...@@ -3674,9 +3676,9 @@ class PetriNet (object) :
3674 return result 3676 return result
3675 def post (self, nodes) : 3677 def post (self, nodes) :
3676 """Return the set of nodes names succeeding `nodes`. 3678 """Return the set of nodes names succeeding `nodes`.
3677 - 3679 +
3678 `nodes` can be a single node name ot a list of nodes names. 3680 `nodes` can be a single node name ot a list of nodes names.
3679 - 3681 +
3680 >>> n = PetriNet('N') 3682 >>> n = PetriNet('N')
3681 >>> n.add_place(Place('p1')) 3683 >>> n.add_place(Place('p1'))
3682 >>> n.add_place(Place('p2')) 3684 >>> n.add_place(Place('p2'))
...@@ -3688,7 +3690,7 @@ class PetriNet (object) : ...@@ -3688,7 +3690,7 @@ class PetriNet (object) :
3688 True 3690 True
3689 >>> n.post(['t1', 't2']) == set(['p2', 'p1']) 3691 >>> n.post(['t1', 't2']) == set(['p2', 'p1'])
3690 True 3692 True
3691 - 3693 +
3692 @param nodes: a single node name or a list of node names 3694 @param nodes: a single node name or a list of node names
3693 @type nodes: `str` or a `list` of `str` 3695 @type nodes: `str` or a `list` of `str`
3694 @return: a set of node names 3696 @return: a set of node names
...@@ -3703,7 +3705,7 @@ class PetriNet (object) : ...@@ -3703,7 +3705,7 @@ class PetriNet (object) :
3703 return result 3705 return result
3704 def get_marking (self) : 3706 def get_marking (self) :
3705 """Return the current marking of the net, omitting empty places. 3707 """Return the current marking of the net, omitting empty places.
3706 - 3708 +
3707 >>> n = PetriNet('N') 3709 >>> n = PetriNet('N')
3708 >>> n.add_place(Place('p0', range(0))) 3710 >>> n.add_place(Place('p0', range(0)))
3709 >>> n.add_place(Place('p1', range(1))) 3711 >>> n.add_place(Place('p1', range(1)))
...@@ -3711,7 +3713,7 @@ class PetriNet (object) : ...@@ -3711,7 +3713,7 @@ class PetriNet (object) :
3711 >>> n.add_place(Place('p3', range(3))) 3713 >>> n.add_place(Place('p3', range(3)))
3712 >>> n.get_marking() == Marking({'p2': MultiSet([0, 1]), 'p3': MultiSet([0, 1, 2]), 'p1': MultiSet([0])}) 3714 >>> n.get_marking() == Marking({'p2': MultiSet([0, 1]), 'p3': MultiSet([0, 1, 2]), 'p1': MultiSet([0])})
3713 True 3715 True
3714 - 3716 +
3715 @return: the current marking 3717 @return: the current marking
3716 @rtype: `Marking` 3718 @rtype: `Marking`
3717 """ 3719 """
...@@ -3720,14 +3722,14 @@ class PetriNet (object) : ...@@ -3720,14 +3722,14 @@ class PetriNet (object) :
3720 if not place.is_empty()) 3722 if not place.is_empty())
3721 def _set_marking (self, marking) : 3723 def _set_marking (self, marking) :
3722 """Assign a marking to the net. 3724 """Assign a marking to the net.
3723 - 3725 +
3724 Places not listed in the marking are considered empty, the 3726 Places not listed in the marking are considered empty, the
3725 corresponding place in the net is thus emptied. If the marking 3727 corresponding place in the net is thus emptied. If the marking
3726 has places that do not belong to the net, these are ignored 3728 has places that do not belong to the net, these are ignored
3727 (as in the last instruction below). If an error occurs during 3729 (as in the last instruction below). If an error occurs during
3728 the assignment, the marking is left inconsistent. You should 3730 the assignment, the marking is left inconsistent. You should
3729 thus use `set_marking` unless you will have no error. 3731 thus use `set_marking` unless you will have no error.
3730 - 3732 +
3731 >>> n = PetriNet('N') 3733 >>> n = PetriNet('N')
3732 >>> n.add_place(Place('p0', range(5))) 3734 >>> n.add_place(Place('p0', range(5)))
3733 >>> n.add_place(Place('p1')) 3735 >>> n.add_place(Place('p1'))
...@@ -3746,7 +3748,7 @@ class PetriNet (object) : ...@@ -3746,7 +3748,7 @@ class PetriNet (object) :
3746 forbidden token '3.14' 3748 forbidden token '3.14'
3747 >>> n.get_marking() # inconsistent 3749 >>> n.get_marking() # inconsistent
3748 Marking({'p2': MultiSet([1])}) 3750 Marking({'p2': MultiSet([1])})
3749 - 3751 +
3750 @param marking: the new marking 3752 @param marking: the new marking
3751 @type marking: `Marking` 3753 @type marking: `Marking`
3752 """ 3754 """
...@@ -3757,13 +3759,13 @@ class PetriNet (object) : ...@@ -3757,13 +3759,13 @@ class PetriNet (object) :
3757 place.empty() 3759 place.empty()
3758 def set_marking (self, marking) : 3760 def set_marking (self, marking) :
3759 """Assign a marking to the net. 3761 """Assign a marking to the net.
3760 - 3762 +
3761 Places not listed in the marking are considered empty, the 3763 Places not listed in the marking are considered empty, the
3762 corresponding place in the net is thus emptied. If the marking 3764 corresponding place in the net is thus emptied. If the marking
3763 has places that do not belong to the net, these are ignored 3765 has places that do not belong to the net, these are ignored
3764 (as in the last instruction below). If an error occurs during 3766 (as in the last instruction below). If an error occurs during
3765 the assignment, the marking is left unchanged. 3767 the assignment, the marking is left unchanged.
3766 - 3768 +
3767 >>> n = PetriNet('N') 3769 >>> n = PetriNet('N')
3768 >>> n.add_place(Place('p0', range(5), tInteger)) 3770 >>> n.add_place(Place('p0', range(5), tInteger))
3769 >>> n.add_place(Place('p1')) 3771 >>> n.add_place(Place('p1'))
...@@ -3782,7 +3784,7 @@ class PetriNet (object) : ...@@ -3782,7 +3784,7 @@ class PetriNet (object) :
3782 forbidden token '3.14' 3784 forbidden token '3.14'
3783 >>> n.get_marking() # unchanged 3785 >>> n.get_marking() # unchanged
3784 Marking({}) 3786 Marking({})
3785 - 3787 +
3786 @param marking: the new marking 3788 @param marking: the new marking
3787 @type marking: `Marking` 3789 @type marking: `Marking`
3788 """ 3790 """
...@@ -3794,11 +3796,11 @@ class PetriNet (object) : ...@@ -3794,11 +3796,11 @@ class PetriNet (object) :
3794 raise 3796 raise
3795 def add_marking (self, marking) : 3797 def add_marking (self, marking) :
3796 """Add a marking to the current one. 3798 """Add a marking to the current one.
3797 - 3799 +
3798 If an error occurs during the process, the marking is left 3800 If an error occurs during the process, the marking is left
3799 unchanged. Places in the marking that do not belong to the net 3801 unchanged. Places in the marking that do not belong to the net
3800 are ignored. 3802 are ignored.
3801 - 3803 +
3802 >>> n = PetriNet('N') 3804 >>> n = PetriNet('N')
3803 >>> n.add_place(Place('p1')) 3805 >>> n.add_place(Place('p1'))
3804 >>> n.add_place(Place('p2', range(3))) 3806 >>> n.add_place(Place('p2', range(3)))
...@@ -3807,7 +3809,7 @@ class PetriNet (object) : ...@@ -3807,7 +3809,7 @@ class PetriNet (object) :
3807 >>> n.add_marking(Marking(p1=MultiSet(range(2)), p2=MultiSet([1]))) 3809 >>> n.add_marking(Marking(p1=MultiSet(range(2)), p2=MultiSet([1])))
3808 >>> n.get_marking() == Marking({'p2': MultiSet([0, 1, 1, 2]), 'p1': MultiSet([0, 1])}) 3810 >>> n.get_marking() == Marking({'p2': MultiSet([0, 1, 1, 2]), 'p1': MultiSet([0, 1])})
3809 True 3811 True
3810 - 3812 +
3811 @param marking: the new marking 3813 @param marking: the new marking
3812 @type marking: `Marking` 3814 @type marking: `Marking`
3813 """ 3815 """
...@@ -3821,11 +3823,11 @@ class PetriNet (object) : ...@@ -3821,11 +3823,11 @@ class PetriNet (object) :
3821 raise 3823 raise
3822 def remove_marking (self, marking) : 3824 def remove_marking (self, marking) :
3823 """Substract a marking from the current one. 3825 """Substract a marking from the current one.
3824 - 3826 +
3825 If an error occurs during the process, the marking is left 3827 If an error occurs during the process, the marking is left
3826 unchanged. Places in the marking that do not belong to the net 3828 unchanged. Places in the marking that do not belong to the net
3827 are ignored. 3829 are ignored.
3828 - 3830 +
3829 >>> n = PetriNet('N') 3831 >>> n = PetriNet('N')
3830 >>> n.add_place(Place('p1')) 3832 >>> n.add_place(Place('p1'))
3831 >>> n.add_place(Place('p2', range(3))) 3833 >>> n.add_place(Place('p2', range(3)))
...@@ -3839,7 +3841,7 @@ class PetriNet (object) : ...@@ -3839,7 +3841,7 @@ class PetriNet (object) :
3839 >>> n.remove_marking(Marking(p2=MultiSet([1]))) 3841 >>> n.remove_marking(Marking(p2=MultiSet([1])))
3840 >>> n.get_marking() == Marking({'p2': MultiSet([0, 2])}) 3842 >>> n.get_marking() == Marking({'p2': MultiSet([0, 2])})
3841 True 3843 True
3842 - 3844 +
3843 @param marking: the new marking 3845 @param marking: the new marking
3844 @type marking: `Marking` 3846 @type marking: `Marking`
3845 """ 3847 """
...@@ -3853,7 +3855,7 @@ class PetriNet (object) : ...@@ -3853,7 +3855,7 @@ class PetriNet (object) :
3853 raise 3855 raise
3854 def rename_node (self, old, new) : 3856 def rename_node (self, old, new) :
3855 """Change the name of a node. 3857 """Change the name of a node.
3856 - 3858 +
3857 >>> n = PetriNet('N') 3859 >>> n = PetriNet('N')
3858 >>> n.add_place(Place('p')) 3860 >>> n.add_place(Place('p'))
3859 >>> n.add_transition(Transition('t')) 3861 >>> n.add_transition(Transition('t'))
...@@ -3873,7 +3875,7 @@ class PetriNet (object) : ...@@ -3873,7 +3875,7 @@ class PetriNet (object) :
3873 >>> try : n.rename_node('old_t', 'new_t') 3875 >>> try : n.rename_node('old_t', 'new_t')
3874 ... except ConstraintError : print(sys.exc_info()[1]) 3876 ... except ConstraintError : print(sys.exc_info()[1])
3875 node 'old_t' not found 3877 node 'old_t' not found
3876 - 3878 +
3877 @param old: the current name of the node 3879 @param old: the current name of the node
3878 @type old: `str` 3880 @type old: `str`
3879 @param new: the new name for the node 3881 @param new: the new name for the node
...@@ -3903,7 +3905,7 @@ class PetriNet (object) : ...@@ -3903,7 +3905,7 @@ class PetriNet (object) :
3903 del other.pre[old] 3905 del other.pre[old]
3904 def copy_place (self, source, targets) : 3906 def copy_place (self, source, targets) :
3905 """Make copies of the `source` place (use place names). 3907 """Make copies of the `source` place (use place names).
3906 - 3908 +
3907 >>> n = PetriNet('N') 3909 >>> n = PetriNet('N')
3908 >>> n.add_place(Place('p', range(3))) 3910 >>> n.add_place(Place('p', range(3)))
3909 >>> n.add_transition(Transition('t')) 3911 >>> n.add_transition(Transition('t'))
...@@ -3917,7 +3919,7 @@ class PetriNet (object) : ...@@ -3917,7 +3919,7 @@ class PetriNet (object) :
3917 Place('ter', MultiSet([...]), tAll)] 3919 Place('ter', MultiSet([...]), tAll)]
3918 >>> list(sorted(n.pre('t'), key=str)) 3920 >>> list(sorted(n.pre('t'), key=str))
3919 ['bis', 'more', 'p', 'ter'] 3921 ['bis', 'more', 'p', 'ter']
3920 - 3922 +
3921 @param source: the name of the place to copy 3923 @param source: the name of the place to copy
3922 @type source: `str` 3924 @type source: `str`
3923 @param targets: a name or a list of names for the copie(s) 3925 @param targets: a name or a list of names for the copie(s)
...@@ -3932,7 +3934,7 @@ class PetriNet (object) : ...@@ -3932,7 +3934,7 @@ class PetriNet (object) :
3932 self.add_output(target, trans, label.copy()) 3934 self.add_output(target, trans, label.copy())
3933 def copy_transition (self, source, targets) : 3935 def copy_transition (self, source, targets) :
3934 """Make copies of the `source` transition (use transition names). 3936 """Make copies of the `source` transition (use transition names).
3935 - 3937 +
3936 >>> n = PetriNet('N') 3938 >>> n = PetriNet('N')
3937 >>> n.add_transition(Transition('t', Expression('x==1'))) 3939 >>> n.add_transition(Transition('t', Expression('x==1')))
3938 >>> n.add_place(Place('p')) 3940 >>> n.add_place(Place('p'))
...@@ -3946,7 +3948,7 @@ class PetriNet (object) : ...@@ -3946,7 +3948,7 @@ class PetriNet (object) :
3946 Transition('ter', Expression('x==1'))] 3948 Transition('ter', Expression('x==1'))]
3947 >>> list(sorted(n.post('p'))) 3949 >>> list(sorted(n.post('p')))
3948 ['bis', 'more', 't', 'ter'] 3950 ['bis', 'more', 't', 'ter']
3949 - 3951 +
3950 @param source: the name of the transition to copy 3952 @param source: the name of the transition to copy
3951 @type source: `str` 3953 @type source: `str`
3952 @param targets: a name or a list of names for the copie(s) 3954 @param targets: a name or a list of names for the copie(s)
...@@ -3961,11 +3963,11 @@ class PetriNet (object) : ...@@ -3961,11 +3963,11 @@ class PetriNet (object) :
3961 self.add_output(place, target, label.copy()) 3963 self.add_output(place, target, label.copy())
3962 def merge_places (self, target, sources) : 3964 def merge_places (self, target, sources) :
3963 """Create a new place by merging those in `sources`. 3965 """Create a new place by merging those in `sources`.
3964 - 3966 +
3965 Markings are added, place types are 'or'ed and arcs labels are 3967 Markings are added, place types are 'or'ed and arcs labels are
3966 joinded into multi-arcs, the sources places are not removed. 3968 joinded into multi-arcs, the sources places are not removed.
3967 Use places names. 3969 Use places names.
3968 - 3970 +
3969 >>> n = PetriNet('n') 3971 >>> n = PetriNet('n')
3970 >>> n.add_place(Place('p1', [1], tInteger)) 3972 >>> n.add_place(Place('p1', [1], tInteger))
3971 >>> n.add_place(Place('p2', [2.0], tFloat)) 3973 >>> n.add_place(Place('p2', [2.0], tFloat))
...@@ -3984,7 +3986,7 @@ class PetriNet (object) : ...@@ -3984,7 +3986,7 @@ class PetriNet (object) :
3984 True 3986 True
3985 >>> n.node('p').checker() 3987 >>> n.node('p').checker()
3986 (Instance(int) | Instance(float)) 3988 (Instance(int) | Instance(float))
3987 - 3989 +
3988 @param target: the name of the created place 3990 @param target: the name of the created place
3989 @type target: `str` 3991 @type target: `str`
3990 @param sources: the list of places names to be merged (or a 3992 @param sources: the list of places names to be merged (or a
...@@ -4027,11 +4029,11 @@ class PetriNet (object) : ...@@ -4027,11 +4029,11 @@ class PetriNet (object) :
4027 self.add_output(target, trans, MultiArc(labels)) 4029 self.add_output(target, trans, MultiArc(labels))
4028 def merge_transitions (self, target, sources) : 4030 def merge_transitions (self, target, sources) :
4029 """Create a new transition by merging those in `sources`. 4031 """Create a new transition by merging those in `sources`.
4030 - 4032 +
4031 Guards are 'and'ed and arcs labels are joinded into multi- 4033 Guards are 'and'ed and arcs labels are joinded into multi-
4032 arcs, the sources transitions are not removed. Use transitions 4034 arcs, the sources transitions are not removed. Use transitions
4033 names. 4035 names.
4034 - 4036 +
4035 >>> n = PetriNet('n') 4037 >>> n = PetriNet('n')
4036 >>> n.add_place(Place('p1')) 4038 >>> n.add_place(Place('p1'))
4037 >>> n.add_place(Place('p2')) 4039 >>> n.add_place(Place('p2'))
...@@ -4049,7 +4051,7 @@ class PetriNet (object) : ...@@ -4049,7 +4051,7 @@ class PetriNet (object) :
4049 Transition('t', Expression('(x==1) and (y==2)')) 4051 Transition('t', Expression('(x==1) and (y==2)'))
4050 >>> n.node('t').post 4052 >>> n.node('t').post
4051 {'p2': MultiArc((Value(2.0), Value(2.0))), 'p1': Value(1)} 4053 {'p2': MultiArc((Value(2.0), Value(2.0))), 'p1': Value(1)}
4052 - 4054 +
4053 @param target: the name of the created transition 4055 @param target: the name of the created transition
4054 @type target: `str` 4056 @type target: `str`
4055 @param sources: the list of transitions names to be merged (or 4057 @param sources: the list of transitions names to be merged (or
...@@ -4100,10 +4102,10 @@ class StateGraph (object) : ...@@ -4100,10 +4102,10 @@ class StateGraph (object) :
4100 "The graph of reachable markings of a net." 4102 "The graph of reachable markings of a net."
4101 def __init__ (self, net) : 4103 def __init__ (self, net) :
4102 """Initialise with the net. 4104 """Initialise with the net.
4103 - 4105 +
4104 >>> StateGraph(PetriNet('N')).net 4106 >>> StateGraph(PetriNet('N')).net
4105 PetriNet('N') 4107 PetriNet('N')
4106 - 4108 +
4107 @param net: the Petri net whose graph has to be computed 4109 @param net: the Petri net whose graph has to be computed
4108 @type net: `PetriNet` 4110 @type net: `PetriNet`
4109 """ 4111 """
...@@ -4128,10 +4130,10 @@ class StateGraph (object) : ...@@ -4128,10 +4130,10 @@ class StateGraph (object) :
4128 return self._last 4130 return self._last
4129 def goto (self, state) : 4131 def goto (self, state) :
4130 """Change the current state to another (given by its number). 4132 """Change the current state to another (given by its number).
4131 - 4133 +
4132 This also changes the marking of the net consistently. Notice 4134 This also changes the marking of the net consistently. Notice
4133 that the state may not exist yet. 4135 that the state may not exist yet.
4134 - 4136 +
4135 >>> n = PetriNet('N') 4137 >>> n = PetriNet('N')
4136 >>> n.add_place(Place('p', [0])) 4138 >>> n.add_place(Place('p', [0]))
4137 >>> n.add_transition(Transition('t', Expression('x<5'))) 4139 >>> n.add_transition(Transition('t', Expression('x<5')))
...@@ -4145,7 +4147,7 @@ class StateGraph (object) : ...@@ -4145,7 +4147,7 @@ class StateGraph (object) :
4145 >>> g.goto(2) 4147 >>> g.goto(2)
4146 >>> g.net.get_marking() 4148 >>> g.net.get_marking()
4147 Marking({'p': MultiSet([2])}) 4149 Marking({'p': MultiSet([2])})
4148 - 4150 +
4149 @param state: the number of the state to go to 4151 @param state: the number of the state to go to
4150 @type state: non-negative `int` 4152 @type state: non-negative `int`
4151 """ 4153 """
...@@ -4159,7 +4161,7 @@ class StateGraph (object) : ...@@ -4159,7 +4161,7 @@ class StateGraph (object) :
4159 raise ValueError("unknown state") 4161 raise ValueError("unknown state")
4160 def current (self) : 4162 def current (self) :
4161 """Return the number of the current state. 4163 """Return the number of the current state.
4162 - 4164 +
4163 >>> n = PetriNet('N') 4165 >>> n = PetriNet('N')
4164 >>> n.add_place(Place('p', [0])) 4166 >>> n.add_place(Place('p', [0]))
4165 >>> n.add_transition(Transition('t', Expression('x<5'))) 4167 >>> n.add_transition(Transition('t', Expression('x<5')))
...@@ -4168,7 +4170,7 @@ class StateGraph (object) : ...@@ -4168,7 +4170,7 @@ class StateGraph (object) :
4168 >>> g = StateGraph(n) 4170 >>> g = StateGraph(n)
4169 >>> g.current() 4171 >>> g.current()
4170 0 4172 0
4171 - 4173 +
4172 @return: the number of the current state 4174 @return: the number of the current state
4173 @rtype: non-negative `int` 4175 @rtype: non-negative `int`
4174 """ 4176 """
...@@ -4224,12 +4226,12 @@ class StateGraph (object) : ...@@ -4224,12 +4226,12 @@ class StateGraph (object) :
4224 return marking in self._state 4226 return marking in self._state
4225 def successors (self, state=None) : 4227 def successors (self, state=None) :
4226 """Return the successors of the current state. 4228 """Return the successors of the current state.
4227 - 4229 +
4228 The value returned is a dictionnary mapping the numbers of 4230 The value returned is a dictionnary mapping the numbers of
4229 successor states to pairs (trans, mode) representing the name 4231 successor states to pairs (trans, mode) representing the name
4230 of the transition and the binding needed to reach the new 4232 of the transition and the binding needed to reach the new
4231 state. 4233 state.
4232 - 4234 +
4233 >>> n = PetriNet('N') 4235 >>> n = PetriNet('N')
4234 >>> n.add_place(Place('p', [0])) 4236 >>> n.add_place(Place('p', [0]))
4235 >>> n.add_transition(Transition('t', Expression('x<5'))) 4237 >>> n.add_transition(Transition('t', Expression('x<5')))
...@@ -4240,7 +4242,7 @@ class StateGraph (object) : ...@@ -4240,7 +4242,7 @@ class StateGraph (object) :
4240 >>> g.goto(2) 4242 >>> g.goto(2)
4241 >>> g.successors() 4243 >>> g.successors()
4242 {3: (Transition('t', Expression('x<5')), Substitution(x=2))} 4244 {3: (Transition('t', Expression('x<5')), Substitution(x=2))}
4243 - 4245 +
4244 @return: the dictionnary of successors and transitions to them 4246 @return: the dictionnary of successors and transitions to them
4245 @rtype: `dict` mapping non-negative `int` to `tuple` holding a 4247 @rtype: `dict` mapping non-negative `int` to `tuple` holding a
4246 `str` and a `Substitution` 4248 `str` and a `Substitution`
...@@ -4252,12 +4254,12 @@ class StateGraph (object) : ...@@ -4252,12 +4254,12 @@ class StateGraph (object) :
4252 for label in self._succ[state][succ]) 4254 for label in self._succ[state][succ])
4253 def predecessors (self, state=None) : 4255 def predecessors (self, state=None) :
4254 """Return the predecessors states. 4256 """Return the predecessors states.
4255 - 4257 +
4256 The returned value is as in `successors`. Notice that if the 4258 The returned value is as in `successors`. Notice that if the
4257 graph is not complete, this value may be wrong: states 4259 graph is not complete, this value may be wrong: states
4258 computed in the future may lead to the current one thus 4260 computed in the future may lead to the current one thus
4259 becoming one of its predecessors. 4261 becoming one of its predecessors.
4260 - 4262 +
4261 >>> n = PetriNet('N') 4263 >>> n = PetriNet('N')
4262 >>> n.add_place(Place('p', [0])) 4264 >>> n.add_place(Place('p', [0]))
4263 >>> n.add_transition(Transition('t', Expression('x<5'))) 4265 >>> n.add_transition(Transition('t', Expression('x<5')))
...@@ -4268,7 +4270,7 @@ class StateGraph (object) : ...@@ -4268,7 +4270,7 @@ class StateGraph (object) :
4268 >>> g.goto(2) 4270 >>> g.goto(2)
4269 >>> g.predecessors() 4271 >>> g.predecessors()
4270 {1: (Transition('t', Expression('x<5')), Substitution(x=1))} 4272 {1: (Transition('t', Expression('x<5')), Substitution(x=1))}
4271 - 4273 +
4272 @return: the dictionnary of predecessors and transitions to 4274 @return: the dictionnary of predecessors and transitions to
4273 them 4275 them
4274 @rtype: `dict` mapping non-negative `int` to `tuple` holding a 4276 @rtype: `dict` mapping non-negative `int` to `tuple` holding a
...@@ -4306,7 +4308,7 @@ class StateGraph (object) : ...@@ -4306,7 +4308,7 @@ class StateGraph (object) :
4306 return 4308 return
4307 def __len__ (self) : 4309 def __len__ (self) :
4308 """Return the number of states currently reached. 4310 """Return the number of states currently reached.
4309 - 4311 +
4310 >>> n = PetriNet('N') 4312 >>> n = PetriNet('N')
4311 >>> n.add_place(Place('p', [0])) 4313 >>> n.add_place(Place('p', [0]))
4312 >>> n.add_transition(Transition('t', Expression('x<5'))) 4314 >>> n.add_transition(Transition('t', Expression('x<5')))
...@@ -4323,21 +4325,21 @@ class StateGraph (object) : ...@@ -4323,21 +4325,21 @@ class StateGraph (object) :
4323 5 states known 4325 5 states known
4324 6 states known 4326 6 states known
4325 6 states known 4327 6 states known
4326 - 4328 +
4327 @return: the number of states generated at call time 4329 @return: the number of states generated at call time
4328 @rtype: non-negative `int` 4330 @rtype: non-negative `int`
4329 """ 4331 """
4330 return len(self._done) + len(self._todo) 4332 return len(self._done) + len(self._todo)
4331 def __iter__ (self) : 4333 def __iter__ (self) :
4332 """Iterate over the reachable states (numbers). 4334 """Iterate over the reachable states (numbers).
4333 - 4335 +
4334 If needed, the successors of each state are computed just 4336 If needed, the successors of each state are computed just
4335 before it is yield. So, if the graph is not complete, getting 4337 before it is yield. So, if the graph is not complete, getting
4336 the predecessors may be wrong during the iteration. 4338 the predecessors may be wrong during the iteration.
4337 - 4339 +
4338 **Warning:** the net may have an infinite state graph, which 4340 **Warning:** the net may have an infinite state graph, which
4339 is not checked. So you may enter an infinite iteration. 4341 is not checked. So you may enter an infinite iteration.
4340 - 4342 +
4341 >>> n = PetriNet('N') 4343 >>> n = PetriNet('N')
4342 >>> n.add_place(Place('p', [0])) 4344 >>> n.add_place(Place('p', [0]))
4343 >>> n.add_transition(Transition('t', Expression('x<5'))) 4345 >>> n.add_transition(Transition('t', Expression('x<5')))
...@@ -4376,13 +4378,13 @@ class StateGraph (object) : ...@@ -4376,13 +4378,13 @@ class StateGraph (object) :
4376 self.goto(current) 4378 self.goto(current)
4377 def _build (self, stop=None) : 4379 def _build (self, stop=None) :
4378 """Build the complete reachability graph. 4380 """Build the complete reachability graph.
4379 - 4381 +
4380 The graph is build using a breadth first exploration as the 4382 The graph is build using a breadth first exploration as the
4381 newly computed states are put in a queue. 4383 newly computed states are put in a queue.
4382 - 4384 +
4383 **Warning:** this may be infinite! No check of this is 4385 **Warning:** this may be infinite! No check of this is
4384 performed. 4386 performed.
4385 - 4387 +
4386 >>> n = PetriNet('N') 4388 >>> n = PetriNet('N')
4387 >>> n.add_place(Place('p', [0])) 4389 >>> n.add_place(Place('p', [0]))
4388 >>> n.add_transition(Transition('t', Expression('x<5'))) 4390 >>> n.add_transition(Transition('t', Expression('x<5')))
...@@ -4404,7 +4406,7 @@ class StateGraph (object) : ...@@ -4404,7 +4406,7 @@ class StateGraph (object) :
4404 pass 4406 pass
4405 def completed (self) : 4407 def completed (self) :
4406 """Check if all the reachable markings have been explored. 4408 """Check if all the reachable markings have been explored.
4407 - 4409 +
4408 >>> n = PetriNet('N') 4410 >>> n = PetriNet('N')
4409 >>> n.add_place(Place('p', [0])) 4411 >>> n.add_place(Place('p', [0]))
4410 >>> n.add_transition(Transition('t', Expression('x<5'))) 4412 >>> n.add_transition(Transition('t', Expression('x<5')))
...@@ -4419,7 +4421,7 @@ class StateGraph (object) : ...@@ -4419,7 +4421,7 @@ class StateGraph (object) :
4419 3 False 4421 3 False
4420 4 False 4422 4 False
4421 5 True 4423 5 True
4422 - 4424 +
4423 @return: `True` if the graph has been completely computed, 4425 @return: `True` if the graph has been completely computed,
4424 `False` otherwise 4426 `False` otherwise
4425 @rtype: `bool` 4427 @rtype: `bool`
...@@ -4428,7 +4430,7 @@ class StateGraph (object) : ...@@ -4428,7 +4430,7 @@ class StateGraph (object) :
4428 def todo (self) : 4430 def todo (self) :
4429 """Return the number of states whose successors are not yet 4431 """Return the number of states whose successors are not yet
4430 computed. 4432 computed.
4431 - 4433 +
4432 >>> n = PetriNet('N') 4434 >>> n = PetriNet('N')
4433 >>> n.add_place(Place('p', [0])) 4435 >>> n.add_place(Place('p', [0]))
4434 >>> n.add_transition(Transition('t', Expression('x<5'))) 4436 >>> n.add_transition(Transition('t', Expression('x<5')))
...@@ -4443,7 +4445,7 @@ class StateGraph (object) : ...@@ -4443,7 +4445,7 @@ class StateGraph (object) :
4443 3 1 4445 3 1
4444 4 1 4446 4 1
4445 5 0 4447 5 0
4446 - 4448 +
4447 @return: the number of pending states 4449 @return: the number of pending states
4448 @rtype: non-negative `int` 4450 @rtype: non-negative `int`
4449 """ 4451 """
......
1 -"""A plugins system. 1 +"""This package implements SNAKES plugin system. SNAKES plugins
2 +themselves are available as modules within the package.
3 +
4 +Examples below are based on plugin `hello` that is distributed with
5 +SNAKES to be used as an exemple of how to build a plugin. It extends
6 +class `PetriNet` adding a method `hello` that says hello displaying
7 +the name of the net.
8 +
9 +## Loading plugins ##
2 10
3 The first example shows how to load a plugin: we load 11 The first example shows how to load a plugin: we load
4 `snakes.plugins.hello` and plug it into `snakes.nets`, which results 12 `snakes.plugins.hello` and plug it into `snakes.nets`, which results
5 -in a new module that actually `snakes.nets` extended by 13 +in a new module that is actually `snakes.nets` extended by
6 `snakes.plugins.hello`. 14 `snakes.plugins.hello`.
7 15
8 >>> import snakes.plugins as plugins 16 >>> import snakes.plugins as plugins
...@@ -18,11 +26,6 @@ The next example shows how to simulate the effect of `import module`: ...@@ -18,11 +26,6 @@ The next example shows how to simulate the effect of `import module`:
18 we give to `load` a thrid argument that is the name of the created 26 we give to `load` a thrid argument that is the name of the created
19 module, from which it becomes possible to import names or `*`. 27 module, from which it becomes possible to import names or `*`.
20 28
21 -**Warning:** this feature will not work `load` is not called from the
22 -module where we then do the `from ... import ...`. This is exactly the
23 -same when, from a module `foo` that you load a module `bar`: if `bar`
24 -loads other modules they will not be imported in `foo`.
25 -
26 >>> plugins.load('hello', 'snakes.nets', 'another_version') 29 >>> plugins.load('hello', 'snakes.nets', 'another_version')
27 <module ...> 30 <module ...>
28 >>> from another_version import PetriNet 31 >>> from another_version import PetriNet
...@@ -33,12 +36,23 @@ Hello from another net ...@@ -33,12 +36,23 @@ Hello from another net
33 >>> n.hello() 36 >>> n.hello()
34 Hi, this is yet another net! 37 Hi, this is yet another net!
35 38
36 -How to define a plugin is explained in the example `hello`. 39 +The last example shows how to load several plugins at once, instead of
40 +giving one plugin name, we just need to give a list of plugin names.
41 +
42 +>>> plugins.load(['hello', 'pos'], 'snakes.nets', 'mynets')
43 +<module ...>
44 +>>> from mynets import PetriNet
45 +>>> n = PetriNet('a net')
46 +>>> n.hello() # works thanks to plugin `hello`
47 +Hello from a net
48 +>>> n.bbox() # works thanks to plugin `pos`
49 +((0, 0), (0, 0))
37 """ 50 """
38 51
39 import imp, sys, inspect 52 import imp, sys, inspect
40 from functools import wraps 53 from functools import wraps
41 54
55 +# apidoc skip
42 def update (module, objects) : 56 def update (module, objects) :
43 """Update a module content 57 """Update a module content
44 """ 58 """
...@@ -54,48 +68,17 @@ def update (module, objects) : ...@@ -54,48 +68,17 @@ def update (module, objects) :
54 else : 68 else :
55 raise ValueError("cannot plug '%r'" % obj) 69 raise ValueError("cannot plug '%r'" % obj)
56 70
57 -def build (name, module, *objects) :
58 - """Builds an extended module.
59 -
60 - The parameter `module` is exactly that taken by the function
61 - `extend` of a plugin. This list argument `objects` holds all the
62 - objects, constructed in `extend`, that are extensions of objects
63 - from `module`. The resulting value should be returned by `extend`.
64 -
65 - @param name: the name of the constructed module
66 - @type name: `str`
67 - @param module: the extended module
68 - @type module: `module`
69 - @param objects: the sub-objects
70 - @type objects: each is a class object
71 - @return: the new module
72 - @rtype: `module`
73 - """
74 - result = imp.new_module(name)
75 - result.__dict__.update(module.__dict__)
76 - update(result, objects)
77 - result.__plugins__ = (module.__dict__.get("__plugins__",
78 - (module.__name__,))
79 - + (name,))
80 - for obj in objects :
81 - if inspect.isclass(obj) :
82 - obj.__plugins__ = result.__plugins__
83 - return result
84 -
85 def load (plugins, base, name=None) : 71 def load (plugins, base, name=None) :
86 - """Load plugins. 72 + """Load plugins, `plugins` can be a single plugin name, a module
87 - 73 + or a list of such values. If `name` is not `None`, the extended
88 - `plugins` can be a single plugin name or module or a list of such 74 + module is loaded as `name` in `sys.modules` as well as in the
89 - values. If `name` is not `None`, the extended module is loaded ad 75 + global environment from which `load` was called.
90 - `name` in `sys.modules` as well as in the global environment from 76 +
91 - which `load` was called.
92 -
93 @param plugins: the module that implements the plugin, or its 77 @param plugins: the module that implements the plugin, or its
94 - name, or a collection of such values 78 + name, or a collection (eg, list, tuple) of such values
95 - @type plugins: `str` or `module`, or a `list`/`tuple`/... of such 79 + @type plugins: `object`
96 - values
97 @param base: the module being extended or its name 80 @param base: the module being extended or its name
98 - @type base: `str` or `module` 81 + @type base: `object`
99 @param name: the name of the created module 82 @param name: the name of the created module
100 @type name: `str` 83 @type name: `str`
101 @return: the extended module 84 @return: the extended module
...@@ -125,17 +108,45 @@ def load (plugins, base, name=None) : ...@@ -125,17 +108,45 @@ def load (plugins, base, name=None) :
125 inspect.stack()[1][0].f_globals[name] = result 108 inspect.stack()[1][0].f_globals[name] = result
126 return result 109 return result
127 110
111 +"""## Creating plugins ###
112 +
113 +We show now how to develop a plugin that allows instances of
114 +`PetriNet` to say hello: a new method `PetriNet.hello` is added and
115 +the constructor `PetriNet.__init__` is added a keyword argument
116 +`hello` for the message to print when calling method `hello`.
117 +
118 +Defining a plugins required to write a module with a function called
119 +`extend` that takes as its single argument the module to be extended.
120 +Inside this function, extensions of the classes in the module are
121 +defined as normal sub-classes. Function `extend` returns the extended
122 +classes. A decorator called `plugin` must be used, it also allows to
123 +resolve plugin dependencies and conflicts.
124 +"""
125 +
126 +# apidoc include "hello.py" lang="python"
127 +
128 +"""Note that, when extending an existing method like `__init__` above,
129 +we have to take care that you may be working on an already extended
130 +class, consequently, we cannot know how its arguments have been
131 +changed already. So, we must always use those from the unextended
132 +method plus `**args`. Then, we remove from the latter what your plugin
133 +needs and pass the remaining to the method of the base class if we
134 +need to call it (which is usually the case). """
135 +
128 def plugin (base, depends=[], conflicts=[]) : 136 def plugin (base, depends=[], conflicts=[]) :
129 """Decorator for extension functions 137 """Decorator for extension functions
130 - 138 +
131 - @param base: name of base module (usually 'snakes.nets') 139 + @param base: name of base module (usually 'snakes.nets') that the
132 - @type base: str 140 + plugin extends
133 - @param depends: list of plugins on which this one depends 141 + @type base: `str`
134 - @type depends: list of str 142 + @param depends: list of plugin names (as `str`) this one depends
135 - @param conflicts: list of plugins with which this one conflicts 143 + on, prefix `snakes.plugins.` may be omitted
136 - @type conflicts: list of str 144 + @type depends: `list`
145 + @param conflicts: list of plugin names with which this one
146 + conflicts
147 + @type conflicts: `list`
137 @return: the appropriate decorator 148 @return: the appropriate decorator
138 - @rtype: function 149 + @rtype: `decorator`
139 """ 150 """
140 def wrapper (fun) : 151 def wrapper (fun) :
141 @wraps(fun) 152 @wraps(fun)
...@@ -164,9 +175,39 @@ def plugin (base, depends=[], conflicts=[]) : ...@@ -164,9 +175,39 @@ def plugin (base, depends=[], conflicts=[]) :
164 return extend 175 return extend
165 return wrapper 176 return wrapper
166 177
178 +# apidoc skip
167 def new_instance (cls, obj) : 179 def new_instance (cls, obj) :
168 """Create a copy of `obj` which is an instance of `cls` 180 """Create a copy of `obj` which is an instance of `cls`
169 """ 181 """
170 result = object.__new__(cls) 182 result = object.__new__(cls)
171 result.__dict__.update(obj.__dict__) 183 result.__dict__.update(obj.__dict__)
172 return result 184 return result
185 +
186 +# apidoc skip
187 +def build (name, module, *objects) :
188 + """Builds an extended module.
189 +
190 + The parameter `module` is exactly that taken by the function
191 + `extend` of a plugin. This list argument `objects` holds all the
192 + objects, constructed in `extend`, that are extensions of objects
193 + from `module`. The resulting value should be returned by `extend`.
194 +
195 + @param name: the name of the constructed module
196 + @type name: `str`
197 + @param module: the extended module
198 + @type module: `module`
199 + @param objects: the sub-objects
200 + @type objects: each is a class object
201 + @return: the new module
202 + @rtype: `module`
203 + """
204 + result = imp.new_module(name)
205 + result.__dict__.update(module.__dict__)
206 + update(result, objects)
207 + result.__plugins__ = (module.__dict__.get("__plugins__",
208 + (module.__name__,))
209 + + (name,))
210 + for obj in objects :
211 + if inspect.isclass(obj) :
212 + obj.__plugins__ = result.__plugins__
213 + return result
......
1 +"""
2 +@todo: revise (actually make) documentation
3 +"""
4 +
1 import snakes.plugins 5 import snakes.plugins
2 from snakes.plugins import new_instance 6 from snakes.plugins import new_instance
3 from snakes.pnml import Tree 7 from snakes.pnml import Tree
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
26 >>> n.layout() 26 >>> n.layout()
27 >>> any(node.pos == (-100, -100) for node in sorted(n.node(), key=str)) 27 >>> any(node.pos == (-100, -100) for node in sorted(n.node(), key=str))
28 False 28 False
29 +
30 +@todo: revise documentation
29 """ 31 """
30 32
31 import os, os.path, subprocess, collections 33 import os, os.path, subprocess, collections
......
1 -"""An example plugin that allows instances class `PetriNet` to say hello. 1 +"""An example plugin that allows instances of `PetriNet` to
2 - 2 +say hello. The source code can be used as a starting
3 -A new method `hello` is added. The constructor is added a keyword 3 +example."""
4 -argument `hello` that must be the `str` to print when calling `hello`,
5 -with one `%s` that will be replaced by the name of the net when
6 -`hello` is called.
7 -
8 -Defining a plugins need writing a module with a single function called
9 -`extend` that takes a single argument that is the module to be
10 -extended.
11 -
12 -Inside the function, extensions of the classes in the module are
13 -defined as normal sub-classes.
14 -
15 -The function `extend` should return the extended module created by
16 -`snakes.plugins.build` that takes as arguments: the name of the
17 -extended module, the module taken as argument and the sub-classes
18 -defined (expected as a list argument `*args` in no special order).
19 -
20 -If the plugin depends on other plugins, for instance `foo` and `bar`,
21 -the function `extend` should be decorated by `@depends('foo', 'bar')`.
22 -
23 -Read the source code of this module to have an example
24 -"""
25 4
26 import snakes.plugins 5 import snakes.plugins
27 6
28 @snakes.plugins.plugin("snakes.nets") 7 @snakes.plugins.plugin("snakes.nets")
29 def extend (module) : 8 def extend (module) :
30 - """Extends `module` 9 + "Extends `module`"
31 - """
32 class PetriNet (module.PetriNet) : 10 class PetriNet (module.PetriNet) :
33 - """Extension of the class `PetriNet` in `module` 11 + "Extension of the class `PetriNet` in `module`"
34 - """
35 def __init__ (self, name, **args) : 12 def __init__ (self, name, **args) :
36 - """When extending an existing method, take care that you may 13 + """Add new keyword argument `hello`
37 - be working on an already extended class, so you so not 14 +
38 - know how its arguments have been changed. So, always use
39 - those from the unextended class plus `**args`, remove from
40 - it what your plugin needs and pass it to the method of the
41 - extended class if you need to call it.
42 -
43 >>> PetriNet('N').hello() 15 >>> PetriNet('N').hello()
44 Hello from N 16 Hello from N
45 >>> PetriNet('N', hello='Hi! This is %s...').hello() 17 >>> PetriNet('N', hello='Hi! This is %s...').hello()
46 Hi! This is N... 18 Hi! This is N...
47 - 19 +
48 @param args: plugin options 20 @param args: plugin options
49 - @keyword hello: the message to print, with `%s` where the 21 + @keyword hello: the message to print, with
50 - net name should appear. 22 + `%s` where the net name should appear.
51 @type hello: `str` 23 @type hello: `str`
52 """ 24 """
53 self._hello = args.pop("hello", "Hello from %s") 25 self._hello = args.pop("hello", "Hello from %s")
54 module.PetriNet.__init__(self, name, **args) 26 module.PetriNet.__init__(self, name, **args)
55 def hello (self) : 27 def hello (self) :
56 - """A new method `hello` 28 + "Ask the net to say hello"
57 -
58 - >>> n = PetriNet('N')
59 - >>> n.hello()
60 - Hello from N
61 - """
62 print(self._hello % self.name) 29 print(self._hello % self.name)
63 return PetriNet 30 return PetriNet
......
1 """A plugin to add labels to nodes and nets. 1 """A plugin to add labels to nodes and nets.
2 +
3 +@todo: revise (actually make) documentation
2 """ 4 """
3 5
4 from snakes.plugins import plugin, new_instance 6 from snakes.plugins import plugin, new_instance
......
...@@ -65,6 +65,8 @@ Buffer('buffer') ...@@ -65,6 +65,8 @@ Buffer('buffer')
65 (1, 2) 65 (1, 2)
66 >>> n._declare 66 >>> n._declare
67 ['global x; x=1'] 67 ['global x; x=1']
68 +
69 +@todo: revise documentation
68 """ 70 """
69 71
70 import snakes.plugins 72 import snakes.plugins
......
...@@ -42,6 +42,8 @@ Position(1, 3) ...@@ -42,6 +42,8 @@ Position(1, 3)
42 >>> n.transpose() 42 >>> n.transpose()
43 >>> n.node('t01').pos 43 >>> n.node('t01').pos
44 Position(-3, 1) 44 Position(-3, 1)
45 +
46 +@todo: revise documentation
45 """ 47 """
46 48
47 from snakes import SnakesError 49 from snakes import SnakesError
......
1 +"""
2 +@todo: revise (actually make) documentation
3 +"""
4 +
1 from snakes.plugins import plugin 5 from snakes.plugins import plugin
2 from snakes.pnml import Tree, loads, dumps 6 from snakes.pnml import Tree, loads, dumps
3 import imp, sys, socket, traceback, operator 7 import imp, sys, socket, traceback, operator
......
...@@ -12,6 +12,8 @@ Several status are defined by default: `entry`, `internal`, `exit`, ...@@ -12,6 +12,8 @@ Several status are defined by default: `entry`, `internal`, `exit`,
12 >>> n.add_place(Place('p1'), status=status.entry) 12 >>> n.add_place(Place('p1'), status=status.entry)
13 >>> n.place('p1') 13 >>> n.place('p1')
14 Place('p1', MultiSet([]), tAll, status=Status('entry')) 14 Place('p1', MultiSet([]), tAll, status=Status('entry'))
15 +
16 +@todo: revise documentation
15 """ 17 """
16 18
17 import operator, weakref 19 import operator, weakref
......
...@@ -6,8 +6,6 @@ The plugin proposes a generalisation of the M-nets synchronisation in ...@@ -6,8 +6,6 @@ The plugin proposes a generalisation of the M-nets synchronisation in
6 that it does not impose a fixed correspondence between action names 6 that it does not impose a fixed correspondence between action names
7 and action arities. 7 and action arities.
8 8
9 -
10 -
11 * class `Action` corresponds to a synchronisable action, it has a 9 * class `Action` corresponds to a synchronisable action, it has a
12 name, a send/receive flag and a list of parameters. Actions have 10 name, a send/receive flag and a list of parameters. Actions have
13 no predetermined arities, only conjugated actions with the same 11 no predetermined arities, only conjugated actions with the same
...@@ -66,6 +64,8 @@ t2 z>0 ...@@ -66,6 +64,8 @@ t2 z>0
66 >>> [t.name for t in sorted(n.transition(), key=str)] 64 >>> [t.name for t in sorted(n.transition(), key=str)]
67 ["((t1{...}+t2{...})[a(...)]{...}+t2{...})[a(...)]", 65 ["((t1{...}+t2{...})[a(...)]{...}+t2{...})[a(...)]",
68 "((t1{...}+t2{...})[a(...)]{...}+t2{...})[a(...)]"] 66 "((t1{...}+t2{...})[a(...)]{...}+t2{...})[a(...)]"]
67 +
68 +@todo: revise documentation
69 """ 69 """
70 70
71 from snakes import ConstraintError 71 from snakes import ConstraintError
...@@ -91,6 +91,7 @@ class Action (object) : ...@@ -91,6 +91,7 @@ class Action (object) :
91 self.send = send 91 self.send = send
92 self.params = list(params) 92 self.params = list(params)
93 __pnmltag__ = "action" 93 __pnmltag__ = "action"
94 + # apidoc skip
94 def __pnmldump__ (self) : 95 def __pnmldump__ (self) :
95 """ 96 """
96 >>> Action('a', True, [Value(1), Variable('x')]).__pnmldump__() 97 >>> Action('a', True, [Value(1), Variable('x')]).__pnmldump__()
...@@ -114,6 +115,7 @@ class Action (object) : ...@@ -114,6 +115,7 @@ class Action (object) :
114 for param in self.params : 115 for param in self.params :
115 result.add_child(Tree.from_obj(param)) 116 result.add_child(Tree.from_obj(param))
116 return result 117 return result
118 + # apidoc skip
117 @classmethod 119 @classmethod
118 def __pnmlload__ (cls, tree) : 120 def __pnmlload__ (cls, tree) :
119 """ 121 """
...@@ -136,6 +138,7 @@ class Action (object) : ...@@ -136,6 +138,7 @@ class Action (object) :
136 return "%s!(%s)" % (self.name, ",".join([str(p) for p in self])) 138 return "%s!(%s)" % (self.name, ",".join([str(p) for p in self]))
137 else : 139 else :
138 return "%s?(%s)" % (self.name, ",".join([str(p) for p in self])) 140 return "%s?(%s)" % (self.name, ",".join([str(p) for p in self]))
141 + # apidoc stop
139 def __repr__ (self) : 142 def __repr__ (self) :
140 """ 143 """
141 >>> a = Action('a', True, [Value(1), Variable('x')]) 144 >>> a = Action('a', True, [Value(1), Variable('x')])
...@@ -150,17 +153,17 @@ class Action (object) : ...@@ -150,17 +153,17 @@ class Action (object) :
150 ", ".join([repr(p) for p in self])) 153 ", ".join([repr(p) for p in self]))
151 def __len__ (self) : 154 def __len__ (self) :
152 """Return the number of parameters, aka the arity of the action. 155 """Return the number of parameters, aka the arity of the action.
153 - 156 +
154 >>> len(Action('a', True, [Value(1), Variable('x')])) 157 >>> len(Action('a', True, [Value(1), Variable('x')]))
155 2 158 2
156 - 159 +
157 @return: the arity of the action 160 @return: the arity of the action
158 @rtype: non negative `int` 161 @rtype: non negative `int`
159 """ 162 """
160 return len(self.params) 163 return len(self.params)
161 def __iter__ (self) : 164 def __iter__ (self) :
162 """Iterate on the parameters 165 """Iterate on the parameters
163 - 166 +
164 >>> list(Action('a', True, [Value(1), Variable('x')])) 167 >>> list(Action('a', True, [Value(1), Variable('x')]))
165 [Value(1), Variable('x')] 168 [Value(1), Variable('x')]
166 """ 169 """
...@@ -169,7 +172,7 @@ class Action (object) : ...@@ -169,7 +172,7 @@ class Action (object) :
169 def __eq__ (self, other) : 172 def __eq__ (self, other) :
170 """Two actions are equal if they have the same name, same send 173 """Two actions are equal if they have the same name, same send
171 flags and same parameters. 174 flags and same parameters.
172 - 175 +
173 >>> Action('a', True, [Value(1), Variable('x')]) == Action('a', True, [Value(1), Variable('x')]) 176 >>> Action('a', True, [Value(1), Variable('x')]) == Action('a', True, [Value(1), Variable('x')])
174 True 177 True
175 >>> Action('a', True, [Value(1), Variable('x')]) == Action('b', True, [Value(1), Variable('x')]) 178 >>> Action('a', True, [Value(1), Variable('x')]) == Action('b', True, [Value(1), Variable('x')])
...@@ -180,7 +183,7 @@ class Action (object) : ...@@ -180,7 +183,7 @@ class Action (object) :
180 False 183 False
181 >>> Action('a', True, [Value(1), Variable('x')]) == Action('a', True, [Value(1)]) 184 >>> Action('a', True, [Value(1), Variable('x')]) == Action('a', True, [Value(1)])
182 False 185 False
183 - 186 +
184 @param other: the action to compare 187 @param other: the action to compare
185 @type other: `Action` 188 @type other: `Action`
186 @return: `True` if the two actions are equal, `False` 189 @return: `True` if the two actions are equal, `False`
...@@ -201,14 +204,14 @@ class Action (object) : ...@@ -201,14 +204,14 @@ class Action (object) :
201 return not (self == other) 204 return not (self == other)
202 def copy (self, subst=None) : 205 def copy (self, subst=None) :
203 """Copy the action, optionally substituting its parameters. 206 """Copy the action, optionally substituting its parameters.
204 - 207 +
205 >>> a = Action('a', True, [Variable('x'), Value(2)]) 208 >>> a = Action('a', True, [Variable('x'), Value(2)])
206 >>> a.copy() 209 >>> a.copy()
207 Action('a', True, [Variable('x'), Value(2)]) 210 Action('a', True, [Variable('x'), Value(2)])
208 >>> a = Action('a', True, [Variable('x'), Value(2)]) 211 >>> a = Action('a', True, [Variable('x'), Value(2)])
209 >>> a.copy(Substitution(x=Value(3))) 212 >>> a.copy(Substitution(x=Value(3)))
210 Action('a', True, [Value(3), Value(2)]) 213 Action('a', True, [Value(3), Value(2)])
211 - 214 +
212 @param subst: if not `None`, a substitution to apply to the 215 @param subst: if not `None`, a substitution to apply to the
213 parameters of the copy 216 parameters of the copy
214 @type subst: `None` or `Substitution` mapping variables names 217 @type subst: `None` or `Substitution` mapping variables names
...@@ -224,12 +227,12 @@ class Action (object) : ...@@ -224,12 +227,12 @@ class Action (object) :
224 return result 227 return result
225 def substitute (self, subst) : 228 def substitute (self, subst) :
226 """Substitute the parameters according to `subst` 229 """Substitute the parameters according to `subst`
227 - 230 +
228 >>> a = Action('a', True, [Variable('x'), Value(2)]) 231 >>> a = Action('a', True, [Variable('x'), Value(2)])
229 >>> a.substitute(Substitution(x=Value(3))) 232 >>> a.substitute(Substitution(x=Value(3)))
230 >>> a 233 >>> a
231 Action('a', True, [Value(3), Value(2)]) 234 Action('a', True, [Value(3), Value(2)])
232 - 235 +
233 @param subst: a substitution to apply to the parameters 236 @param subst: a substitution to apply to the parameters
234 @type subst: `Substitution` mapping variables names to `Value` 237 @type subst: `Substitution` mapping variables names to `Value`
235 or `Variable` 238 or `Variable`
...@@ -241,7 +244,7 @@ class Action (object) : ...@@ -241,7 +244,7 @@ class Action (object) :
241 """ 244 """
242 >>> Action('a', True, [Value(3), Variable('x'), Variable('y'), Variable('x')]).vars() == set(['x', 'y']) 245 >>> Action('a', True, [Value(3), Variable('x'), Variable('y'), Variable('x')]).vars() == set(['x', 'y'])
243 True 246 True
244 - 247 +
245 @return: the set of variable names appearing in the parameters 248 @return: the set of variable names appearing in the parameters
246 of the action 249 of the action
247 @rtype: `set` of `str` 250 @rtype: `set` of `str`
...@@ -249,13 +252,13 @@ class Action (object) : ...@@ -249,13 +252,13 @@ class Action (object) :
249 return set(p.name for p in self.params if isinstance(p, Variable)) 252 return set(p.name for p in self.params if isinstance(p, Variable))
250 def __and__ (self, other) : 253 def __and__ (self, other) :
251 """Compute an unification of two conjugated actions. 254 """Compute an unification of two conjugated actions.
252 - 255 +
253 An unification is a `Substitution` that maps variable names to 256 An unification is a `Substitution` that maps variable names to
254 `Variable` or `Values`. If both actions are substituted by 257 `Variable` or `Values`. If both actions are substituted by
255 this unification, their parameters lists become equal. If no 258 this unification, their parameters lists become equal. If no
256 unification can be found, `ConstraintError` is raised (or, 259 unification can be found, `ConstraintError` is raised (or,
257 rarely, `DomainError` depending on the cause of the failure). 260 rarely, `DomainError` depending on the cause of the failure).
258 - 261 +
259 >>> s = Action('a', True, [Value(3), Variable('x'), Variable('y'), Variable('x')]) 262 >>> s = Action('a', True, [Value(3), Variable('x'), Variable('y'), Variable('x')])
260 >>> r = Action('a', False, [Value(3), Value(2), Variable('t'), Variable('z')]) 263 >>> r = Action('a', False, [Value(3), Value(2), Variable('t'), Variable('z')])
261 >>> u = s & r 264 >>> u = s & r
...@@ -284,7 +287,7 @@ class Action (object) : ...@@ -284,7 +287,7 @@ class Action (object) :
284 >>> try : s & r 287 >>> try : s & r
285 ... except ConstraintError : print(sys.exc_info()[1]) 288 ... except ConstraintError : print(sys.exc_info()[1])
286 actions not conjugated 289 actions not conjugated
287 - 290 +
288 @param other: the other action to unify with 291 @param other: the other action to unify with
289 @type other: `Action` 292 @type other: `Action`
290 @return: a substitution that unify both actions 293 @return: a substitution that unify both actions
...@@ -323,7 +326,7 @@ class MultiAction (object) : ...@@ -323,7 +326,7 @@ class MultiAction (object) :
323 ... Action('a', False, [Value(2)])]) 326 ... Action('a', False, [Value(2)])])
324 ... except ConstraintError : print(sys.exc_info()[1]) 327 ... except ConstraintError : print(sys.exc_info()[1])
325 conjugated actions in the same multiaction 328 conjugated actions in the same multiaction
326 - 329 +
327 @param actions: a collection of actions with no conjugated 330 @param actions: a collection of actions with no conjugated
328 actions in it 331 actions in it
329 @type actions: `list` of `Action` 332 @type actions: `list` of `Action`
...@@ -392,10 +395,10 @@ class MultiAction (object) : ...@@ -392,10 +395,10 @@ class MultiAction (object) :
392 def send (self, name) : 395 def send (self, name) :
393 """Returns the send flag of the action `name` in this 396 """Returns the send flag of the action `name` in this
394 multiaction. 397 multiaction.
395 - 398 +
396 This value is unique as conjugated actions are forbidden in 399 This value is unique as conjugated actions are forbidden in
397 the same multiaction. 400 the same multiaction.
398 - 401 +
399 >>> m = MultiAction([Action('a', True, [Variable('x')]), 402 >>> m = MultiAction([Action('a', True, [Variable('x')]),
400 ... Action('b', False, [Variable('y'), Value(2)])]) 403 ... Action('b', False, [Variable('y'), Value(2)])])
401 >>> m.send('a'), m.send('b') 404 >>> m.send('a'), m.send('b')
...@@ -404,10 +407,10 @@ class MultiAction (object) : ...@@ -404,10 +407,10 @@ class MultiAction (object) :
404 return self._sndrcv[name] 407 return self._sndrcv[name]
405 def add (self, action) : 408 def add (self, action) :
406 """Add an action to the multiaction. 409 """Add an action to the multiaction.
407 - 410 +
408 This may raise `ConstraintError` if the added action is 411 This may raise `ConstraintError` if the added action is
409 conjugated to one that already belongs to the multiaction. 412 conjugated to one that already belongs to the multiaction.
410 - 413 +
411 @param action: the action to add 414 @param action: the action to add
412 @type action: `Action` 415 @type action: `Action`
413 """ 416 """
...@@ -418,10 +421,10 @@ class MultiAction (object) : ...@@ -418,10 +421,10 @@ class MultiAction (object) :
418 self._actions.append(action) 421 self._actions.append(action)
419 def remove (self, action) : 422 def remove (self, action) :
420 """Remove an action from the multiaction. 423 """Remove an action from the multiaction.
421 - 424 +
422 This may raise `ValueError` if the removed action does belongs 425 This may raise `ValueError` if the removed action does belongs
423 to the multiaction. 426 to the multiaction.
424 - 427 +
425 @param action: the action to remove 428 @param action: the action to remove
426 @type action: `Action` 429 @type action: `Action`
427 """ 430 """
...@@ -432,7 +435,7 @@ class MultiAction (object) : ...@@ -432,7 +435,7 @@ class MultiAction (object) :
432 del self._sndrcv[action.name] 435 del self._sndrcv[action.name]
433 def __iter__ (self) : 436 def __iter__ (self) :
434 """Iterate over the actions in the multiaction. 437 """Iterate over the actions in the multiaction.
435 - 438 +
436 >>> list(MultiAction([Action('a', True, [Variable('x')]), 439 >>> list(MultiAction([Action('a', True, [Variable('x')]),
437 ... Action('b', False, [Variable('y'), Value(2)])])) 440 ... Action('b', False, [Variable('y'), Value(2)])]))
438 [Action('a', True, [Variable('x')]), 441 [Action('a', True, [Variable('x')]),
...@@ -442,18 +445,18 @@ class MultiAction (object) : ...@@ -442,18 +445,18 @@ class MultiAction (object) :
442 yield action 445 yield action
443 def __len__ (self) : 446 def __len__ (self) :
444 """Return the number of actions in a multiaction. 447 """Return the number of actions in a multiaction.
445 - 448 +
446 >>> len(MultiAction([Action('a', True, [Variable('x')]), 449 >>> len(MultiAction([Action('a', True, [Variable('x')]),
447 ... Action('b', False, [Variable('y'), Value(2)])])) 450 ... Action('b', False, [Variable('y'), Value(2)])]))
448 2 451 2
449 - 452 +
450 @return: the number of contained actions 453 @return: the number of contained actions
451 @rtype: non negative `int` 454 @rtype: non negative `int`
452 """ 455 """
453 return len(self._actions) 456 return len(self._actions)
454 def substitute (self, subst) : 457 def substitute (self, subst) :
455 """Substitute bu `subt` all the actions in the multiaction. 458 """Substitute bu `subt` all the actions in the multiaction.
456 - 459 +
457 >>> m = MultiAction([Action('a', True, [Variable('x')]), 460 >>> m = MultiAction([Action('a', True, [Variable('x')]),
458 ... Action('b', False, [Variable('y'), Variable('x')])]) 461 ... Action('b', False, [Variable('y'), Variable('x')])])
459 >>> m.substitute(Substitution(x=Value(4))) 462 >>> m.substitute(Substitution(x=Value(4)))
...@@ -466,7 +469,7 @@ class MultiAction (object) : ...@@ -466,7 +469,7 @@ class MultiAction (object) :
466 def copy (self, subst=None) : 469 def copy (self, subst=None) :
467 """Copy the multiaction (and the actions is contains) optionally 470 """Copy the multiaction (and the actions is contains) optionally
468 substituting it. 471 substituting it.
469 - 472 +
470 @param subst: if not `None`, the substitution to apply to the 473 @param subst: if not `None`, the substitution to apply to the
471 copy. 474 copy.
472 @type subst: `None` or `Substitution` 475 @type subst: `None` or `Substitution`
...@@ -479,10 +482,10 @@ class MultiAction (object) : ...@@ -479,10 +482,10 @@ class MultiAction (object) :
479 return result 482 return result
480 def __contains__ (self, action) : 483 def __contains__ (self, action) :
481 """Search an action in the multiaction. 484 """Search an action in the multiaction.
482 - 485 +
483 The searched action may be a complete `Action`, just an action 486 The searched action may be a complete `Action`, just an action
484 name, or a pair `(name, send_flag)`. 487 name, or a pair `(name, send_flag)`.
485 - 488 +
486 >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]), 489 >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
487 ... Action('a', True, [Value(3), Variable('y')]), 490 ... Action('a', True, [Value(3), Variable('y')]),
488 ... Action('b', False, [Variable('x'), Variable('y')])]) 491 ... Action('b', False, [Variable('x'), Variable('y')])])
...@@ -498,7 +501,7 @@ class MultiAction (object) : ...@@ -498,7 +501,7 @@ class MultiAction (object) :
498 False 501 False
499 >>> Action('c', True, [Variable('x'), Value(2)]) in m 502 >>> Action('c', True, [Variable('x'), Value(2)]) in m
500 False 503 False
501 - 504 +
502 @param action: an complete action, or its name or its name and 505 @param action: an complete action, or its name or its name and
503 send flag 506 send flag
504 @type action: `Action` or `str` or `tuple(str, bool)` 507 @type action: `Action` or `str` or `tuple(str, bool)`
...@@ -517,7 +520,7 @@ class MultiAction (object) : ...@@ -517,7 +520,7 @@ class MultiAction (object) :
517 raise ValueError("invalid action specification") 520 raise ValueError("invalid action specification")
518 def __add__ (self, other) : 521 def __add__ (self, other) :
519 """Create a multiaction by adding the actions of two others. 522 """Create a multiaction by adding the actions of two others.
520 - 523 +
521 >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]), 524 >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
522 ... Action('a', True, [Value(3), Variable('y')]), 525 ... Action('a', True, [Value(3), Variable('y')]),
523 ... Action('b', False, [Variable('x'), Variable('y')])]) 526 ... Action('b', False, [Variable('x'), Variable('y')])])
...@@ -533,7 +536,7 @@ class MultiAction (object) : ...@@ -533,7 +536,7 @@ class MultiAction (object) :
533 Action('a', True, [Value(3), Variable('y')]), 536 Action('a', True, [Value(3), Variable('y')]),
534 Action('b', False, [Variable('x'), Variable('y')]), 537 Action('b', False, [Variable('x'), Variable('y')]),
535 Action('c', True, [])]) 538 Action('c', True, [])])
536 - 539 +
537 @param other: the other multiaction to combine or a single 540 @param other: the other multiaction to combine or a single
538 action 541 action
539 @type other: `MultiAction` or `Action` 542 @type other: `MultiAction` or `Action`
...@@ -549,7 +552,7 @@ class MultiAction (object) : ...@@ -549,7 +552,7 @@ class MultiAction (object) :
549 def __sub__ (self, other) : 552 def __sub__ (self, other) :
550 """Create a multiaction by substracting the actions of two 553 """Create a multiaction by substracting the actions of two
551 others. 554 others.
552 - 555 +
553 >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]), 556 >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
554 ... Action('a', True, [Value(3), Variable('y')]), 557 ... Action('a', True, [Value(3), Variable('y')]),
555 ... Action('b', False, [Variable('x'), Variable('y')])]) 558 ... Action('b', False, [Variable('x'), Variable('y')])])
...@@ -558,7 +561,7 @@ class MultiAction (object) : ...@@ -558,7 +561,7 @@ class MultiAction (object) :
558 >>> m - Action('b', False, [Variable('x'), Variable('y')]) 561 >>> m - Action('b', False, [Variable('x'), Variable('y')])
559 MultiAction([Action('a', True, [Variable('x'), Value(2)]), 562 MultiAction([Action('a', True, [Variable('x'), Value(2)]),
560 Action('a', True, [Value(3), Variable('y')])]) 563 Action('a', True, [Value(3), Variable('y')])])
561 - 564 +
562 @param other: the other multiaction to combine or a single 565 @param other: the other multiaction to combine or a single
563 action 566 action
564 @type other: `MultiAction` or `Action` 567 @type other: `MultiAction` or `Action`
...@@ -574,12 +577,12 @@ class MultiAction (object) : ...@@ -574,12 +577,12 @@ class MultiAction (object) :
574 def vars (self) : 577 def vars (self) :
575 """Return the set of variable names used in all the actions of 578 """Return the set of variable names used in all the actions of
576 the multiaction. 579 the multiaction.
577 - 580 +
578 >>> MultiAction([Action('a', True, [Variable('x'), Value(2)]), 581 >>> MultiAction([Action('a', True, [Variable('x'), Value(2)]),
579 ... Action('a', True, [Value(3), Variable('y')]), 582 ... Action('a', True, [Value(3), Variable('y')]),
580 ... Action('b', False, [Variable('x'), Variable('z')])]).vars() == set(['x', 'y', 'z']) 583 ... Action('b', False, [Variable('x'), Variable('z')])]).vars() == set(['x', 'y', 'z'])
581 True 584 True
582 - 585 +
583 @return: the set of variable names 586 @return: the set of variable names
584 @rtype: `set` of `str` 587 @rtype: `set` of `str`
585 """ 588 """
...@@ -590,12 +593,12 @@ class MultiAction (object) : ...@@ -590,12 +593,12 @@ class MultiAction (object) :
590 def synchronise (self, other, name) : 593 def synchronise (self, other, name) :
591 """Search all the possible synchronisation on an action name with 594 """Search all the possible synchronisation on an action name with
592 another multiaction. 595 another multiaction.
593 - 596 +
594 This method returns an iterator that yields for each possible 597 This method returns an iterator that yields for each possible
595 synchronisation a 4-tuple whose components are: 598 synchronisation a 4-tuple whose components are:
596 - 599 +
597 - 600 +
598 - 601 +
599 * the sending action that did synchronise, it is already 602 * the sending action that did synchronise, it is already
600 unified, so the corresponding receiving action is just 603 unified, so the corresponding receiving action is just
601 the same with the reversed send flag 604 the same with the reversed send flag
...@@ -605,7 +608,7 @@ class MultiAction (object) : ...@@ -605,7 +608,7 @@ class MultiAction (object) :
605 that provided the sending action 608 that provided the sending action
606 * the substitution that must be applied to the transition 609 * the substitution that must be applied to the transition
607 that provided the receiving action 610 that provided the receiving action
608 - 611 +
609 >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]), 612 >>> m = MultiAction([Action('a', True, [Variable('x'), Value(2)]),
610 ... Action('a', True, [Value(3), Variable('y')]), 613 ... Action('a', True, [Value(3), Variable('y')]),
611 ... Action('b', False, [Variable('x'), Variable('y')])]) 614 ... Action('b', False, [Variable('x'), Variable('y')])])
...@@ -615,7 +618,7 @@ class MultiAction (object) : ...@@ -615,7 +618,7 @@ class MultiAction (object) :
615 ... print('%s %s %s %s' % (str(a), str(x), list(sorted(u.items())), list(sorted(v.items())))) 618 ... print('%s %s %s %s' % (str(a), str(x), list(sorted(u.items())), list(sorted(v.items()))))
616 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'))] 619 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'))]
617 a!(3,a) [a!(x,2), b?(x,a), c?(a)] [('w', Value(3)), ('y', Variable('a'))] [('w', Value(3)), ('y', Variable('a'))] 620 a!(3,a) [a!(x,2), b?(x,a), c?(a)] [('w', Value(3)), ('y', Variable('a'))] [('w', Value(3)), ('y', Variable('a'))]
618 - 621 +
619 @param other: the other multiaction to synchronise with 622 @param other: the other multiaction to synchronise with
620 @type other: `MultiAction` 623 @type other: `MultiAction`
621 @param name: the name of the action to synchronise on 624 @param name: the name of the action to synchronise on
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
3 Petri nets objects are saved in PNML, other Python objects are saved 3 Petri nets objects are saved in PNML, other Python objects are saved
4 in a readable format when possible and pickled as a last solution. 4 in a readable format when possible and pickled as a last solution.
5 This should result in a complete PNML serialization of any object. 5 This should result in a complete PNML serialization of any object.
6 +
7 +@todo: revise documentation
6 """ 8 """
7 9
8 import xml.dom.minidom 10 import xml.dom.minidom
...@@ -160,7 +162,7 @@ class _set (object) : ...@@ -160,7 +162,7 @@ class _set (object) :
160 162
161 class Tree (object) : 163 class Tree (object) :
162 """Abstraction of a PNML tree 164 """Abstraction of a PNML tree
163 - 165 +
164 >>> Tree('tag', 'data', Tree('child', None), attr='attribute value') 166 >>> Tree('tag', 'data', Tree('child', None), attr='attribute value')
165 <?xml version="1.0" encoding="utf-8"?> 167 <?xml version="1.0" encoding="utf-8"?>
166 <pnml> 168 <pnml>
...@@ -172,7 +174,7 @@ class Tree (object) : ...@@ -172,7 +174,7 @@ class Tree (object) :
172 """ 174 """
173 def __init__ (self, _name, _data, *_children, **_attributes) : 175 def __init__ (self, _name, _data, *_children, **_attributes) :
174 """Initialize a PNML tree 176 """Initialize a PNML tree
175 - 177 +
176 >>> Tree('tag', 'data', 178 >>> Tree('tag', 'data',
177 ... Tree('first_child', None), 179 ... Tree('first_child', None),
178 ... Tree('second_child', None), 180 ... Tree('second_child', None),
...@@ -186,10 +188,10 @@ class Tree (object) : ...@@ -186,10 +188,10 @@ class Tree (object) :
186 data 188 data
187 </tag> 189 </tag>
188 </pnml> 190 </pnml>
189 - 191 +
190 Note: parameters names start with a '_' in order to allow for 192 Note: parameters names start with a '_' in order to allow for
191 using them as attributes. 193 using them as attributes.
192 - 194 +
193 @param _name: the name of the tag 195 @param _name: the name of the tag
194 @type _name: `str` 196 @type _name: `str`
195 @param _data: the text held by the tag or `None` 197 @param _data: the text held by the tag or `None`
...@@ -225,7 +227,7 @@ class Tree (object) : ...@@ -225,7 +227,7 @@ class Tree (object) :
225 return result 227 return result
226 def to_pnml (self) : 228 def to_pnml (self) :
227 """Dumps a PNML tree to an XML string 229 """Dumps a PNML tree to an XML string
228 - 230 +
229 >>> print(Tree('tag', 'data', Tree('child', None), attr='value').to_pnml()) 231 >>> print(Tree('tag', 'data', Tree('child', None), attr='value').to_pnml())
230 <?xml version="1.0" encoding="utf-8"?> 232 <?xml version="1.0" encoding="utf-8"?>
231 <pnml> 233 <pnml>
...@@ -234,7 +236,7 @@ class Tree (object) : ...@@ -234,7 +236,7 @@ class Tree (object) :
234 data 236 data
235 </tag> 237 </tag>
236 </pnml> 238 </pnml>
237 - 239 +
238 @return: the XML string that represents the PNML tree 240 @return: the XML string that represents the PNML tree
239 @rtype: `str` 241 @rtype: `str`
240 """ 242 """
...@@ -269,7 +271,7 @@ class Tree (object) : ...@@ -269,7 +271,7 @@ class Tree (object) :
269 @classmethod 271 @classmethod
270 def from_dom (cls, node) : 272 def from_dom (cls, node) :
271 """Load a PNML tree from an XML DOM representation 273 """Load a PNML tree from an XML DOM representation
272 - 274 +
273 >>> src = Tree('object', '42', type='int').to_pnml() 275 >>> src = Tree('object', '42', type='int').to_pnml()
274 >>> dom = xml.dom.minidom.parseString(src) 276 >>> dom = xml.dom.minidom.parseString(src)
275 >>> Tree.from_dom(dom.documentElement) 277 >>> Tree.from_dom(dom.documentElement)
...@@ -279,7 +281,7 @@ class Tree (object) : ...@@ -279,7 +281,7 @@ class Tree (object) :
279 42 281 42
280 </object> 282 </object>
281 </pnml> 283 </pnml>
282 - 284 +
283 @param node: the DOM node to load 285 @param node: the DOM node to load
284 @type node: `xml.dom.minidom.Element` 286 @type node: `xml.dom.minidom.Element`
285 @return: the loaded PNML tree 287 @return: the loaded PNML tree
...@@ -299,7 +301,7 @@ class Tree (object) : ...@@ -299,7 +301,7 @@ class Tree (object) :
299 @classmethod 301 @classmethod
300 def from_pnml (cls, source, plugins=[]) : 302 def from_pnml (cls, source, plugins=[]) :
301 """Load a PNML tree from an XML string representation 303 """Load a PNML tree from an XML string representation
302 - 304 +
303 >>> src = Tree('object', '42', type='int').to_pnml() 305 >>> src = Tree('object', '42', type='int').to_pnml()
304 >>> Tree.from_pnml(src) 306 >>> Tree.from_pnml(src)
305 <?xml version="1.0" encoding="utf-8"?> 307 <?xml version="1.0" encoding="utf-8"?>
...@@ -308,7 +310,7 @@ class Tree (object) : ...@@ -308,7 +310,7 @@ class Tree (object) :
308 42 310 42
309 </object> 311 </object>
310 </pnml> 312 </pnml>
311 - 313 +
312 @param source: the XML string to load or an opened file that 314 @param source: the XML string to load or an opened file that
313 contains it 315 contains it
314 @type source: `str` or `file` 316 @type source: `str` or `file`
...@@ -360,7 +362,7 @@ class Tree (object) : ...@@ -360,7 +362,7 @@ class Tree (object) :
360 return result 362 return result
361 def nodes (self) : 363 def nodes (self) :
362 """Iterate over all the nodes (top-down) in a tree 364 """Iterate over all the nodes (top-down) in a tree
363 - 365 +
364 >>> t = Tree('foo', None, 366 >>> t = Tree('foo', None,
365 ... Tree('bar', None), 367 ... Tree('bar', None),
366 ... Tree('egg', None, 368 ... Tree('egg', None,
...@@ -371,7 +373,7 @@ class Tree (object) : ...@@ -371,7 +373,7 @@ class Tree (object) :
371 <PNML tree 'bar'> 373 <PNML tree 'bar'>
372 <PNML tree 'egg'> 374 <PNML tree 'egg'>
373 <PNML tree 'spam'> 375 <PNML tree 'spam'>
374 - 376 +
375 @return: an iterator on all the nodes in the tree, including 377 @return: an iterator on all the nodes in the tree, including
376 this one 378 this one
377 @rtype: `generator` 379 @rtype: `generator`
...@@ -383,7 +385,7 @@ class Tree (object) : ...@@ -383,7 +385,7 @@ class Tree (object) :
383 def update (self, other) : 385 def update (self, other) :
384 """Incorporates children, attributes and data from another PNML 386 """Incorporates children, attributes and data from another PNML
385 tree 387 tree
386 - 388 +
387 >>> t = Tree('foo', 'hello', 389 >>> t = Tree('foo', 'hello',
388 ... Tree('bar', None), 390 ... Tree('bar', None),
389 ... Tree('egg', None, 391 ... Tree('egg', None,
...@@ -411,7 +413,7 @@ class Tree (object) : ...@@ -411,7 +413,7 @@ class Tree (object) :
411 >>> try : t.update(o) 413 >>> try : t.update(o)
412 ... except SnakesError : print(sys.exc_info()[1]) 414 ... except SnakesError : print(sys.exc_info()[1])
413 tag mismatch 'foo', 'oops' 415 tag mismatch 'foo', 'oops'
414 - 416 +
415 @param other: the other tree to get data from 417 @param other: the other tree to get data from
416 @type other: `Tree` 418 @type other: `Tree`
417 @raise SnakesError: when `other` has not the same tag as 419 @raise SnakesError: when `other` has not the same tag as
...@@ -424,7 +426,7 @@ class Tree (object) : ...@@ -424,7 +426,7 @@ class Tree (object) :
424 self.add_data(other.data) 426 self.add_data(other.data)
425 def add_child (self, child) : 427 def add_child (self, child) :
426 """Add a child to a PNML tree 428 """Add a child to a PNML tree
427 - 429 +
428 >>> t = Tree('foo', None) 430 >>> t = Tree('foo', None)
429 >>> t.add_child(Tree('bar', None)) 431 >>> t.add_child(Tree('bar', None))
430 >>> t 432 >>> t
...@@ -434,14 +436,14 @@ class Tree (object) : ...@@ -434,14 +436,14 @@ class Tree (object) :
434 <bar/> 436 <bar/>
435 </foo> 437 </foo>
436 </pnml> 438 </pnml>
437 - 439 +
438 @param child: the PNML tree to append 440 @param child: the PNML tree to append
439 @type child: `Tree` 441 @type child: `Tree`
440 """ 442 """
441 self.children.append(child) 443 self.children.append(child)
442 def add_data (self, data, sep='\n') : 444 def add_data (self, data, sep='\n') :
443 """Appends data to the current node 445 """Appends data to the current node
444 - 446 +
445 >>> t = Tree('foo', None) 447 >>> t = Tree('foo', None)
446 >>> t.add_data('hello') 448 >>> t.add_data('hello')
447 >>> t 449 >>> t
...@@ -469,7 +471,7 @@ class Tree (object) : ...@@ -469,7 +471,7 @@ class Tree (object) :
469 world! 471 world!
470 </foo> 472 </foo>
471 </pnml> 473 </pnml>
472 - 474 +
473 @param data: the data to add 475 @param data: the data to add
474 @type data: `str` 476 @type data: `str`
475 @param sep: separator to insert between pieces of data 477 @param sep: separator to insert between pieces of data
...@@ -486,14 +488,14 @@ class Tree (object) : ...@@ -486,14 +488,14 @@ class Tree (object) :
486 pass 488 pass
487 def __getitem__ (self, name) : 489 def __getitem__ (self, name) :
488 """Returns one attribute 490 """Returns one attribute
489 - 491 +
490 >>> Tree('foo', None, x='egg', y='spam')['x'] 492 >>> Tree('foo', None, x='egg', y='spam')['x']
491 'egg' 493 'egg'
492 >>> Tree('foo', None, x='egg', y='spam')['z'] 494 >>> Tree('foo', None, x='egg', y='spam')['z']
493 Traceback (most recent call last): 495 Traceback (most recent call last):
494 ... 496 ...
495 KeyError: 'z' 497 KeyError: 'z'
496 - 498 +
497 @param name: the name of the attribute 499 @param name: the name of the attribute
498 @type name: `str` 500 @type name: `str`
499 @return: the value of the attribute 501 @return: the value of the attribute
...@@ -503,7 +505,7 @@ class Tree (object) : ...@@ -503,7 +505,7 @@ class Tree (object) :
503 return self.attributes[name] 505 return self.attributes[name]
504 def __setitem__ (self, name, value) : 506 def __setitem__ (self, name, value) :
505 """Sets an attribute 507 """Sets an attribute
506 - 508 +
507 >>> t = Tree('foo', None) 509 >>> t = Tree('foo', None)
508 >>> t['egg'] = 'spam' 510 >>> t['egg'] = 'spam'
509 >>> t 511 >>> t
...@@ -511,7 +513,7 @@ class Tree (object) : ...@@ -511,7 +513,7 @@ class Tree (object) :
511 <pnml> 513 <pnml>
512 <foo egg="spam"/> 514 <foo egg="spam"/>
513 </pnml> 515 </pnml>
514 - 516 +
515 @param name: the name of the attribute 517 @param name: the name of the attribute
516 @type name: `str` 518 @type name: `str`
517 @param value: the value of the attribute 519 @param value: the value of the attribute
...@@ -520,20 +522,20 @@ class Tree (object) : ...@@ -520,20 +522,20 @@ class Tree (object) :
520 self.attributes[name] = value 522 self.attributes[name] = value
521 def __iter__ (self) : 523 def __iter__ (self) :
522 """Iterate over children nodes 524 """Iterate over children nodes
523 - 525 +
524 >>> [str(node) for node in Tree('foo', None, 526 >>> [str(node) for node in Tree('foo', None,
525 ... Tree('egg', None), 527 ... Tree('egg', None),
526 ... Tree('spam', None, 528 ... Tree('spam', None,
527 ... Tree('bar', None)))] 529 ... Tree('bar', None)))]
528 ["<PNML tree 'egg'>", "<PNML tree 'spam'>"] 530 ["<PNML tree 'egg'>", "<PNML tree 'spam'>"]
529 - 531 +
530 @return: an iterator over direct children of the node 532 @return: an iterator over direct children of the node
531 @rtype: `generator` 533 @rtype: `generator`
532 """ 534 """
533 return iter(self.children) 535 return iter(self.children)
534 def has_child (self, name) : 536 def has_child (self, name) :
535 """Test if the tree has the given tag as a direct child 537 """Test if the tree has the given tag as a direct child
536 - 538 +
537 >>> t = Tree('foo', None, 539 >>> t = Tree('foo', None,
538 ... Tree('egg', None), 540 ... Tree('egg', None),
539 ... Tree('spam', None, 541 ... Tree('spam', None,
...@@ -544,7 +546,7 @@ class Tree (object) : ...@@ -544,7 +546,7 @@ class Tree (object) :
544 False 546 False
545 >>> t.has_child('python') 547 >>> t.has_child('python')
546 False 548 False
547 - 549 +
548 @param name: tag name to search for 550 @param name: tag name to search for
549 @type name: `str` 551 @type name: `str`
550 @return: a Boolean value indicating wether such a child was 552 @return: a Boolean value indicating wether such a child was
...@@ -557,7 +559,7 @@ class Tree (object) : ...@@ -557,7 +559,7 @@ class Tree (object) :
557 return False 559 return False
558 def child (self, name=None) : 560 def child (self, name=None) :
559 """Return the direct child that as the given tag 561 """Return the direct child that as the given tag
560 - 562 +
561 >>> t = Tree('foo', None, 563 >>> t = Tree('foo', None,
562 ... Tree('egg', 'first'), 564 ... Tree('egg', 'first'),
563 ... Tree('egg', 'second'), 565 ... Tree('egg', 'second'),
...@@ -582,7 +584,7 @@ class Tree (object) : ...@@ -582,7 +584,7 @@ class Tree (object) :
582 >>> try : t.child() 584 >>> try : t.child()
583 ... except SnakesError : print(sys.exc_info()[1]) 585 ... except SnakesError : print(sys.exc_info()[1])
584 multiple children 586 multiple children
585 - 587 +
586 @param name: name of the tag to search for, if `None`, the 588 @param name: name of the tag to search for, if `None`, the
587 fisrt child is returned if it is the only child 589 fisrt child is returned if it is the only child
588 @type name: `str` or `None` 590 @type name: `str` or `None`
...@@ -606,7 +608,7 @@ class Tree (object) : ...@@ -606,7 +608,7 @@ class Tree (object) :
606 return result 608 return result
607 def get_children (self, name=None) : 609 def get_children (self, name=None) :
608 """Iterates over direct children having the given tag 610 """Iterates over direct children having the given tag
609 - 611 +
610 >>> t = Tree('foo', None, 612 >>> t = Tree('foo', None,
611 ... Tree('egg', 'first'), 613 ... Tree('egg', 'first'),
612 ... Tree('egg', 'second'), 614 ... Tree('egg', 'second'),
...@@ -620,7 +622,7 @@ class Tree (object) : ...@@ -620,7 +622,7 @@ class Tree (object) :
620 [] 622 []
621 >>> [str(n) for n in t.get_children('bar')] 623 >>> [str(n) for n in t.get_children('bar')]
622 [] 624 []
623 - 625 +
624 @param name: tag to search for or `None` 626 @param name: tag to search for or `None`
625 @type name: `str` or `None` 627 @type name: `str` or `None`
626 @return: iterator over all the children if `name` is `None`, 628 @return: iterator over all the children if `name` is `None`,
...@@ -632,20 +634,20 @@ class Tree (object) : ...@@ -632,20 +634,20 @@ class Tree (object) :
632 yield child 634 yield child
633 def __str__ (self) : 635 def __str__ (self) :
634 """Return a simple string representation of the node 636 """Return a simple string representation of the node
635 - 637 +
636 >>> str(Tree('foo', None, Tree('child', None))) 638 >>> str(Tree('foo', None, Tree('child', None)))
637 "<PNML tree 'foo'>" 639 "<PNML tree 'foo'>"
638 - 640 +
639 @return: simple string representation of the node 641 @return: simple string representation of the node
640 @rtype: `str` 642 @rtype: `str`
641 """ 643 """
642 return "<PNML tree %r>" % self.name 644 return "<PNML tree %r>" % self.name
643 def __repr__ (self) : 645 def __repr__ (self) :
644 """Return a detailed representation of the node. 646 """Return a detailed representation of the node.
645 - 647 +
646 This is actually the XML text that corresponds to the `Tree`, 648 This is actually the XML text that corresponds to the `Tree`,
647 as returned by `Tree.to_pnml`. 649 as returned by `Tree.to_pnml`.
648 - 650 +
649 >>> print(repr(Tree('foo', None, Tree('child', None)))) 651 >>> print(repr(Tree('foo', None, Tree('child', None))))
650 <?xml version="1.0" encoding="utf-8"?> 652 <?xml version="1.0" encoding="utf-8"?>
651 <pnml> 653 <pnml>
...@@ -653,7 +655,7 @@ class Tree (object) : ...@@ -653,7 +655,7 @@ class Tree (object) :
653 <child/> 655 <child/>
654 </foo> 656 </foo>
655 </pnml> 657 </pnml>
656 - 658 +
657 @return: XML string representation of the node 659 @return: XML string representation of the node
658 @rtype: `str` 660 @rtype: `str`
659 """ 661 """
...@@ -663,10 +665,10 @@ class Tree (object) : ...@@ -663,10 +665,10 @@ class Tree (object) :
663 @classmethod 665 @classmethod
664 def from_obj (cls, obj) : 666 def from_obj (cls, obj) :
665 """Builds a PNML tree from an object. 667 """Builds a PNML tree from an object.
666 - 668 +
667 Objects defined in SNAKES usually have a method `__pnmldump__` 669 Objects defined in SNAKES usually have a method `__pnmldump__`
668 that handles the conversion, for instance: 670 that handles the conversion, for instance:
669 - 671 +
670 >>> import snakes.nets 672 >>> import snakes.nets
671 >>> Tree.from_obj(snakes.nets.Place('p')) 673 >>> Tree.from_obj(snakes.nets.Place('p'))
672 <?xml version="1.0" encoding="utf-8"?> 674 <?xml version="1.0" encoding="utf-8"?>
...@@ -678,9 +680,9 @@ class Tree (object) : ...@@ -678,9 +680,9 @@ class Tree (object) :
678 </initialMarking> 680 </initialMarking>
679 </place> 681 </place>
680 </pnml> 682 </pnml>
681 - 683 +
682 Most basic Python classes are handled has readable XML: 684 Most basic Python classes are handled has readable XML:
683 - 685 +
684 >>> Tree.from_obj(42) 686 >>> Tree.from_obj(42)
685 <?xml version="1.0" encoding="utf-8"?> 687 <?xml version="1.0" encoding="utf-8"?>
686 <pnml> 688 <pnml>
...@@ -703,10 +705,10 @@ class Tree (object) : ...@@ -703,10 +705,10 @@ class Tree (object) :
703 </object> 705 </object>
704 </object> 706 </object>
705 </pnml> 707 </pnml>
706 - 708 +
707 Otherwise, the object is serialised using module `pickle`, 709 Otherwise, the object is serialised using module `pickle`,
708 which allows to embed almost anything into PNML. 710 which allows to embed almost anything into PNML.
709 - 711 +
710 >>> import re 712 >>> import re
711 >>> Tree.from_obj(re.compile('foo|bar')) # serialized data replaced with '...' 713 >>> Tree.from_obj(re.compile('foo|bar')) # serialized data replaced with '...'
712 <?xml version="1.0" encoding="utf-8"?> 714 <?xml version="1.0" encoding="utf-8"?>
...@@ -715,7 +717,7 @@ class Tree (object) : ...@@ -715,7 +717,7 @@ class Tree (object) :
715 ... 717 ...
716 </object> 718 </object>
717 </pnml> 719 </pnml>
718 - 720 +
719 @param obj: the object to convert to PNML 721 @param obj: the object to convert to PNML
720 @type obj: `object` 722 @type obj: `object`
721 @return: the corresponding PNML tree 723 @return: the corresponding PNML tree
...@@ -854,7 +856,7 @@ class Tree (object) : ...@@ -854,7 +856,7 @@ class Tree (object) :
854 return pickle.loads(self.data) 856 return pickle.loads(self.data)
855 def to_obj (self) : 857 def to_obj (self) :
856 """Build an object from its PNML representation 858 """Build an object from its PNML representation
857 - 859 +
858 This is just the reverse as `Tree.from_obj`, objects that have 860 This is just the reverse as `Tree.from_obj`, objects that have
859 a `__pnmldump__` method should also have a `__pnmlload__` 861 a `__pnmldump__` method should also have a `__pnmlload__`
860 class method to perform the reverse operation, together with 862 class method to perform the reverse operation, together with
...@@ -863,10 +865,10 @@ class Tree (object) : ...@@ -863,10 +865,10 @@ class Tree (object) :
863 that `C.__pnmltag__ == 'foo'` is searched in module 865 that `C.__pnmltag__ == 'foo'` is searched in module
864 `snakes.nets` and `C.__pnmlload__(tree)` is called to rebuild 866 `snakes.nets` and `C.__pnmlload__(tree)` is called to rebuild
865 the object. 867 the object.
866 - 868 +
867 Standard Python objects and pickled ones are also recognised 869 Standard Python objects and pickled ones are also recognised
868 and correctly rebuilt. 870 and correctly rebuilt.
869 - 871 +
870 >>> import snakes.nets 872 >>> import snakes.nets
871 >>> Tree.from_obj(snakes.nets.Place('p')).to_obj() 873 >>> Tree.from_obj(snakes.nets.Place('p')).to_obj()
872 Place('p', MultiSet([]), tAll) 874 Place('p', MultiSet([]), tAll)
...@@ -877,7 +879,7 @@ class Tree (object) : ...@@ -877,7 +879,7 @@ class Tree (object) :
877 >>> import re 879 >>> import re
878 >>> Tree.from_obj(re.compile('foo|bar')).to_obj() 880 >>> Tree.from_obj(re.compile('foo|bar')).to_obj()
879 <... object at ...> 881 <... object at ...>
880 - 882 +
881 @return: the Python object encoded by the PNML tree 883 @return: the Python object encoded by the PNML tree
882 @rtype: `object` 884 @rtype: `object`
883 """ 885 """
...@@ -910,7 +912,7 @@ class Tree (object) : ...@@ -910,7 +912,7 @@ class Tree (object) :
910 912
911 def dumps (obj) : 913 def dumps (obj) :
912 """Dump an object to a PNML string 914 """Dump an object to a PNML string
913 - 915 +
914 >>> print(dumps(42)) 916 >>> print(dumps(42))
915 <?xml version="1.0" encoding="utf-8"?> 917 <?xml version="1.0" encoding="utf-8"?>
916 <pnml> 918 <pnml>
...@@ -918,7 +920,7 @@ def dumps (obj) : ...@@ -918,7 +920,7 @@ def dumps (obj) :
918 42 920 42
919 </object> 921 </object>
920 </pnml> 922 </pnml>
921 - 923 +
922 @param obj: the object to dump 924 @param obj: the object to dump
923 @type obj: `object` 925 @type obj: `object`
924 @return: the PNML that represents the object 926 @return: the PNML that represents the object
...@@ -928,10 +930,10 @@ def dumps (obj) : ...@@ -928,10 +930,10 @@ def dumps (obj) :
928 930
929 def loads (source, plugins=[]) : 931 def loads (source, plugins=[]) :
930 """Load an object from a PNML string 932 """Load an object from a PNML string
931 - 933 +
932 >>> loads(dumps(42)) 934 >>> loads(dumps(42))
933 42 935 42
934 - 936 +
935 @param source: the data to parse 937 @param source: the data to parse
936 @type source: `str` 938 @type source: `str`
937 @return: the object represented by the source 939 @return: the object represented by the source
......
...@@ -60,6 +60,8 @@ TypeError: ... ...@@ -60,6 +60,8 @@ TypeError: ...
60 Traceback (most recent call last): 60 Traceback (most recent call last):
61 ... 61 ...
62 TypeError: ... 62 TypeError: ...
63 +
64 +@todo: revise documentation
63 """ 65 """
64 66
65 import inspect, sys 67 import inspect, sys
......
1 +"""
2 +@todo: revise (actually make) documentation
3 +"""
4 +
1 from snakes import SnakesError 5 from snakes import SnakesError
2 6
3 class CompilationError (SnakesError) : 7 class CompilationError (SnakesError) :
......
1 +"""
2 +@todo: revise (actually make) documentation
3 +"""
4 +
1 import sys, os, os.path 5 import sys, os, os.path
2 -import inspect, fnmatch, collections 6 +import inspect, fnmatch, collections, re, shlex
3 -import textwrap, doctest, ast 7 +import textwrap, doctest
4 import snakes 8 import snakes
5 from snakes.lang import unparse 9 from snakes.lang import unparse
10 +from snakes.lang.python.parser import parse, ast
11 +
12 +try :
13 + import markdown
14 +except :
15 + markdown = None
6 16
7 ## 17 ##
8 ## console messages 18 ## console messages
...@@ -62,6 +72,13 @@ class DocExtract (object) : ...@@ -62,6 +72,13 @@ class DocExtract (object) :
62 self.out = None 72 self.out = None
63 self.exclude = exclude 73 self.exclude = exclude
64 self._last = "\n\n" 74 self._last = "\n\n"
75 + def md (self, text, inline=True) :
76 + if markdown is None :
77 + return text
78 + elif inline :
79 + return re.sub("</?p>", "\n", markdown.markdown(text), re.I)
80 + else :
81 + return markdown.markdown(text)
65 def openout (self, path) : 82 def openout (self, path) :
66 if self.out is not None : 83 if self.out is not None :
67 self.out.close() 84 self.out.close()
...@@ -81,6 +98,10 @@ class DocExtract (object) : ...@@ -81,6 +98,10 @@ class DocExtract (object) :
81 return False 98 return False
82 outdir = os.path.join(self.outpath, relpath) 99 outdir = os.path.join(self.outpath, relpath)
83 outpath = os.path.join(outdir, target) 100 outpath = os.path.join(outdir, target)
101 + if os.path.exists(outpath) :
102 + if os.stat(path).st_mtime <= os.stat(outpath).st_mtime :
103 + return False
104 + self.inpath = path
84 info("%s -> %r" % (self.module, outpath)) 105 info("%s -> %r" % (self.module, outpath))
85 if not os.path.exists(outdir) : 106 if not os.path.exists(outdir) :
86 os.makedirs(outdir) 107 os.makedirs(outdir)
...@@ -114,56 +135,89 @@ class DocExtract (object) : ...@@ -114,56 +135,89 @@ class DocExtract (object) :
114 def process (self) : 135 def process (self) :
115 for dirpath, dirnames, filenames in os.walk(self.path) : 136 for dirpath, dirnames, filenames in os.walk(self.path) :
116 for name in sorted(filenames) : 137 for name in sorted(filenames) :
117 - if not name.endswith(".py") : 138 + if not name.endswith(".py") or name.startswith(".") :
118 continue 139 continue
119 path = os.path.join(dirpath, name) 140 path = os.path.join(dirpath, name)
120 if not self.openout(path) : 141 if not self.openout(path) :
121 continue 142 continue
122 - node = ast.parse(open(path).read()) 143 + node = parse(open(path).read())
123 if ".plugins." in self.module : 144 if ".plugins." in self.module :
124 self.visit_plugin(node) 145 self.visit_plugin(node)
125 else : 146 else :
126 self.visit_module(node) 147 self.visit_module(node)
127 def _pass (self, node) : 148 def _pass (self, node) :
128 pass 149 pass
150 + def directive (self, node) :
151 + lines = node.st.text.lexer.lines
152 + num = node.st.srow - 2
153 + while num >= 0 and (not lines[num].strip()
154 + or lines[num].strip().startswith("@")) :
155 + num -= 1
156 + if num >= 0 and lines[num].strip().startswith("#") :
157 + dirline = lines[num].lstrip("# \t").rstrip()
158 + items = shlex.split(dirline)
159 + if len(items) >= 2 and items[0].lower() == "apidoc" :
160 + if len(items) == 2 and items[1].lower() in ("skip", "stop") :
161 + return items[1]
162 + elif items[1].lower() == "include" :
163 + path = items[2]
164 + try :
165 + args = dict(i.split("=", 1) for i in items[3:])
166 + except :
167 + err("invalid directive %r (line %s)"
168 + % (dirline, num+1))
169 + self.write_include(path, **args)
170 + else :
171 + err("unknown directive %r (line %s)"
172 + % (items[1], num+1))
173 + return None
174 + def children (self, node) :
175 + for child in ast.iter_child_nodes(node) :
176 + directive = self.directive(child)
177 + if directive == "skip" :
178 + continue
179 + elif directive == "stop" :
180 + break
181 + yield child
129 def visit (self, node) : 182 def visit (self, node) :
130 name = getattr(node, "name", "__") 183 name = getattr(node, "name", "__")
131 if (name.startswith("_") and not (name.startswith("__") 184 if (name.startswith("_") and not (name.startswith("__")
132 and name.endswith("__"))) : 185 and name.endswith("__"))) :
133 return 186 return
134 try : 187 try :
135 - getattr(self, "visit_" + node.__class__.__name__, self._pass)(node) 188 + getattr(self, "visit_" + node.__class__.__name__,
189 + self._pass)(node)
136 except : 190 except :
137 - src = unparse(node) 191 + src = node.st.source()
138 if len(src) > 40 : 192 if len(src) > 40 :
139 src = src[:40] + "..." 193 src = src[:40] + "..."
140 err("line %s source %r" % (node.lineno, src)) 194 err("line %s source %r" % (node.lineno, src))
141 raise 195 raise
142 def visit_module (self, node) : 196 def visit_module (self, node) :
143 self.write_module() 197 self.write_module()
144 - for child in ast.iter_child_nodes(node) : 198 + for child in self.children(node) :
145 self.visit(child) 199 self.visit(child)
146 def visit_plugin (self, node) : 200 def visit_plugin (self, node) :
147 self.write_module() 201 self.write_module()
148 extend = None 202 extend = None
149 - for child in ast.iter_child_nodes(node) : 203 + for child in self.children(node) :
150 if (getattr(child, "name", None) == "extend" 204 if (getattr(child, "name", None) == "extend"
151 and isinstance(child, ast.FunctionDef)) : 205 and isinstance(child, ast.FunctionDef)) :
152 extend = child 206 extend = child
153 else : 207 else :
154 self.visit(child) 208 self.visit(child)
155 self.write_plugin() 209 self.write_plugin()
156 - for child in ast.iter_child_nodes(extend) : 210 + for child in self.children(extend) :
157 self.visit(child) 211 self.visit(child)
158 def visit_ClassDef (self, node) : 212 def visit_ClassDef (self, node) :
159 self.write_class(node) 213 self.write_class(node)
160 self.classname = node.name 214 self.classname = node.name
161 - for child in ast.iter_child_nodes(node) : 215 + for child in self.children(node) :
162 self.visit(child) 216 self.visit(child)
163 self.classname = None 217 self.classname = None
164 def visit_FunctionDef (self, node) : 218 def visit_FunctionDef (self, node) :
165 self.write_function(node) 219 self.write_function(node)
166 - self.args = [n.id for n in node.args.args] 220 + self.args = [n.arg for n in node.args.args]
167 if self.args and self.args[0] == "self" : 221 if self.args and self.args[0] == "self" :
168 del self.args[0] 222 del self.args[0]
169 if node.args.vararg : 223 if node.args.vararg :
...@@ -193,26 +247,32 @@ class DocExtract (object) : ...@@ -193,26 +247,32 @@ class DocExtract (object) :
193 else : 247 else :
194 self.writeline("### Function `%s` ###" % node.name) 248 self.writeline("### Function `%s` ###" % node.name)
195 self.newline() 249 self.newline()
196 - self.writeline(" :::python") 250 + self.write_def(node)
197 - for line in unparse(node).splitlines() :
198 - if line.startswith("def") :
199 - self.writeline(" %s ..." % line)
200 - break
201 - else :
202 - self.writeline(" " + line)
203 - self.newline
204 def write_class (self, node) : 251 def write_class (self, node) :
205 self.newline() 252 self.newline()
206 self.writeline("### Class `%s` ###" % node.name) 253 self.writeline("### Class `%s` ###" % node.name)
254 + self.write_def(node)
255 + def write_def (self, node) :
256 + indent = node.st.scol
257 + if node.st[0].symbol == "decorated" :
258 + srow, scol = node.st[0][0][0][0].srow, node.st[0][0][0][0].scol
259 + erow, ecol = node.st[0][1][-2].erow, node.st[0][1][-2].ecol
260 + else :
261 + srow, scol = node.st[0].srow, node.st[0].scol
262 + erow, ecol = node.st[0][-2].erow, node.st[0][-2].ecol
263 + lines = node.st.text.lexer.lines
264 + if srow == erow :
265 + source = [lines[srow-1][scol:ecol+1]]
266 + else :
267 + source = lines[srow-1:erow]
268 + source[0] = source[0][scol:]
269 + source[1:-1] = [s[indent:] for s in source[1:-1]]
270 + source[-1] = source[-1][indent:ecol+1].rstrip() + " ..."
207 self.newline() 271 self.newline()
208 self.writeline(" :::python") 272 self.writeline(" :::python")
209 - for line in unparse(node).splitlines() : 273 + for line in source :
210 - if line.startswith("class") : 274 + self.writeline(" " + line)
211 - self.writeline(" %s ..." % line) 275 + self.newline()
212 - break
213 - else :
214 - self.writeline(" " + line)
215 - self.newline
216 parse = doctest.DocTestParser().parse 276 parse = doctest.DocTestParser().parse
217 def write_doc (self, doc) : 277 def write_doc (self, doc) :
218 if doc is None : 278 if doc is None :
...@@ -240,7 +300,9 @@ class DocExtract (object) : ...@@ -240,7 +300,9 @@ class DocExtract (object) :
240 if not test : 300 if not test :
241 test = True 301 test = True
242 self.newline() 302 self.newline()
243 - self.writeline(" :::python") 303 + self.writetext("<!-- this comment avoids a bug in"
304 + " Markdown parsing -->")
305 + self.writeline(" :::pycon")
244 for i, line in enumerate(doc.source.splitlines()) : 306 for i, line in enumerate(doc.source.splitlines()) :
245 if i > 0 : 307 if i > 0 :
246 self.writeline(" ... %s" % line) 308 self.writeline(" ... %s" % line)
...@@ -281,65 +343,74 @@ class DocExtract (object) : ...@@ -281,65 +343,74 @@ class DocExtract (object) :
281 if any(k in info for k in ("author", "organization", "copyright", 343 if any(k in info for k in ("author", "organization", "copyright",
282 "license", "contact")) : 344 "license", "contact")) :
283 self.newline() 345 self.newline()
284 - self.writeline('<div class="api-info">') 346 + self.writeline('<ul id="api-info">')
285 - for tag in ("author", "organization", "copyright", 347 + for tag in ("author", "organization", "contact",
286 - "license", "contact") : 348 + "copyright", "license", ) :
287 if tag in info : 349 if tag in info :
288 - self.writeline('<div class="api-%s">' % tag) 350 + self.writeline('<li id="api-%s">' % tag)
289 self.writetext('<span class="api-title">%s:</span> %s' 351 self.writetext('<span class="api-title">%s:</span> %s'
290 - % (tag.capitalize(), info[tag]), 352 + % (tag.capitalize(),
353 + self.md(info[tag])),
291 subsequent_indent=" ") 354 subsequent_indent=" ")
292 - self.writeline('</div>') 355 + self.writeline('</li>')
293 - self.writeline('</div>') 356 + self.writeline('</ul>')
294 if any(info[k] for k in 357 if any(info[k] for k in
295 ("todo", "note", "attention", "bug", "warning")) : 358 ("todo", "note", "attention", "bug", "warning")) :
296 self.newline() 359 self.newline()
297 - self.writeline('<div class="api-remarks">') 360 + self.writeline('<div id="api-remarks">')
298 - self.writeline("##### Remarks #####")
299 - self.newline()
300 for tag in ("note", "todo", "attention", "bug", "warning") : 361 for tag in ("note", "todo", "attention", "bug", "warning") :
301 for text in info[tag] : 362 for text in info[tag] :
302 self.writeline('<div class="api-%s">' % tag) 363 self.writeline('<div class="api-%s">' % tag)
303 self.writetext('<span class="api-title">%s:</span> %s' 364 self.writetext('<span class="api-title">%s:</span> %s'
304 - % (tag.capitalize(), text), 365 + % (tag.capitalize(), self.md(text)),
305 subsequent_indent=" ") 366 subsequent_indent=" ")
306 self.writeline('</div>') 367 self.writeline('</div>')
307 self.writeline('</div>') 368 self.writeline('</div>')
308 if (any(info[k] for k in ("param", "type", "keyword")) 369 if (any(info[k] for k in ("param", "type", "keyword"))
309 or any(k in info for k in ("return", "rtype"))) : 370 or any(k in info for k in ("return", "rtype"))) :
310 self.newline() 371 self.newline()
311 - self.writeline('<div class="api-call">')
312 self.writeline("##### Call API #####") 372 self.writeline("##### Call API #####")
313 self.newline() 373 self.newline()
314 for arg in self.args : 374 for arg in self.args :
315 if arg in info["param"] : 375 if arg in info["param"] :
316 - self.writelist("`%s` (%s): %s" 376 + self.writelist("`%s %s`: %s"
317 - % (arg, 377 + % (info["type"].get(arg, "object").strip("`"),
318 - info["type"].get(arg, "`object`"), 378 + arg,
319 info["param"][arg])) 379 info["param"][arg]))
320 else : 380 else :
321 - self.writelist("`%s` (%s)" 381 + self.writelist("`%s %s`"
322 - % (arg, 382 + % (info["type"].get(arg, "object").strip("`"),
323 - info["type"].get(arg, "`object`"))) 383 + arg))
324 for kw, text in sorted(info["keyword"].items()) : 384 for kw, text in sorted(info["keyword"].items()) :
325 self.writelist("`%s`: %s" % (kw, text)) 385 self.writelist("`%s`: %s" % (kw, text))
326 if any(k in info for k in ("return", "rtype")) : 386 if any(k in info for k in ("return", "rtype")) :
327 if "return" in info : 387 if "return" in info :
328 - self.writelist("return %s: %s" 388 + self.writelist("`return %s`: %s"
329 - % (info.get("rtype", "`object`"), 389 + % (info.get("rtype", "object").strip("`"),
330 info["return"])) 390 info["return"]))
331 else : 391 else :
332 - self.writelist("return %s" 392 + self.writelist("`return %s`"
333 - % (info.get("rtype", "`object`"))) 393 + % (info.get("rtype", "object").strip("`")))
334 - self.writeline('</div>')
335 if info["raise"] : 394 if info["raise"] :
336 self.newline() 395 self.newline()
337 - self.writeline('<div class="api-errors">')
338 self.writeline("##### Exceptions #####") 396 self.writeline("##### Exceptions #####")
339 self.newline() 397 self.newline()
340 for exc, reason in sorted(info["raise"].items()) : 398 for exc, reason in sorted(info["raise"].items()) :
341 self.writelist("`%s`: %s" % (exc, reason)) 399 self.writelist("`%s`: %s" % (exc, reason))
342 - self.writeline('</div>') 400 + self.newline()
401 + def write_include (self, name, lang="python") :
402 + if os.path.exists(name) :
403 + path = name
404 + else :
405 + path = os.path.join(os.path.dirname(self.inpath), name)
406 + if not os.path.exists(path) :
407 + err("include file %r not found" % name)
408 + with open(path) as infile :
409 + self.newline()
410 + self.writeline(" :::%s" % lang)
411 + for line in infile :
412 + self.writeline(" " + line.rstrip())
413 + self.newline()
343 414
344 def main (finder, args) : 415 def main (finder, args) :
345 try : 416 try :
......
1 +"""
2 +@todo: revise (actually make) documentation
3 +"""
......