"""
The :mod:`pynusmv.utils` module contains some secondary functions and classes
used by PyNuSMV internals.
"""
__all__ = ['PointerWrapper', 'fixpoint', 'update', 'StdioFile','writeonly',
'indexed']
from pynusmv_lower_interface.nusmv.utils import utils
from pynusmv.init import _register_wrapper
[docs]class PointerWrapper(object):
"""
Superclass wrapper for NuSMV pointers.
Every pointer to a NuSMV structure is wrapped in a PointerWrapper
or in a subclass of PointerWrapper.
Every subclass instance takes a pointer to a NuSMV structure as constructor
parameter.
It is the responsibility of PointerWrapper and its subclasses to free
the wrapped pointer. Some pointers have to be freed like `bdd_ptr`,
but other do not have to be freed since NuSMV takes care of this;
for example, `BddFrm_ptr` does not have to be freed.
To ensure that a pointer is freed only once, PyNuSMV ensures that
any pointer is wrapped by only one PointerWrapper (or subclass of it)
if the pointer have to be freed.
"""
def __init__(self, pointer, freeit=False):
"""
Create a new PointerWrapper.
:param pointer: the pointer to wrap
:param freeit: whether the pointer has to be freed when this wrapper
is destroyed
"""
self._ptr = pointer
self._freeit = freeit
_register_wrapper(self)
def _free(self):
"""
Every subclass must implement `_free` if there is something to free.
"""
pass
def __del__(self):
if self._freeit and self._ptr is not None:
self._free()
def __hash__(self):
"""
Makes this object hashable.
.. warning::
Beware it uses the pointer to implement the hashing function.
So it is IDENTITY dependent (in C) and not value dependant.
:return: an object that can serve as key to perform the lookup in a dict.
"""
return self._ptr.__hash__()
def __eq__(self, other):
"""
Equality test between two objects.
.. warning::
Beware it uses the pointer to implement the hashing function.
So it is IDENTITY dependent (in C) and not value dependant.
:return: True iff the two object are the same
"""
return self._ptr == other._ptr
class AttributeDict(dict):
"""
An `AttributeDict` is a dictionary for which elements can be accessed by
using their keys as attribute names.
"""
def __init__(self, *args, **kwargs):
super(AttributeDict, self).__init__(*args, **kwargs)
self.__dict__ = self
[docs]def fixpoint(funct, start):
"""
Return the fixpoint of `funct`, as a BDD, starting with `start` BDD.
:rtype: :class:`BDD <pynusmv.dd.BDD>`
.. note:: mu Z.f(Z) least fixpoint is implemented with
`fixpoint(funct, false)`.
nu Z.f(Z) greatest fixpoint is implemented with
`fixpoint(funct, true)`.
"""
old = start
new = funct(start)
while old != new:
old = new
new = funct(old)
return old
[docs]def update(old, new):
"""
Update `old` with `new`. `old` is assumed to have the `extend` or `update`
method, and `new` is assumed to be a good argument for the corresponding
method.
:param old: the data to update.
:param new: the date to update with.
"""
try:
old.extend(new)
except AttributeError:
old.update(new)
[docs]class StdioFile:
"""
Wrapper class that provides a context manager to access a FILE* whenever the
lower interface needs one. This makes for a more pythonic way to interact
with APIs that need a standard file handle without having to deal with the
low level open/close instructions. Example::
# opens an arbitrary file of your choice.
with StdioFile.for_name('my_output_file', 'w') as f:
lower_interface_do_something_smart(f)
This wrapper also gives you access to stdin, stdout and stderr which, are
never closed despite the fact that they may be used with a `with` statement::
# stdio is ALREADY open at this time
with StdioFile.stdout() as out:
lower_interface_do_something_smart(out)
# stdio is STILL open here
"""
def __init__(self, fname, mode):
"""
Creates a new instance that will open the file `fname` in the `mode`
mode.
:param fname: the name of the file to be opened (more info -> stdio.h)
:param mode: the mode in which to open the file (more info -> stdio.h)
"""
self._fname = fname
self._mode = mode
self._ptr = None
self._isspecial = False
@staticmethod
[docs] def for_name(fname=None, mode='w'):
"""
This function acts like a generic factory that either return a handle
for standard file if the name is specified or to stdin or stdout if the
name is not specified (it depends on the mode)
:return: a stdiofile for the given name or stdin/stdout if no name is
specified depending on the value of the mode
"""
if fname is None:
return StdioFile.stdin() if mode == "r" else StdioFile.stdout()
else:
return StdioFile(fname, mode)
@staticmethod
[docs] def stdin():
"""Standard input"""
ret = StdioFile("(standard input)", "r")
ret._ptr = utils.stdio_stdin()
ret._isspecial = True
return ret
@staticmethod
[docs] def stdout():
"""standard output"""
ret = StdioFile("(standard output)", "w")
ret._ptr = utils.stdio_stdout()
ret._isspecial = True
return ret
@staticmethod
[docs] def stderr():
"""standard error"""
ret = StdioFile("(standard error)", "w")
ret._ptr = utils.stdio_stderr()
ret._isspecial = True
return ret
def __enter__(self):
"""Opens the file"""
if not self._isspecial:
self._ptr = utils.stdio_fopen(self._fname, self._mode)
return self
def __exit__(self, exception_type, exception_value, trace):
"""Makes sure the file is closed"""
if self._ptr is not None and not self._isspecial:
utils.stdio_fclose(self._ptr)
self._ptr = None
@property
def handle(self):
""":return: a FILE* handle to the opened stream"""
return self._ptr
#===============================================================================
#====== Decorators =============================================================
#===============================================================================
[docs]class writeonly:
"""
writeonly provides a write only decorator for properties that do not
have a getter accessor. This makes for pythonic property-lik APIs where your
class defines should have defined a setter. Example::
class Dummy(PointerWrapper):
# .. code elided ...
@writeonly
def config_tweak(self, new_value_of_tweak):
lower_interface_set_tweak(self._ptr, new_value_of_tweak)
Can be used the following way::
d = Dummy()
# this is now perfectly OK
d.config_tweak = 42
# this will however fail since no getter was defined
d.config_tweak
"""
def __init__(self, fn):
"""Creates the decorator memeorizing the decorated func"""
self._fn = fn
self.__doc__ = fn.__doc__ # be nice with the user documentation
def __call__(self):
"""
Executes the decoration (takes care itself to apply the decorated fn
"""
return self
def __set__(self, obj, value):
"""
Executes the decorated setter function
"""
self._fn(obj, value)
def __get__(self, obj, _type):
"""
Makes sure this property is only accessed in write only mode
"""
msg = '{} is defined as a write only property'.format(self._fn.__name__)
raise AttributeError(msg)
[docs]class indexed:
"""
indexed provides a set of decorators that enable the use of 'pythonic' indexed
get/setters. These give you the possibility to automagically add syntax sugar
to the classes you write.
The easiest (and most flexible way) to get started with the indexed series
of decorator is to use `@indexed.property(<name>)`. But if you are after
something more limited, you might want to give a look to the other decorators
that are provided: namely, `@indexed.getter`, `@indexed.setter` and
`@indexed.deleter`.
"""
def __init__(self, target, fget=None, fset=None, fdel=None):
"""
Creates a new indexed callback instance configured to use `fget` as
indexed getter, `fset` as indexed setter and where args[0] is a reference
to the target object to which the get/set function are going to be
directed to.
:param target: the receiver object who will receive the indexed messages
translated with the getter, setter, deleter functions
:param fget: the 'getter' function (the one to call to __getitem__)
:param fset: the 'setter' function (the one to call to __setitem__)
:param fdel: the 'deleter' function(the one to call to __delitem__)
"""
self._targ = target
self._get = indexed.__prepare_fget(fget)
self._set = indexed.__prepare_fset(fset)
self._del = indexed.__prepare_fdel(fdel)
def __getitem__(self, key):
"""
performs the key based lookup using the configured getter
:param key: the key to use as 'lookup' key
:return: the output of target.getter(key)
"""
return self._get(self._targ, key)
def __setitem__(self, key, value):
"""
performs the key based assignment of value to the key-th variable
:param key: the key to use as 'lookup' key
:param value: the value to give to whatever corresponds to key in the
underlying structure.
:return: whatever target.setter(key, value) returns (more than likely,
it should be None)
"""
return self._set(self._targ, key, value)
def __delitem__(self, key):
"""
performs the key based suppression using the configured deleter function
:param key: the key to use as 'deletion' key
:return: whatever target.deleter(key) returns (more than likely,
it should be None)
"""
return self._del(self._targ, key)
@staticmethod
def __wrap(fget=None, fset=None, fdel=None, doc=None):
"""
wraps the given function into a runtime property that can be accessed
in an indexed way.
.. note::
This method is private and should not be used from the outside.
:param fget: the 'getter' function (the one to call to __getitem__)
:param fset: the 'setter' function (the one to call to __setitem__)
:param fdel: the 'deleter' function(the one to call to __delitem__)
:param doc: the docstring to set to the returned property
"""
wrap = lambda *args: indexed(args[0], fget=fget, fset=fset, fdel=fdel)
wrap.doc = doc
return property(wrap)
@staticmethod
def __prepare_fget(fn):
"""
Prepares the fget function from the given fn.
This function returns the raw code to be executed for fget. If the given
`fn` is an indexed instance (ie. because the getter has been decorated),
then it is undecorated and the _get function is returned. If the obtained
callable is a method (not a function), then its __fun__ attribute is
returned in order to avoid weird behaviors (case where the given self is
actually of the right type, albeit not the expected behavior)
.. note::
This method is private and should not be used from the outside.
:param fn: the getter 'function'
:return: the raw getter code
"""
return indexed.__prepare(fn._get if isinstance(fn, indexed) else fn)
@staticmethod
def __prepare_fset(fn):
"""
Prepares the fset function from the given fn.
This function returns the raw code to be executed for fset. If the given
`fn` is an indexed instance (ie. because the setter has been decorated),
then it is undecorated and the _set function is returned. If the obtained
callable is a method (not a function), then its __fun__ attribute is
returned in order to avoid weird behaviors (case where the given self is
actually of the right type, albeit not the expected behavior)
.. note::
This method is private and should not be used from the outside.
:param fn: the setter 'function'
:return: the raw setter code
"""
return indexed.__prepare(fn._set if isinstance(fn, indexed) else fn)
@staticmethod
def __prepare_fdel(fn):
"""
Prepares the fdel function from the given fn.
This function returns the raw code to be executed for fdel. If the given
`fn` is an indexed instance (ie. because the deleter has been decorated),
then it is undecorated and the _del function is returned. If the obtained
callable is a method (not a function), then its __fun__ attribute is
returned in order to avoid weird behaviors (case where the given self is
actually of the right type, albeit not the expected behavior)
.. note::
This method is private and should not be used from the outside.
:param fn: the deleter 'function'
:return: the raw deleter code
"""
return indexed.__prepare(fn._del if isinstance(fn, indexed) else fn)
@staticmethod
def __prepare(fn):
"""
Returns the raw code of the given `fn`.
If `fn` is a method (not a function), then its __fun__ attribute is
returned in order to avoid weird behaviors (case where the given self is
actually of the right type, albeit not the expected behavior) otherwise
`fn` is returned as is.
.. note:: This method is private and should not be used from the outside.
:param fn: the function to prepare. (fn is assumed not to be an indexed)
:return: the raw code of the given `fn`
"""
return fn.__func__ if hasattr(fn, '__func__') else fn
@staticmethod
[docs] def getter(fn):
"""
Wraps a function `fn` and turns it into pythonic indexed-like acessor.
:param fn: the function to use to perform the keyed-lookup
Example usage::
class GetterOnly:
# ... code elided ...
# using @indexed or @indexed.getter is perfectly equivalent although
# the use of @indexed.getter is considered slightly cleaner
@indexed.getter
def clause(self, index):
return lower_interface_get_clause_at(self._ptr, index)
# example of use:
g = GetterOnly()
g.clause[42] # returns the 42th clause
"""
return indexed.__wrap(fget=fn, doc=fn.__doc__)
@staticmethod
[docs] def setter(fn):
"""
wraps a function `fn` and turns it into pythonic indexed-like acessor.
:param fn: the function to use to perform the keyed-assignment
Example usage::
class SetterOnly:
# ... code elided ...
@indexed.setter
def clause(self, index, new_value):
lower_interface_set_clause_at(self._ptr, index, new_value)
# example of use:
s = GetterOnly()
s.clause[42] = another_clause # changes the value of the clause
"""
return indexed.__wrap(fset=fn, doc=fn.__doc__)
@staticmethod
[docs] def deleter(fn):
"""
wraps a function `fn` and turns it into pythonic indexed-like deleter.
:param fn: the function to use to perform the keyed-lookup
Example usage::
class DeleterOnly:
# ... code elided ...
@indexed.deleter
def clause(self, index, new_value):
return lower_interface_delete_clause_at(self._ptr, index)
# example of use:
d = DeleterOnly()
del d.clause[42] # 42th clause has been deleted
"""
return indexed.__wrap(fdel=fn, doc=fn.__doc__)
@staticmethod
[docs] def property(name, **kwargs):
"""
Wraps the constructor of the decorated class to add a virtual indexed
property called `name`
By **default**, the generated indexed getter, indexed setter and
indexed deleted are assumed to be called respectively:
- get_`name`
- set_`name`
- del_`name`
However, these names are not enforced and can be customized if you pass the
keywords fget=<the_name_of_your_getter_fn>, fset=<the_name_of_your_setter_fn>
and/or fdel=<the_name_of_your_deleter_fn>.
.. note:: The keyword parameters also let you provide a docstring for
the virtual property you define. To this end, simply use the
`doc` keyword.
.. warning:: The getter, setter and deleter functions MUST BE CALLABLE
objects ! This means, you MAY NOT decorate any of the
functions you intend to use in your virtual property with
any of the @property, @indexed.getter, @indexed.setter or
@indexed.deleter since you resulting property would simply
not work.
Simple example::
@indexed.property('smartlst')
class Cls:
def __init__(self):
self._lst = [4,5,6]
def get_smartlst(self, idx):
return self._lst[idx]
def set_smartlst(self, idx, v):
self._lst[idx] = v
def del_smartlst(self, idx):
del self._lst[idx]
# Usage:
c = Cls()
c.smartlst[1] # calls _get_smartlst and returns 5
c.smartlst[1]=42 # calls _set_smartlst and changes _slt to be [4,42,6]
del c.smartlst[1] # calls _del_smartlst and changes _slt to be [4, 6]
Example with custom property names::
@indexed.property('smartlst', fget='glst', fset='slst', fdel='dlst')
class Cls:
def __init__(self):
self._lst = [4,5,6]
def glst(self, idx):
return self._lst[idx]
def slst(self, idx, v):
self._lst[idx] = v
def dlst(self, idx):
del self._lst[idx]
# Usage:
c = Cls()
c.smartlst[1] # calls glst and returns 5
c.smartlst[1]=42 # calls slst and changes _slt to be [4,42,6]
del c.smartlst[1] # calls dlst and changes _slt to be [4, 6]
If you don't like to use the decorator 'magic' but still want to define
a virtual property with very little effort: you should then use the
indexed constructor itself as such::
class Dummy:
def __init__(self):
self.clause= indexed(self, fget=self.get_clause, fset=self.set_clause,fdel=self.del_clause)
@indexed.getter
def get_clause(self, clause_idx):
return lower_interface_get_clause_at(self._ptr, index)
@indexed.setter
def set_clause(self, clause_idx, value):
lower_interface_set_clause_at(self._ptr, index, new_value)
@indexed.deleter
def del_clause(self, clause_idx):
lower_interface_delete_clause_at(self._ptr, index)
# example of use:
d = Dummy()
d.clause[42] # returns the 42th clause
d.clause[42] = other_clause # updates the 42th clause
del d.clause[42] # drops the 42th clause
"""
def decorate_init(init, fget, fset, fdel):
"""
Internal function used to decorate the __init__ method of the decorated
class.
.. note:: You should may not use this function for yourself
:param init: the __init__ method to decorate
:param fget: the function to use as indexed getter in the virtual prop
:param fset: the function to use as indexed setter in the virtual prop
:param fdel: the function to use as indexed deleter in the virtual prop
"""
def decorated(self, *args, **kw2):
"""The function that will be used instead of `init`"""
init(self, *args, **kw2)
self.__dict__[name] = indexed(self, fget=fget, fset=fset, fdel=fdel)
# set the docstring of the virtual property
if 'doc' in kwargs:
self.__dict__[name].__doc__ = kwargs['doc']
else:
self.__dict__[name].__doc__ = "Virtual indexed property"
return decorated
def class_deco(cls):
"""
Function that actually decorates the given `cls` class.
:param cls: the class being decorated.
"""
_dict= cls.__dict__
fget = None
fset = None
fdel = None
# default settings
if 'get_'+name in _dict:
fget = _dict['get_'+name]
if 'set_'+name in _dict:
fset = _dict['set_'+name]
if 'del_'+name in _dict:
fdel = _dict['del_'+name]
# override default settings
if 'fget' in kwargs:
fget = cls.__dict__[kwargs['fget']]
if 'fset' in kwargs:
fset = cls.__dict__[kwargs['fset']]
if 'fdel' in kwargs:
fdel = cls.__dict__[kwargs['fdel']]
cls.__init__ = decorate_init(cls.__init__, fget, fset, fdel)
return cls
return class_deco