Skip to content

Commit 8a5b786

Browse files
committed
Merge branch 'define_OMCSessionCmd' into merge
2 parents 3653220 + 7c66a53 commit 8a5b786

4 files changed

Lines changed: 108 additions & 88 deletions

File tree

OMPython/ModelicaSystem.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
from dataclasses import dataclass
4747
from typing import Optional
4848

49-
from OMPython.OMCSession import OMCSessionBase, OMCSessionZMQ
49+
from OMPython.OMCSession import OMCSessionZMQ
5050

5151
# define logger using the current module name as ID
5252
logger = logging.getLogger(__name__)
@@ -117,7 +117,7 @@ def __init__(
117117
variableFilter: Optional[str] = None,
118118
customBuildDirectory: Optional[str | os.PathLike] = None,
119119
omhome: Optional[str] = None,
120-
session: Optional[OMCSessionBase] = None
120+
session: Optional[OMCSessionZMQ] = None
121121
):
122122
"""Initialize, load and build a model.
123123

OMPython/OMCSession.py

Lines changed: 80 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
Definition of an OMC session.
44
"""
55

6+
from __future__ import annotations
7+
68
__license__ = """
79
This file is part of OpenModelica.
810
@@ -33,7 +35,6 @@
3335
"""
3436

3537
import shutil
36-
import abc
3738
import getpass
3839
import logging
3940
import json
@@ -45,6 +46,7 @@
4546
import sys
4647
import tempfile
4748
import time
49+
from typing import Optional
4850
import uuid
4951
import pyparsing
5052
import zmq
@@ -79,53 +81,40 @@ class OMCSessionException(Exception):
7981
pass
8082

8183

82-
class OMCSessionBase(metaclass=abc.ABCMeta):
84+
class OMCSessionCmd:
8385

84-
def __init__(self, readonly=False):
86+
def __init__(self, session: OMCSessionZMQ, readonly: Optional[bool] = False):
87+
if not isinstance(session, OMCSessionZMQ):
88+
raise OMCSessionException("Invalid session definition!")
89+
self._session = session
8590
self._readonly = readonly
8691
self._omc_cache = {}
8792

88-
def execute(self, command):
89-
warnings.warn("This function is depreciated and will be removed in future versions; "
90-
"please use sendExpression() instead", DeprecationWarning, stacklevel=1)
93+
def sendExpression(self, command, parsed=True):
94+
return self._session.sendExpression(command=command, parsed=parsed)
9195

92-
return self.sendExpression(command, parsed=False)
96+
def _ask(self, question: str, opt: Optional[list[str]] = None, parsed: Optional[bool] = True):
9397

94-
@abc.abstractmethod
95-
def sendExpression(self, command, parsed=True):
96-
"""
97-
Sends an expression to the OpenModelica. The return type is parsed as if the
98-
expression was part of the typed OpenModelica API (see ModelicaBuiltin.mo).
99-
* Integer and Real are returned as Python numbers
100-
* Strings, enumerations, and typenames are returned as Python strings
101-
* Arrays, tuples, and MetaModelica lists are returned as tuples
102-
* Records are returned as dicts (the name of the record is lost)
103-
* Booleans are returned as True or False
104-
* NONE() is returned as None
105-
* SOME(value) is returned as value
106-
"""
107-
pass
98+
if opt is None:
99+
expression = question
100+
elif isinstance(opt, list):
101+
expression = f"{question}({','.join(opt)})"
102+
else:
103+
raise OMCSessionException(f"Invalid definition of options for {repr(question)}: {repr(opt)}")
108104

109-
def ask(self, question, opt=None, parsed=True):
110-
p = (question, opt, parsed)
105+
p = (expression, parsed)
111106

112107
if self._readonly and question != 'getErrorString':
113108
# can use cache if readonly
114109
if p in self._omc_cache:
115110
return self._omc_cache[p]
116111

117-
if opt:
118-
expression = f'{question}({opt})'
119-
else:
120-
expression = question
121-
122-
logger.debug('OMC ask: %s - parsed: %s', expression, parsed)
112+
logger.debug('OMC ask: %s (parsed=%s)', expression, parsed)
123113

124114
try:
125-
res = self.sendExpression(expression, parsed=parsed)
126-
except OMCSessionException:
127-
logger.error("OMC failed: %s, %s, parsed=%s", question, opt, parsed)
128-
raise
115+
res = self._session.sendExpression(expression, parsed=parsed)
116+
except OMCSessionException as ex:
117+
raise OMCSessionException("OMC _ask() failed: %s (parsed=%s)", expression, parsed) from ex
129118

130119
# save response
131120
self._omc_cache[p] = res
@@ -134,126 +123,133 @@ def ask(self, question, opt=None, parsed=True):
134123

135124
# TODO: Open Modelica Compiler API functions. Would be nice to generate these.
136125
def loadFile(self, filename):
137-
return self.ask('loadFile', f'"{filename}"')
126+
return self._ask(question='loadFile', opt=[f'"{filename}"'])
138127

139128
def loadModel(self, className):
140-
return self.ask('loadModel', className)
129+
return self._ask(question='loadModel', opt=[className])
141130

142131
def isModel(self, className):
143-
return self.ask('isModel', className)
132+
return self._ask(question='isModel', opt=[className])
144133

145134
def isPackage(self, className):
146-
return self.ask('isPackage', className)
135+
return self._ask(question='isPackage', opt=[className])
147136

148137
def isPrimitive(self, className):
149-
return self.ask('isPrimitive', className)
138+
return self._ask(question='isPrimitive', opt=[className])
150139

151140
def isConnector(self, className):
152-
return self.ask('isConnector', className)
141+
return self._ask(question='isConnector', opt=[className])
153142

154143
def isRecord(self, className):
155-
return self.ask('isRecord', className)
144+
return self._ask(question='isRecord', opt=[className])
156145

157146
def isBlock(self, className):
158-
return self.ask('isBlock', className)
147+
return self._ask(question='isBlock', opt=[className])
159148

160149
def isType(self, className):
161-
return self.ask('isType', className)
150+
return self._ask(question='isType', opt=[className])
162151

163152
def isFunction(self, className):
164-
return self.ask('isFunction', className)
153+
return self._ask(question='isFunction', opt=[className])
165154

166155
def isClass(self, className):
167-
return self.ask('isClass', className)
156+
return self._ask(question='isClass', opt=[className])
168157

169158
def isParameter(self, className):
170-
return self.ask('isParameter', className)
159+
return self._ask(question='isParameter', opt=[className])
171160

172161
def isConstant(self, className):
173-
return self.ask('isConstant', className)
162+
return self._ask(question='isConstant', opt=[className])
174163

175164
def isProtected(self, className):
176-
return self.ask('isProtected', className)
165+
return self._ask(question='isProtected', opt=[className])
177166

178167
def getPackages(self, className="AllLoadedClasses"):
179-
return self.ask('getPackages', className)
168+
return self._ask(question='getPackages', opt=[className])
180169

181170
def getClassRestriction(self, className):
182-
return self.ask('getClassRestriction', className)
171+
return self._ask(question='getClassRestriction', opt=[className])
183172

184173
def getDerivedClassModifierNames(self, className):
185-
return self.ask('getDerivedClassModifierNames', className)
174+
return self._ask(question='getDerivedClassModifierNames', opt=[className])
186175

187176
def getDerivedClassModifierValue(self, className, modifierName):
188-
return self.ask('getDerivedClassModifierValue', f'{className}, {modifierName}')
177+
return self._ask(question='getDerivedClassModifierValue', opt=[className, modifierName])
189178

190179
def typeNameStrings(self, className):
191-
return self.ask('typeNameStrings', className)
180+
return self._ask(question='typeNameStrings', opt=[className])
192181

193182
def getComponents(self, className):
194-
return self.ask('getComponents', className)
183+
return self._ask(question='getComponents', opt=[className])
195184

196185
def getClassComment(self, className):
197186
try:
198-
return self.ask('getClassComment', className)
187+
return self._ask(question='getClassComment', opt=[className])
199188
except pyparsing.ParseException as ex:
200-
logger.warning("Method 'getClassComment' failed for %s", className)
201-
logger.warning('OMTypedParser error: %s', ex.msg)
189+
logger.warning("Method 'getClassComment(%s)' failed; OMTypedParser error: %s",
190+
className, ex.msg)
202191
return 'No description available'
192+
except OMCSessionException:
193+
raise
203194

204195
def getNthComponent(self, className, comp_id):
205196
""" returns with (type, name, description) """
206-
return self.ask('getNthComponent', f'{className}, {comp_id}')
197+
return self._ask(question='getNthComponent', opt=[className, comp_id])
207198

208199
def getNthComponentAnnotation(self, className, comp_id):
209-
return self.ask('getNthComponentAnnotation', f'{className}, {comp_id}')
200+
return self._ask(question='getNthComponentAnnotation', opt=[className, comp_id])
210201

211202
def getImportCount(self, className):
212-
return self.ask('getImportCount', className)
203+
return self._ask(question='getImportCount', opt=[className])
213204

214205
def getNthImport(self, className, importNumber):
215206
# [Path, id, kind]
216-
return self.ask('getNthImport', f'{className}, {importNumber}')
207+
return self._ask(question='getNthImport', opt=[className, importNumber])
217208

218209
def getInheritanceCount(self, className):
219-
return self.ask('getInheritanceCount', className)
210+
return self._ask(question='getInheritanceCount', opt=[className])
220211

221212
def getNthInheritedClass(self, className, inheritanceDepth):
222-
return self.ask('getNthInheritedClass', f'{className}, {inheritanceDepth}')
213+
return self._ask(question='getNthInheritedClass', opt=[className, inheritanceDepth])
223214

224215
def getParameterNames(self, className):
225216
try:
226-
return self.ask('getParameterNames', className)
217+
return self._ask(question='getParameterNames', opt=[className])
227218
except KeyError as ex:
228219
logger.warning('OMPython error: %s', ex)
229220
# FIXME: OMC returns with a different structure for empty parameter set
230221
return []
222+
except OMCSessionException:
223+
raise
231224

232225
def getParameterValue(self, className, parameterName):
233226
try:
234-
return self.ask('getParameterValue', f'{className}, {parameterName}')
227+
return self._ask(question='getParameterValue', opt=[className, parameterName])
235228
except pyparsing.ParseException as ex:
236-
logger.warning('OMTypedParser error: %s', ex.msg)
229+
logger.warning("Method 'getParameterValue(%s, %s)' failed; OMTypedParser error: %s",
230+
className, parameterName, ex.msg)
237231
return ""
232+
except OMCSessionException:
233+
raise
238234

239235
def getComponentModifierNames(self, className, componentName):
240-
return self.ask('getComponentModifierNames', f'{className}, {componentName}')
236+
return self._ask(question='getComponentModifierNames', opt=[className, componentName])
241237

242238
def getComponentModifierValue(self, className, componentName):
243-
return self.ask(question='getComponentModifierValue', opt=f'{className}, {componentName}')
239+
return self._ask(question='getComponentModifierValue', opt=[className, componentName])
244240

245241
def getExtendsModifierNames(self, className, componentName):
246-
return self.ask('getExtendsModifierNames', f'{className}, {componentName}')
242+
return self._ask(question='getExtendsModifierNames', opt=[className, componentName])
247243

248244
def getExtendsModifierValue(self, className, extendsName, modifierName):
249-
return self.ask(question='getExtendsModifierValue', opt=f'{className}, {extendsName}, {modifierName}')
245+
return self._ask(question='getExtendsModifierValue', opt=[className, extendsName, modifierName])
250246

251247
def getNthComponentModification(self, className, comp_id):
252248
# FIXME: OMPython exception Results KeyError exception
253249

254250
# get {$Code(....)} field
255251
# \{\$Code\((\S*\s*)*\)\}
256-
value = self.ask('getNthComponentModification', f'{className}, {comp_id}', parsed=False)
252+
value = self._ask(question='getNthComponentModification', opt=[className, comp_id], parsed=False)
257253
value = value.replace("{$Code(", "")
258254
return value[:-3]
259255
# return self.re_Code.findall(value)
@@ -269,28 +265,22 @@ def getNthComponentModification(self, className, comp_id):
269265
# end getClassNames;
270266
def getClassNames(self, className=None, recursive=False, qualified=False, sort=False, builtin=False,
271267
showProtected=False):
272-
value = self.ask(
273-
'getClassNames',
274-
(f'{className}, ' if className else '') +
275-
f'recursive={str(recursive).lower()}, '
276-
f'qualified={str(qualified).lower()}, '
277-
f'sort={str(sort).lower()}, '
278-
f'builtin={str(builtin).lower()}, '
279-
f'showProtected={str(showProtected).lower()}'
280-
)
281-
return value
268+
opt = [className] if className else [] + [f'recursive={str(recursive).lower()}',
269+
f'qualified={str(qualified).lower()}',
270+
f'sort={str(sort).lower()}',
271+
f'builtin={str(builtin).lower()}',
272+
f'showProtected={str(showProtected).lower()}']
273+
return self._ask(question='getClassNames', opt=opt)
282274

283275

284-
class OMCSessionZMQ(OMCSessionBase):
276+
class OMCSessionZMQ:
285277

286-
def __init__(self, readonly=False, timeout=10.00,
278+
def __init__(self, timeout=10.00,
287279
docker=None, dockerContainer=None, dockerExtraArgs=None, dockerOpenModelicaPath="omc",
288280
dockerNetwork=None, port=None, omhome: str = None):
289281
if dockerExtraArgs is None:
290282
dockerExtraArgs = []
291283

292-
super().__init__(readonly=readonly)
293-
294284
self.omhome = self._get_omhome(omhome=omhome)
295285

296286
self._omc_process = None
@@ -528,6 +518,12 @@ def _connect_to_omc(self, timeout):
528518
self._omc.setsockopt(zmq.IMMEDIATE, True) # Queue messages only to completed connections
529519
self._omc.connect(self._port)
530520

521+
def execute(self, command):
522+
warnings.warn("This function is depreciated and will be removed in future versions; "
523+
"please use sendExpression() instead", DeprecationWarning, stacklevel=1)
524+
525+
return self.sendExpression(command, parsed=False)
526+
531527
def sendExpression(self, command, parsed=True):
532528
p = self._omc_process.poll() # check if process is running
533529
if p is not None:

OMPython/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
CONDITIONS OF OSMC-PL.
3737
"""
3838

39-
from OMPython.OMCSession import OMCSessionBase, OMCSessionZMQ, OMCSessionException
39+
from OMPython.OMCSession import OMCSessionCmd, OMCSessionZMQ, OMCSessionException
4040
from OMPython.ModelicaSystem import ModelicaSystem, ModelicaSystemError, LinearizationResult
4141

4242
# global names imported if import 'from OMPython import *' is used
@@ -47,5 +47,5 @@
4747

4848
'OMCSessionException',
4949
'OMCSessionZMQ',
50-
'OMCSessionBase',
50+
'OMCSessionCmd',
5151
]

tests/test_OMSessionCmd.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import OMPython
2+
import unittest
3+
4+
5+
class OMCSessionCmdTester(unittest.TestCase):
6+
def __init__(self, *args, **kwargs):
7+
super(OMCSessionCmdTester, self).__init__(*args, **kwargs)
8+
9+
def test_isPackage(self):
10+
omczmq = OMPython.OMCSessionZMQ()
11+
omccmd = OMPython.OMCSessionCmd(session=omczmq)
12+
assert not omccmd.isPackage('Modelica')
13+
14+
def test_isPackage2(self):
15+
mod = OMPython.ModelicaSystem(modelName="Modelica.Electrical.Analog.Examples.CauerLowPassAnalog",
16+
lmodel=["Modelica"])
17+
omccmd = OMPython.OMCSessionCmd(session=mod.getconn)
18+
assert omccmd.isPackage('Modelica')
19+
20+
# TODO: add more checks ...
21+
22+
23+
if __name__ == '__main__':
24+
unittest.main()

0 commit comments

Comments
 (0)