This package implements SNAKES plugin system. SNAKES plugins themselves are available as modules within the package.
Examples below are based on plugin hello
that is distributed with
SNAKES to be used as an exemple of how to build a plugin. It extends
class PetriNet
adding a method hello
that says hello displaying
the name of the net.
Loading plugins
The first example shows how to load a plugin: we load
snakes.plugins.hello
and plug it into snakes.nets
, which results
in a new module that is actually snakes.nets
extended by
snakes.plugins.hello
.
>>> import snakes.plugins as plugins
>>> hello_nets = plugins.load('hello', 'snakes.nets')
>>> n = hello_nets.PetriNet('N')
>>> n.hello()
Hello from N
>>> n = hello_nets.PetriNet('N', hello='Hi, this is %s!')
>>> n.hello()
Hi, this is N!
The next example shows how to simulate the effect of import module
:
we give to load
a thrid argument that is the name of the created
module, from which it becomes possible to import names or *
.
>>> plugins.load('hello', 'snakes.nets', 'another_version')
<module ...>
>>> from another_version import PetriNet
>>> n = PetriNet('another net')
>>> n.hello()
Hello from another net
>>> n = PetriNet('yet another net', hello='Hi, this is %s!')
>>> n.hello()
Hi, this is yet another net!
The last example shows how to load several plugins at once, instead of giving one plugin name, we just need to give a list of plugin names.
>>> plugins.load(['hello', 'pos'], 'snakes.nets', 'mynets')
<module ...>
>>> from mynets import PetriNet
>>> n = PetriNet('a net')
>>> n.hello() # works thanks to plugin `hello`
Hello from a net
>>> n.bbox() # works thanks to plugin `pos`
((0, 0), (0, 0))
Function load
def load (plugins, base, name=None) :
Load plugins, plugins
can be a single plugin name, a module
or a list of such values. If name
is not None
, the extended
module is loaded as name
in sys.modules
as well as in the
global environment from which load
was called.
Call API
object plugins
: the module that implements the plugin, or its name, or a collection (eg, list, tuple) of such valuesobject base
: the module being extended or its namestr name
: the name of the created modulereturn module
: the extended module
Creating plugins
We show now how to develop a plugin that allows instances of
PetriNet
to say hello: a new method PetriNet.hello
is added and
the constructor PetriNet.__init__
is added a keyword argument
hello
for the message to print when calling method hello
.
Defining a plugins required to write a module with a function called
extend
that takes as its single argument the module to be extended.
Inside this function, extensions of the classes in the module are
defined as normal sub-classes. Function extend
returns the extended
classes. A decorator called plugin
must be used, it also allows to
resolve plugin dependencies and conflicts.
"""An example plugin that allows instances of `PetriNet` to
say hello. The source code can be used as a starting
example."""
import snakes.plugins
@snakes.plugins.plugin("snakes.nets")
def extend (module) :
"Extends `module`"
class PetriNet (module.PetriNet) :
"Extension of the class `PetriNet` in `module`"
def __init__ (self, name, **args) :
"""Add new keyword argument `hello`
>>> PetriNet('N').hello()
Hello from N
>>> PetriNet('N', hello='Hi! This is %s...').hello()
Hi! This is N...
@param args: plugin options
@keyword hello: the message to print, with
`%s` where the net name should appear.
@type hello: `str`
"""
self._hello = args.pop("hello", "Hello from %s")
module.PetriNet.__init__(self, name, **args)
def hello (self) :
"Ask the net to say hello"
print(self._hello % self.name)
return PetriNet
Note that, when extending an existing method like __init__
above,
we have to take care that you may be working on an already extended
class, consequently, we cannot know how its arguments have been
changed already. So, we must always use those from the unextended
method plus **args
. Then, we remove from the latter what your plugin
needs and pass the remaining to the method of the base class if we
need to call it (which is usually the case).
Function plugin
def plugin (base, depends=[], conflicts=[]) :
Decorator for extension functions
Call API
str base
: name of base module (usually 'snakes.nets') that the plugin extendslist depends
: list of plugin names (asstr
) this one depends on, prefixsnakes.plugins.
may be omittedlist conflicts
: list of plugin names with which this one conflictsreturn decorator
: the appropriate decorator