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) :
......
This diff is collapsed. Click to expand it.
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)
......
This diff is collapsed. Click to expand it.
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
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
...@@ -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) :
......
This diff is collapsed. Click to expand it.
1 +"""
2 +@todo: revise (actually make) documentation
3 +"""
......