-
Notifications
You must be signed in to change notification settings - Fork 0
/
scan.py
executable file
·89 lines (77 loc) · 2.35 KB
/
scan.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/env python3
#
# This script scans Myokit models for equations involving a 'PrefixPlus', that
# is a unary plus operation, which are affected by the bug described at
# https://github.com/myokit/myokit/pull/1055
#
# To use:
#
# python scan.py my-model.mmt
#
# or
#
# python scan.py my-directory-with-models
#
import os
import myokit
def has_issue(expression):
""" Scan an expression for potential issues. """
if isinstance(expression, myokit.PrefixPlus):
if isinstance(expression[0], myokit.InfixExpression):
return True
if isinstance(expression, myokit.Power):
if isinstance(expression[0], myokit.Power):
return True
for op in expression:
if has_issue(op):
return True
return False
def scan_var_owner(owner, issues):
""" Scan a component or variable. """
for var in owner:
if has_issue(var.rhs()):
issues.append((f'Equation', var, var.rhs()))
if var.is_state() and has_issue(var.initial_value()):
issues.append((f'Initial value', var, var.initial_value()))
scan_var_owner(var, issues)
def scan_model(filename):
print(f'Checking model {filename}...', end='')
try:
model = myokit.load_model(filename)
except myokit.SectionNotFoundError:
print(' [no model section]')
except myokit.MyokitError as e:
print(' [error when reading model]')
print(str(e))
except IOError as e:
print(' [i/o error when reading model]')
print(str(e))
else:
issues = []
for component in model:
scan_var_owner(component, issues)
if issues:
print(' [potential issue detected]')
for where, var, expression in issues:
print(f'{where} for {var.qname()}: {expression.code()}')
else:
print(' [ok]')
def scan_dir(path):
for leaf in os.listdir(path):
full = os.path.join(path, leaf)
if leaf in ('.', '..'):
continue
if os.path.isdir(full):
scan_dir(full)
elif leaf.endswith('.mmt'):
scan_model(full)
if __name__ == '__main__':
import sys
if len(sys.argv) != 2:
print('Usage: ./scan.py path')
sys.exit(1)
path = sys.argv[1]
if os.path.isdir(path):
scan_dir(path)
else:
scan_model(path)