cleanup ltsmin bindings
* python/spot/aux.py (rm_f): new function. * python/spot/ltsmin.i: Replace the %require magic by a simple function. Rewrite the %%dve magic. * tests/python/otfcrash.py: Simplify using spot.ltsmin.require() * tests/python/ltsmin.ipynb: Likewise, also add more text for the documentation. * NEWS: Adjust.
This commit is contained in:
parent
9b5a763538
commit
9692d734a9
5 changed files with 559 additions and 611 deletions
8
NEWS
8
NEWS
|
|
@ -104,11 +104,9 @@ New in spot 1.99.7a (not yet released)
|
||||||
|
|
||||||
Python:
|
Python:
|
||||||
|
|
||||||
* The ltsmin interface has been binded in Python. See
|
* The ltsmin interface has been binded in Python. It also
|
||||||
https://spot.lrde.epita.fr/ipynb/ltsmin.html for a short example.
|
comes with a %%dve cell magic to edit DiVinE models in the notebook.
|
||||||
|
See https://spot.lrde.epita.fr/ipynb/ltsmin.html for a short example.
|
||||||
* The ltsmin interface support two extra 'magic commands' when ipython
|
|
||||||
is used: %dve and %%require.
|
|
||||||
|
|
||||||
* spot.setup() sets de maximum number of states to display in
|
* spot.setup() sets de maximum number of states to display in
|
||||||
automata to 50 by default, as more states is likely to be
|
automata to 50 by default, as more states is likely to be
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ Auxiliary functions for Spot's Python bindings
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
|
import errno
|
||||||
|
|
||||||
def extend(*classes):
|
def extend(*classes):
|
||||||
"""
|
"""
|
||||||
|
|
@ -68,3 +69,14 @@ def ostream_to_svg(ostr):
|
||||||
Encode an ostringstream as utf-8 and send it to dot for cocnversion to SVG.
|
Encode an ostringstream as utf-8 and send it to dot for cocnversion to SVG.
|
||||||
"""
|
"""
|
||||||
return str_to_svg(ostr.str().encode('utf-8'))
|
return str_to_svg(ostr.str().encode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
|
def rm_f(filename):
|
||||||
|
"""
|
||||||
|
Remove filename if it exists.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
os.remove(filename)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,9 @@ namespace std {
|
||||||
|
|
||||||
%pythoncode %{
|
%pythoncode %{
|
||||||
import spot
|
import spot
|
||||||
|
import spot.aux
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
def load(filename):
|
def load(filename):
|
||||||
return model.load(filename)
|
return model.load(filename)
|
||||||
|
|
@ -107,75 +110,65 @@ class model:
|
||||||
res += '\n';
|
res += '\n';
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def require(tool):
|
||||||
|
"""
|
||||||
|
Exit with status code 77 if the required tool is not installed.
|
||||||
|
|
||||||
|
This function is mostly useful in Spot test suite, where 77 is a
|
||||||
|
code used to indicate that some test should be skipped.
|
||||||
|
"""
|
||||||
|
if tool != "divine":
|
||||||
|
raise ValueError("unsupported argument for require(): " + tool)
|
||||||
|
import shutil
|
||||||
|
if shutil.which("divine") == None:
|
||||||
|
print ("divine not available", file=sys.stderr)
|
||||||
|
sys.exit(77)
|
||||||
|
out = subprocess.check_output(['divine', 'compile',
|
||||||
|
'--help'], stderr=subprocess.STDOUT)
|
||||||
|
if b'LTSmin' not in out:
|
||||||
|
print ("divine available but no support for LTSmin",
|
||||||
|
file=sys.stderr)
|
||||||
|
sys.exit(77)
|
||||||
|
|
||||||
|
|
||||||
# Load IPython specific support if we can.
|
# Load IPython specific support if we can.
|
||||||
try:
|
try:
|
||||||
# Load only if we are running IPython.
|
# Load only if we are running IPython.
|
||||||
__IPYTHON__
|
__IPYTHON__
|
||||||
|
|
||||||
from IPython.core.magic import (Magics, magics_class, line_cell_magic)
|
from IPython.core.magic import Magics, magics_class, cell_magic
|
||||||
from IPython.core.magic_arguments \
|
|
||||||
import (argument, magic_arguments, parse_argstring)
|
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import sys
|
|
||||||
import shutil
|
|
||||||
try:
|
|
||||||
import ipywidgets as widgets
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# This class provides support for %%dve model description
|
|
||||||
@magics_class
|
@magics_class
|
||||||
class EditDVE(Magics):
|
class EditDVE(Magics):
|
||||||
|
|
||||||
@line_cell_magic
|
@cell_magic
|
||||||
def dve(self, line, cell=None):
|
def dve(self, line, cell):
|
||||||
try:
|
if not line:
|
||||||
# DiViNe prefers when files are in the current directory
|
raise ValueError("missing variable name for %%dve")
|
||||||
# so write cell into local file
|
# DiViNe prefers when files are in the current directory
|
||||||
t = tempfile.NamedTemporaryFile(dir=os.getcwd())
|
# so write cell into local file
|
||||||
filename = t.name + '.dve'
|
with tempfile.NamedTemporaryFile(dir='.', suffix='.dve') as t:
|
||||||
f = open(filename,"w")
|
t.write(cell.encode('utf-8'))
|
||||||
f.write(cell)
|
t.flush()
|
||||||
f.close()
|
|
||||||
|
|
||||||
# Then compile and unlink temporary files
|
|
||||||
import subprocess
|
|
||||||
out=""
|
|
||||||
self.shell.user_ns[line] = None
|
|
||||||
out = subprocess.check_call(['divine', 'compile',
|
|
||||||
'--ltsmin', filename])
|
|
||||||
os.unlink(filename)
|
|
||||||
os.unlink(filename + '.cpp')
|
|
||||||
self.shell.user_ns[line] = load(filename + '2C')
|
|
||||||
os.unlink(filename + '2C')
|
|
||||||
except Exception as error:
|
|
||||||
try:
|
try:
|
||||||
os.unlink(filename)
|
p = subprocess.Popen(['divine', 'compile',
|
||||||
os.unlink(filename + '.cpp')
|
'--ltsmin', t.name],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
universal_newlines=True)
|
||||||
|
out = p.communicate()
|
||||||
|
if out[0]:
|
||||||
|
print(out[0], file=sys.stderr)
|
||||||
|
ret = p.wait()
|
||||||
|
if ret:
|
||||||
|
raise subprocess.CalledProcessError(ret, 'divine')
|
||||||
|
self.shell.user_ns[line] = load(t.name + '2C')
|
||||||
finally:
|
finally:
|
||||||
if out != "":
|
spot.aux.rm_f(t.name + '.cpp')
|
||||||
raise RuntimeError(out) from error
|
spot.aux.rm_f(t.name + '2C')
|
||||||
raise error
|
|
||||||
|
|
||||||
@line_cell_magic
|
|
||||||
def require(self, line, cell=None):
|
|
||||||
if line != "divine":
|
|
||||||
print ("Unknown" + line, file=sys.stderr)
|
|
||||||
sys.exit(77)
|
|
||||||
if cell != None:
|
|
||||||
print ("No support for Cell magic command")
|
|
||||||
sys.exit(77)
|
|
||||||
if shutil.which("divine") == None:
|
|
||||||
print ("divine not available", file=sys.stderr)
|
|
||||||
sys.exit(77)
|
|
||||||
import subprocess
|
|
||||||
out = subprocess.check_output(['divine', 'compile',
|
|
||||||
'--help'], stderr=subprocess.STDOUT)
|
|
||||||
if b'LTSmin' not in out:
|
|
||||||
print ("divine available but no support for LTSmin",
|
|
||||||
file=sys.stderr)
|
|
||||||
sys.exit(77)
|
|
||||||
|
|
||||||
ip = get_ipython()
|
ip = get_ipython()
|
||||||
ip.register_magics(EditDVE)
|
ip.register_magics(EditDVE)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -4,14 +4,7 @@ import tempfile
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# this test requires divine with --ltsmin support
|
spot.ltsmin.require('divine')
|
||||||
if shutil.which("divine") == None:
|
|
||||||
sys.exit(77)
|
|
||||||
import subprocess
|
|
||||||
out = subprocess.check_output(['divine', 'compile',
|
|
||||||
'--help'], stderr=subprocess.STDOUT)
|
|
||||||
if b'LTSmin' not in out:
|
|
||||||
sys.exit(77)
|
|
||||||
|
|
||||||
# the test case actually starts here
|
# the test case actually starts here
|
||||||
with tempfile.NamedTemporaryFile(dir='.', suffix='.dve') as fp:
|
with tempfile.NamedTemporaryFile(dir='.', suffix='.dve') as fp:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue