Commit 67468acb authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

* wrap/python/spot.py: Minor cleanups to follow PEP8.

parent 24a8affb
...@@ -22,6 +22,7 @@ import subprocess ...@@ -22,6 +22,7 @@ import subprocess
import sys import sys
from functools import lru_cache from functools import lru_cache
def setup(**kwargs): def setup(**kwargs):
"""Configure Spot for fancy display. """Configure Spot for fancy display.
...@@ -43,7 +44,8 @@ def setup(**kwargs): ...@@ -43,7 +44,8 @@ def setup(**kwargs):
""" """
import os import os
s = 'size="{}" node[style=filled,fillcolor="{}"] edge[arrowhead=vee, arrowsize=.7]' s = ('size="{}" node[style=filled,fillcolor="{}"] '
'edge[arrowhead=vee, arrowsize=.7]')
os.environ['SPOT_DOTEXTRA'] = s.format(kwargs.get('size', '10.2,5'), os.environ['SPOT_DOTEXTRA'] = s.format(kwargs.get('size', '10.2,5'),
kwargs.get('fillcolor', '#ffffaa')) kwargs.get('fillcolor', '#ffffaa'))
...@@ -55,20 +57,21 @@ def setup(**kwargs): ...@@ -55,20 +57,21 @@ def setup(**kwargs):
# namespace without prefixing them. Latter versions fix this. So we # namespace without prefixing them. Latter versions fix this. So we
# can remove for following hack once 3.0.2 is no longer used in our # can remove for following hack once 3.0.2 is no longer used in our
# build farm. # build farm.
if not 'op_ff' in globals(): if 'op_ff' not in globals():
for i in ('ff', 'tt', 'eword', 'ap', 'Not', 'X', 'F', 'G', for i in ('ff', 'tt', 'eword', 'ap', 'Not', 'X', 'F', 'G',
'Closure', 'NegClosure', 'NegClosureMarked', 'Closure', 'NegClosure', 'NegClosureMarked',
'Xor', 'Implies', 'Equiv', 'U', 'R', 'W', 'M', 'Xor', 'Implies', 'Equiv', 'U', 'R', 'W', 'M',
'EConcat', 'EConcatMarked', 'UConcat', 'Or', 'EConcat', 'EConcatMarked', 'UConcat', 'Or',
'OrRat', 'And', 'AndRat', 'AndNLM', 'Concat', 'OrRat', 'And', 'AndRat', 'AndNLM', 'Concat',
'Fusion', 'Star', 'FStar'): 'Fusion', 'Star', 'FStar'):
globals()['op_' + i] = globals()[i]; globals()['op_' + i] = globals()[i]
del globals()[i] del globals()[i]
# Global BDD dict so that we do not have to create one in user code. # Global BDD dict so that we do not have to create one in user code.
_bdd_dict = make_bdd_dict() _bdd_dict = make_bdd_dict()
# Add a small LRU cache so that when we display automata into a # Add a small LRU cache so that when we display automata into a
# interactive widget, we avoid some repeated calls to dot for # interactive widget, we avoid some repeated calls to dot for
# identical inputs. # identical inputs.
...@@ -81,17 +84,21 @@ def _str_to_svg(str): ...@@ -81,17 +84,21 @@ def _str_to_svg(str):
res = dotty.communicate() res = dotty.communicate()
return res[0].decode('utf-8') return res[0].decode('utf-8')
def _ostream_to_svg(ostr): def _ostream_to_svg(ostr):
return _str_to_svg(ostr.str().encode('utf-8')) return _str_to_svg(ostr.str().encode('utf-8'))
def _render_automaton_as_svg(a, opt=None): def _render_automaton_as_svg(a, opt=None):
ostr = ostringstream() ostr = ostringstream()
print_dot(ostr, a, opt) print_dot(ostr, a, opt)
return _ostream_to_svg(ostr) return _ostream_to_svg(ostr)
twa._repr_svg_ = _render_automaton_as_svg twa._repr_svg_ = _render_automaton_as_svg
ta._repr_svg_ = _render_automaton_as_svg ta._repr_svg_ = _render_automaton_as_svg
def _render_formula_as_svg(a): def _render_formula_as_svg(a):
# Load the SVG function only if we need it. This way the bindings # Load the SVG function only if we need it. This way the bindings
# can still be used outside of IPython if IPython is not # can still be used outside of IPython if IPython is not
...@@ -101,19 +108,24 @@ def _render_formula_as_svg(a): ...@@ -101,19 +108,24 @@ def _render_formula_as_svg(a):
print_dot_psl(ostr, a) print_dot_psl(ostr, a)
return SVG(_ostream_to_svg(ostr)) return SVG(_ostream_to_svg(ostr))
def _return_automaton_as_svg(a, opt=None): def _return_automaton_as_svg(a, opt=None):
# Load the SVG function only if we need it. This way the bindings # Load the SVG function only if we need it. This way the bindings
# can still be used outside of IPython if IPython is not # can still be used outside of IPython if IPython is not
# installed. # installed.
from IPython.display import SVG from IPython.display import SVG
return SVG(_render_automaton_as_svg(a, opt)) return SVG(_render_automaton_as_svg(a, opt))
twa.show = _return_automaton_as_svg twa.show = _return_automaton_as_svg
ta.show = _return_automaton_as_svg ta.show = _return_automaton_as_svg
def _formula_str_ctor(self, str): def _formula_str_ctor(self, str):
self.this = parse_formula(str) self.this = parse_formula(str)
def _formula_to_str(self, format = 'spot', parenth = False):
def _formula_to_str(self, format='spot', parenth=False):
if format == 'spot' or format == 'f': if format == 'spot' or format == 'f':
return str_psl(self, parenth) return str_psl(self, parenth)
elif format == 'spin' or format == 's': elif format == 'spin' or format == 's':
...@@ -131,6 +143,7 @@ def _formula_to_str(self, format = 'spot', parenth = False): ...@@ -131,6 +143,7 @@ def _formula_to_str(self, format = 'spot', parenth = False):
else: else:
raise ValueError("unknown string format: " + format) raise ValueError("unknown string format: " + format)
def _formula_format(self, spec): def _formula_format(self, spec):
"""Format the formula according to spec. """Format the formula according to spec.
...@@ -200,16 +213,18 @@ def _formula_format(self, spec): ...@@ -200,16 +213,18 @@ def _formula_format(self, spec):
return s.__format__(spec) return s.__format__(spec)
def _formula_traverse(self, func): def _formula_traverse(self, func):
if func(self): if func(self):
return return
for f in self: for f in self:
f.traverse(func) f.traverse(func)
def _formula_map(self, func): def _formula_map(self, func):
k = self.kind() k = self.kind()
if k in (op_ff, op_tt, op_eword, op_ap): if k in (op_ff, op_tt, op_eword, op_ap):
return self; return self
if k in (op_Not, op_X, op_F, op_G, op_Closure, if k in (op_Not, op_X, op_F, op_G, op_Closure,
op_NegClosure, op_NegClosureMarked): op_NegClosure, op_NegClosureMarked):
return formula.unop(k, func(self[0])) return formula.unop(k, func(self[0]))
...@@ -223,6 +238,7 @@ def _formula_map(self, func): ...@@ -223,6 +238,7 @@ def _formula_map(self, func):
return formula.bunop(k, func(self[0]), self.min(), self.max()) return formula.bunop(k, func(self[0]), self.min(), self.max())
raise ValueError("unknown type of formula") raise ValueError("unknown type of formula")
formula.__init__ = _formula_str_ctor formula.__init__ = _formula_str_ctor
formula.to_str = _formula_to_str formula.to_str = _formula_to_str
formula.show_ast = _render_formula_as_svg formula.show_ast = _render_formula_as_svg
...@@ -230,6 +246,7 @@ formula.traverse = _formula_traverse ...@@ -230,6 +246,7 @@ formula.traverse = _formula_traverse
formula.__format__ = _formula_format formula.__format__ = _formula_format
formula.map = _formula_map formula.map = _formula_map
def _twa_to_str(a, format='hoa', opt=None): def _twa_to_str(a, format='hoa', opt=None):
format = format.lower() format = format.lower()
if format == 'hoa': if format == 'hoa':
...@@ -250,6 +267,7 @@ def _twa_to_str(a, format='hoa', opt=None): ...@@ -250,6 +267,7 @@ def _twa_to_str(a, format='hoa', opt=None):
return ostr.str() return ostr.str()
raise ValueError("unknown string format: " + format) raise ValueError("unknown string format: " + format)
def _twa_save(a, filename, format='hoa', opt=None, append=False): def _twa_save(a, filename, format='hoa', opt=None, append=False):
with open(filename, 'a' if append else 'w') as f: with open(filename, 'a' if append else 'w') as f:
s = a.to_str(format, opt) s = a.to_str(format, opt)
...@@ -258,17 +276,21 @@ def _twa_save(a, filename, format='hoa', opt=None, append=False): ...@@ -258,17 +276,21 @@ def _twa_save(a, filename, format='hoa', opt=None, append=False):
f.write('\n') f.write('\n')
return a return a
twa.to_str = _twa_to_str twa.to_str = _twa_to_str
twa.save = _twa_save twa.save = _twa_save
def automata(*filenames): def automata(*filenames):
"""Read automata from a list of filenames. """Read automata from a list of filenames.
The automata can be written in the The automata can be written in the
[HOA format](http://adl.github.io/hoaf/), as [HOA format](http://adl.github.io/hoaf/), as
[never claims](http://spinroot.com/spin/Man/never.html), [never claims](http://spinroot.com/spin/Man/never.html),
or in [LBTT's format] in [LBTT's format]
(http://www.tcs.hut.fi/Software/lbtt/doc/html/Format-for-automata.html). (http://www.tcs.hut.fi/Software/lbtt/doc/html/Format-for-automata.html),
or in [ltl2dstar's
format](http://www.ltl2dstar.de/docs/ltl2dstar.html#output-format-dstar)
If an argument ends with a `|`, then this argument is interpreted as If an argument ends with a `|`, then this argument is interpreted as
a shell command, and the output of that command (without the `|`) a shell command, and the output of that command (without the `|`)
...@@ -304,7 +326,7 @@ def automata(*filenames): ...@@ -304,7 +326,7 @@ def automata(*filenames):
# Make sure we destroy the parser (p) and the subprocess # Make sure we destroy the parser (p) and the subprocess
# (prop) in the correct order... # (prop) in the correct order...
del p del p
if proc != None: if proc is not None:
if not a: if not a:
# We reached the end of the stream. Wait for the # We reached the end of the stream. Wait for the
# process to finish, so that we can its exit code. # process to finish, so that we can its exit code.
...@@ -321,6 +343,7 @@ def automata(*filenames): ...@@ -321,6 +343,7 @@ def automata(*filenames):
.format(filename[:-1], ret)) .format(filename[:-1], ret))
return return
def automaton(filename): def automaton(filename):
"""Read a single automaton from a file. """Read a single automaton from a file.
...@@ -330,6 +353,7 @@ def automaton(filename): ...@@ -330,6 +353,7 @@ def automaton(filename):
except StopIteration: except StopIteration:
raise RuntimeError("Failed to read automaton from {}".format(filename)) raise RuntimeError("Failed to read automaton from {}".format(filename))
def translate(formula, *args): def translate(formula, *args):
"""Translate a formula into an automaton. """Translate a formula into an automaton.
...@@ -358,7 +382,7 @@ def translate(formula, *args): ...@@ -358,7 +382,7 @@ def translate(formula, *args):
def type_set(val): def type_set(val):
nonlocal type_ nonlocal type_
if type_ != None and type_ != val: if type_ is not None and type_ != val:
raise ValueError("type cannot be both {} and {}" raise ValueError("type cannot be both {} and {}"
.format(type_, val)) .format(type_, val))
elif val == 'tgba': elif val == 'tgba':
...@@ -371,7 +395,7 @@ def translate(formula, *args): ...@@ -371,7 +395,7 @@ def translate(formula, *args):
def pref_set(val): def pref_set(val):
nonlocal pref_ nonlocal pref_
if pref_ != None and pref_ != val: if pref_ is not None and pref_ != val:
raise ValueError("preference cannot be both {} and {}" raise ValueError("preference cannot be both {} and {}"
.format(pref_, val)) .format(pref_, val))
elif val == 'small': elif val == 'small':
...@@ -384,7 +408,7 @@ def translate(formula, *args): ...@@ -384,7 +408,7 @@ def translate(formula, *args):
def optm_set(val): def optm_set(val):
nonlocal optm_ nonlocal optm_
if optm_ != None and optm_ != val: if optm_ is not None and optm_ != val:
raise ValueError("optimization level cannot be both {} and {}" raise ValueError("optimization level cannot be both {} and {}"
.format(optm_, val)) .format(optm_, val))
if val == 'high': if val == 'high':
...@@ -444,17 +468,16 @@ def translate(formula, *args): ...@@ -444,17 +468,16 @@ def translate(formula, *args):
raise ValueError("ambiguous option '{}' is prefix of {}" raise ValueError("ambiguous option '{}' is prefix of {}"
.format(arg, str(compat))) .format(arg, str(compat)))
if type_ == None: if type_ is None:
type_ = postprocessor.TGBA type_ = postprocessor.TGBA
if pref_ == None: if pref_ is None:
pref_ = postprocessor.Small pref_ = postprocessor.Small
if optm_ == None: if optm_ is None:
optm_ = postprocessor.High optm_ = postprocessor.High
if type(formula) == str: if type(formula) == str:
formula = parse_formula(formula) formula = parse_formula(formula)
a = translator(_bdd_dict) a = translator(_bdd_dict)
a.set_type(type_) a.set_type(type_)
a.set_pref(pref_ | comp_ | unam_ | sbac_) a.set_pref(pref_ | comp_ | unam_ | sbac_)
...@@ -462,8 +485,10 @@ def translate(formula, *args): ...@@ -462,8 +485,10 @@ def translate(formula, *args):
return a.run(formula) return a.run(formula)
formula.translate = translate formula.translate = translate
# Wrapper around a formula iterator to which we add some methods of formula # Wrapper around a formula iterator to which we add some methods of formula
# (using _addfilter and _addmap), so that we can write things like # (using _addfilter and _addmap), so that we can write things like
# formulas.simplify().is_X_free(). # formulas.simplify().is_X_free().
...@@ -477,6 +502,7 @@ class formulaiterator: ...@@ -477,6 +502,7 @@ class formulaiterator:
def __next__(self): def __next__(self):
return next(self._formulas) return next(self._formulas)
# fun shoud be a predicate and should be a method of formula. # fun shoud be a predicate and should be a method of formula.
# _addfilter adds this predicate as a filter whith the same name in # _addfilter adds this predicate as a filter whith the same name in
# formulaiterator. # formulaiterator.
...@@ -484,9 +510,11 @@ def _addfilter(fun): ...@@ -484,9 +510,11 @@ def _addfilter(fun):
def filtf(self, *args, **kwargs): def filtf(self, *args, **kwargs):
it = filter(lambda f: getattr(f, fun)(*args, **kwargs), self) it = filter(lambda f: getattr(f, fun)(*args, **kwargs), self)
return formulaiterator(it) return formulaiterator(it)
def nfiltf(self, *args, **kwargs): def nfiltf(self, *args, **kwargs):
it = filter(lambda f: not getattr(f, fun)(*args, **kwargs), self) it = filter(lambda f: not getattr(f, fun)(*args, **kwargs), self)
return formulaiterator(it) return formulaiterator(it)
if fun[:3] == 'is_': if fun[:3] == 'is_':
notfun = 'is_not_' + fun[3:] notfun = 'is_not_' + fun[3:]
elif fun[:4] == 'has_': elif fun[:4] == 'has_':
...@@ -496,18 +524,21 @@ def _addfilter(fun): ...@@ -496,18 +524,21 @@ def _addfilter(fun):
setattr(formulaiterator, fun, filtf) setattr(formulaiterator, fun, filtf)
setattr(formulaiterator, notfun, nfiltf) setattr(formulaiterator, notfun, nfiltf)
# fun should be a function taking a formula as its first parameter and returning
# a formula. # fun should be a function taking a formula as its first parameter and
# _addmap adds this function as a method of formula and formulaiterator. # returning a formula. _addmap adds this function as a method of
# formula and formulaiterator.
def _addmap(fun): def _addmap(fun):
def mapf(self, *args, **kwargs): def mapf(self, *args, **kwargs):
return formulaiterator(map(lambda f: getattr(f, fun)(*args, **kwargs), return formulaiterator(map(lambda f: getattr(f, fun)(*args, **kwargs),
self)) self))
setattr(formula, fun, setattr(formula, fun,
lambda self, *args, **kwargs: globals()[fun](self, *args, **kwargs)) lambda self, *args, **kwargs:
globals()[fun](self, *args, **kwargs))
setattr(formulaiterator, fun, mapf) setattr(formulaiterator, fun, mapf)
def randltl(ap, n = -1, **kwargs):
def randltl(ap, n=-1, **kwargs):
"""Generate random formulas. """Generate random formulas.
Returns a random formula iterator. Returns a random formula iterator.
...@@ -534,10 +565,10 @@ def randltl(ap, n = -1, **kwargs): ...@@ -534,10 +565,10 @@ def randltl(ap, n = -1, **kwargs):
""" """
opts = option_map() opts = option_map()
output_map = { output_map = {
"ltl" : OUTPUTLTL, "ltl": OUTPUTLTL,
"psl" : OUTPUTPSL, "psl": OUTPUTPSL,
"bool" : OUTPUTBOOL, "bool": OUTPUTBOOL,
"sere" : OUTPUTSERE "sere": OUTPUTSERE
} }
if isinstance(ap, list): if isinstance(ap, list):
...@@ -567,33 +598,33 @@ def randltl(ap, n = -1, **kwargs): ...@@ -567,33 +598,33 @@ def randltl(ap, n = -1, **kwargs):
opts.set("simplification_level", simpl_level) opts.set("simplification_level", simpl_level)
rg = randltlgenerator(ap, opts, ltl_priorities, sere_priorities, rg = randltlgenerator(ap, opts, ltl_priorities, sere_priorities,
boolean_priorities) boolean_priorities)
dump_priorities = kwargs.get("dump_priorities", False) dump_priorities = kwargs.get("dump_priorities", False)
if dump_priorities: if dump_priorities:
dumpstream = ostringstream() dumpstream = ostringstream()
if output == OUTPUTLTL: if output == OUTPUTLTL:
print('Use argument ltl_priorities=STRING to set the following ' \ print('Use argument ltl_priorities=STRING to set the following '
'LTL priorities:\n') 'LTL priorities:\n')
rg.dump_ltl_priorities(dumpstream) rg.dump_ltl_priorities(dumpstream)
print(dumpstream.str()) print(dumpstream.str())
elif output == OUTPUTBOOL: elif output == OUTPUTBOOL:
print('Use argument boolean_priorities=STRING to set the ' \ print('Use argument boolean_priorities=STRING to set the '
'following Boolean formula priorities:\n') 'following Boolean formula priorities:\n')
rg.dump_bool_priorities(dumpstream) rg.dump_bool_priorities(dumpstream)
print(dumpstream.str()) print(dumpstream.str())
elif output == OUTPUTPSL or output == OUTPUTSERE: elif output == OUTPUTPSL or output == OUTPUTSERE:
if output != OUTPUTSERE: if output != OUTPUTSERE:
print('Use argument ltl_priorities=STRING to set the following ' \ print('Use argument ltl_priorities=STRING to set the '
'LTL priorities:\n') 'following LTL priorities:\n')
rg.dump_psl_priorities(dumpstream) rg.dump_psl_priorities(dumpstream)
print(dumpstream.str()) print(dumpstream.str())
print('Use argument sere_priorities=STRING to set the following ' \ print('Use argument sere_priorities=STRING to set the '
'SERE priorities:\n') 'following SERE priorities:\n')
rg.dump_sere_priorities(dumpstream) rg.dump_sere_priorities(dumpstream)
print(dumpstream.str()) print(dumpstream.str())
print('Use argument boolean_priorities=STRING to set the ' \ print('Use argument boolean_priorities=STRING to set the '
'following Boolean formula priorities:\n') 'following Boolean formula priorities:\n')
rg.dump_sere_bool_priorities(dumpstream) rg.dump_sere_bool_priorities(dumpstream)
print(dumpstream.str()) print(dumpstream.str())
else: else:
...@@ -605,8 +636,10 @@ def randltl(ap, n = -1, **kwargs): ...@@ -605,8 +636,10 @@ def randltl(ap, n = -1, **kwargs):
self.rg = rg self.rg = rg
self.i = 0 self.i = 0
self.n = n self.n = n
def __iter__(self): def __iter__(self):
return self return self
def __next__(self): def __next__(self):
if self.i == self.n: if self.i == self.n:
raise StopIteration raise StopIteration
...@@ -621,6 +654,7 @@ def randltl(ap, n = -1, **kwargs): ...@@ -621,6 +654,7 @@ def randltl(ap, n = -1, **kwargs):
return formulaiterator(_randltliterator(rg, n)) return formulaiterator(_randltliterator(rg, n))
def simplify(f, **kwargs): def simplify(f, **kwargs):
level = kwargs.get('level', None) level = kwargs.get('level', None)
if level is not None: if level is not None:
...@@ -629,8 +663,8 @@ def simplify(f, **kwargs): ...@@ -629,8 +663,8 @@ def simplify(f, **kwargs):
basics = kwargs.get('basics', True) basics = kwargs.get('basics', True)
synt_impl = kwargs.get('synt_impl', True) synt_impl = kwargs.get('synt_impl', True)
event_univ = kwargs.get('event_univ', True) event_univ = kwargs.get('event_univ', True)
containment_checks = kwargs.get('containment_checks', False) cont_checks = kwargs.get('containment_checks', False)
containment_checks_stronger = kwargs.get('containment_checks_stronger', False) cont_checks_stronger = kwargs.get('containment_checks_stronger', False)
nenoform_stop_on_boolean = kwargs.get('nenoform_stop_on_boolean', False) nenoform_stop_on_boolean = kwargs.get('nenoform_stop_on_boolean', False)
reduce_size_strictly = kwargs.get('reduce_size_strictly', False) reduce_size_strictly = kwargs.get('reduce_size_strictly', False)
boolean_to_isop = kwargs.get('boolean_to_isop', False) boolean_to_isop = kwargs.get('boolean_to_isop', False)
...@@ -639,17 +673,18 @@ def simplify(f, **kwargs): ...@@ -639,17 +673,18 @@ def simplify(f, **kwargs):
simp_opts = ltl_simplifier_options(basics, simp_opts = ltl_simplifier_options(basics,
synt_impl, synt_impl,
event_univ, event_univ,
containment_checks, cont_checks,
containment_checks_stronger, cont_checks_stronger,
nenoform_stop_on_boolean, nenoform_stop_on_boolean,
reduce_size_strictly, reduce_size_strictly,
boolean_to_isop, boolean_to_isop,
favor_event_univ) favor_event_univ)
return ltl_simplifier(simp_opts).simplify(f) return ltl_simplifier(simp_opts).simplify(f)
for fun in dir(formula): for fun in dir(formula):
if (callable(getattr(formula, fun)) and if (callable(getattr(formula, fun)) and (fun[:3] == 'is_' or
(fun[:3] == 'is_' or fun[:4] == 'has_')): fun[:4] == 'has_')):
_addfilter(fun) _addfilter(fun)
for fun in ['remove_x', 'get_literal', 'relabel', 'relabel_bse', for fun in ['remove_x', 'get_literal', 'relabel', 'relabel_bse',
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment