Skip to content

Commit 0a4be69

Browse files
committed
[ModelicaSystem] update input handling for set*() functions
* use a Pythonic way for input: setParameters(a=123) param = {'a': 123} setParameters(**param) see input by SengerM in PR OpenModelica#326
1 parent ad91868 commit 0a4be69

File tree

2 files changed

+83
-54
lines changed

2 files changed

+83
-54
lines changed

OMPython/ModelicaSystem.py

Lines changed: 77 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,7 +1203,8 @@ def getSolutions(self, varList: Optional[str | list[str]] = None, resultfile: Op
12031203

12041204
@staticmethod
12051205
def _prepare_input_data(
1206-
raw_input: str | list[str] | dict[str, Any],
1206+
input_args: Any,
1207+
input_kwargs: dict[str, Any],
12071208
) -> dict[str, str]:
12081209
"""
12091210
Convert raw input to a structured dictionary {'key1': 'value1', 'key2': 'value2'}.
@@ -1221,38 +1222,42 @@ def prepare_str(str_in: str) -> dict[str, str]:
12211222

12221223
input_data: dict[str, str] = {}
12231224

1224-
if isinstance(raw_input, str):
1225-
warnings.warn(message="The definition of values to set should use a dictionary, "
1226-
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
1227-
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
1228-
category=DeprecationWarning,
1229-
stacklevel=3)
1230-
return prepare_str(raw_input)
1231-
1232-
if isinstance(raw_input, list):
1233-
warnings.warn(message="The definition of values to set should use a dictionary, "
1234-
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
1235-
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
1236-
category=DeprecationWarning,
1237-
stacklevel=3)
1238-
1239-
for item in raw_input:
1240-
input_data |= prepare_str(item)
1241-
1242-
return input_data
1243-
1244-
if isinstance(raw_input, dict):
1245-
for key, val in raw_input.items():
1246-
# convert all values to strings to align it on one type: dict[str, str]
1247-
# spaces have to be removed as setInput() could take list of tuples as input and spaces would
1248-
str_val = str(val).replace(' ', '')
1225+
for input_arg in input_args:
1226+
if isinstance(input_arg, str):
1227+
warnings.warn(message="The definition of values to set should use a dictionary, "
1228+
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
1229+
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
1230+
category=DeprecationWarning,
1231+
stacklevel=3)
1232+
input_data = input_data | prepare_str(input_arg)
1233+
elif isinstance(input_arg, list):
1234+
warnings.warn(message="The definition of values to set should use a dictionary, "
1235+
"i.e. {'key1': 'val1', 'key2': 'val2', ...}. Please convert all cases which "
1236+
"use a string ('key=val') or list ['key1=val1', 'key2=val2', ...]",
1237+
category=DeprecationWarning,
1238+
stacklevel=3)
1239+
1240+
for item in input_arg:
1241+
if not isinstance(item, str):
1242+
raise ModelicaSystemError(f"Invalid input data type for set*() function: {type(item)}!")
1243+
input_data = input_data | prepare_str(item)
1244+
else:
1245+
raise ModelicaSystemError(f"Invalid input data type for set*() function: {type(input_arg)}!")
1246+
1247+
if len(input_kwargs):
1248+
for key, val in input_kwargs.items():
1249+
# ensure all values are strings to align it on one type: dict[str, str]
1250+
if not isinstance(val, str):
1251+
# spaces have to be removed as setInput() could take list of tuples as input and spaces would
1252+
# result in an error on recreating the input data
1253+
str_val = str(val).replace(' ', '')
1254+
else:
1255+
str_val = val
12491256
if ' ' in key or ' ' in str_val:
12501257
raise ModelicaSystemError(f"Spaces not allowed in key/value pairs: {repr(key)} = {repr(val)}!")
12511258
input_data[key] = str_val
12521259

1253-
return input_data
1254-
1255-
raise ModelicaSystemError(f"Invalid type of input: {type(raw_input)}")
1260+
return input_data
12561261

12571262
def _set_method_helper(
12581263
self,
@@ -1284,8 +1289,7 @@ def _set_method_helper(
12841289

12851290
for key, val in inputdata.items():
12861291
if key not in classdata:
1287-
raise ModelicaSystemError("Unhandled case in setMethodHelper.apply_single() - "
1288-
f"{repr(key)} is not a {repr(datatype)} variable")
1292+
raise ModelicaSystemError(f"Invalid variable for type {repr(datatype)}: {repr(key)}")
12891293

12901294
if datatype == "parameter" and not self.isParameterChangeable(key):
12911295
raise ModelicaSystemError(f"It is not possible to set the parameter {repr(key)}. It seems to be "
@@ -1313,17 +1317,21 @@ def isParameterChangeable(
13131317

13141318
def setContinuous(
13151319
self,
1316-
cvals: str | list[str] | dict[str, Any],
1320+
*args: Any,
1321+
**kwargs: dict[str, Any],
13171322
) -> bool:
13181323
"""
13191324
This method is used to set continuous values. It can be called:
13201325
with a sequence of continuous name and assigning corresponding values as arguments as show in the example below:
13211326
usage
13221327
>>> setContinuous("Name=value") # depreciated
13231328
>>> setContinuous(["Name1=value1","Name2=value2"]) # depreciated
1324-
>>> setContinuous(cvals={"Name1": "value1", "Name2": "value2"})
1329+
1330+
>>> setContinuous(Name1="value1", Name2="value2")
1331+
>>> param = {"Name1": "value1", "Name2": "value2"}
1332+
>>> setContinuous(**param)
13251333
"""
1326-
inputdata = self._prepare_input_data(raw_input=cvals)
1334+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
13271335

13281336
return self._set_method_helper(
13291337
inputdata=inputdata,
@@ -1333,17 +1341,21 @@ def setContinuous(
13331341

13341342
def setParameters(
13351343
self,
1336-
pvals: str | list[str] | dict[str, Any],
1344+
*args: Any,
1345+
**kwargs: dict[str, Any],
13371346
) -> bool:
13381347
"""
13391348
This method is used to set parameter values. It can be called:
13401349
with a sequence of parameter name and assigning corresponding value as arguments as show in the example below:
13411350
usage
13421351
>>> setParameters("Name=value") # depreciated
13431352
>>> setParameters(["Name1=value1","Name2=value2"]) # depreciated
1344-
>>> setParameters(pvals={"Name1": "value1", "Name2": "value2"})
1353+
1354+
>>> setParameters(Name1="value1", Name2="value2")
1355+
>>> param = {"Name1": "value1", "Name2": "value2"}
1356+
>>> setParameters(**param)
13451357
"""
1346-
inputdata = self._prepare_input_data(raw_input=pvals)
1358+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
13471359

13481360
return self._set_method_helper(
13491361
inputdata=inputdata,
@@ -1353,17 +1365,21 @@ def setParameters(
13531365

13541366
def setSimulationOptions(
13551367
self,
1356-
simOptions: str | list[str] | dict[str, Any],
1368+
*args: Any,
1369+
**kwargs: dict[str, Any],
13571370
) -> bool:
13581371
"""
13591372
This method is used to set simulation options. It can be called:
13601373
with a sequence of simulation options name and assigning corresponding values as arguments as show in the example below:
13611374
usage
13621375
>>> setSimulationOptions("Name=value") # depreciated
13631376
>>> setSimulationOptions(["Name1=value1","Name2=value2"]) # depreciated
1364-
>>> setSimulationOptions(simOptions={"Name1": "value1", "Name2": "value2"})
1377+
1378+
>>> setSimulationOptions(Name1="value1", Name2="value2")
1379+
>>> param = {"Name1": "value1", "Name2": "value2"}
1380+
>>> setSimulationOptions(**param)
13651381
"""
1366-
inputdata = self._prepare_input_data(raw_input=simOptions)
1382+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
13671383

13681384
return self._set_method_helper(
13691385
inputdata=inputdata,
@@ -1373,17 +1389,21 @@ def setSimulationOptions(
13731389

13741390
def setLinearizationOptions(
13751391
self,
1376-
linearizationOptions: str | list[str] | dict[str, Any],
1392+
*args: Any,
1393+
**kwargs: dict[str, Any],
13771394
) -> bool:
13781395
"""
13791396
This method is used to set linearization options. It can be called:
13801397
with a sequence of linearization options name and assigning corresponding value as arguments as show in the example below
13811398
usage
13821399
>>> setLinearizationOptions("Name=value") # depreciated
13831400
>>> setLinearizationOptions(["Name1=value1","Name2=value2"]) # depreciated
1384-
>>> setLinearizationOptions(linearizationOtions={"Name1": "value1", "Name2": "value2"})
1401+
1402+
>>> setLinearizationOptions(Name1="value1", Name2="value2")
1403+
>>> param = {"Name1": "value1", "Name2": "value2"}
1404+
>>> setLinearizationOptions(**param)
13851405
"""
1386-
inputdata = self._prepare_input_data(raw_input=linearizationOptions)
1406+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
13871407

13881408
return self._set_method_helper(
13891409
inputdata=inputdata,
@@ -1393,17 +1413,21 @@ def setLinearizationOptions(
13931413

13941414
def setOptimizationOptions(
13951415
self,
1396-
optimizationOptions: str | list[str] | dict[str, Any],
1416+
*args: Any,
1417+
**kwargs: dict[str, Any],
13971418
) -> bool:
13981419
"""
13991420
This method is used to set optimization options. It can be called:
14001421
with a sequence of optimization options name and assigning corresponding values as arguments as show in the example below:
14011422
usage
14021423
>>> setOptimizationOptions("Name=value") # depreciated
14031424
>>> setOptimizationOptions(["Name1=value1","Name2=value2"]) # depreciated
1404-
>>> setOptimizationOptions(optimizationOptions={"Name1": "value1", "Name2": "value2"})
1425+
1426+
>>> setOptimizationOptions(Name1="value1", Name2="value2")
1427+
>>> param = {"Name1": "value1", "Name2": "value2"}
1428+
>>> setOptimizationOptions(**param)
14051429
"""
1406-
inputdata = self._prepare_input_data(raw_input=optimizationOptions)
1430+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
14071431

14081432
return self._set_method_helper(
14091433
inputdata=inputdata,
@@ -1413,7 +1437,8 @@ def setOptimizationOptions(
14131437

14141438
def setInputs(
14151439
self,
1416-
name: str | list[str] | dict[str, Any],
1440+
*args: Any,
1441+
**kwargs: dict[str, Any],
14171442
) -> bool:
14181443
"""
14191444
This method is used to set input values. It can be called with a sequence of input name and assigning
@@ -1423,9 +1448,12 @@ def setInputs(
14231448
14241449
>>> setInputs("Name=value") # depreciated
14251450
>>> setInputs(["Name1=value1","Name2=value2"]) # depreciated
1426-
>>> setInputs(name={"Name1": "value1", "Name2": "value2"})
1451+
1452+
>>> setInputs(Name1="value1", Name2="value2")
1453+
>>> param = {"Name1": "value1", "Name2": "value2"}
1454+
>>> setInputs(**param)
14271455
"""
1428-
inputdata = self._prepare_input_data(raw_input=name)
1456+
inputdata = self._prepare_input_data(input_args=args, input_kwargs=kwargs)
14291457

14301458
for key, val in inputdata.items():
14311459
if key not in self._inputs:

tests/test_ModelicaSystem.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ def test_setParameters():
3434
model_path = omc.sendExpression("getInstallationDirectoryPath()") + "/share/doc/omc/testmodels/"
3535
mod = OMPython.ModelicaSystem(model_path + "BouncingBall.mo", "BouncingBall")
3636

37-
# method 1
38-
mod.setParameters(pvals={"e": 1.234})
39-
mod.setParameters(pvals={"g": 321.0})
37+
# method 1 (test depreciated variants)
38+
mod.setParameters("e=1.234")
39+
mod.setParameters(["g=321.0"])
4040
assert mod.getParameters("e") == ["1.234"]
4141
assert mod.getParameters("g") == ["321.0"]
4242
assert mod.getParameters() == {
@@ -46,8 +46,9 @@ def test_setParameters():
4646
with pytest.raises(KeyError):
4747
mod.getParameters("thisParameterDoesNotExist")
4848

49-
# method 2
50-
mod.setParameters(pvals={"e": 21.3, "g": 0.12})
49+
# method 2 (new style)
50+
pvals = {"e": 21.3, "g": 0.12}
51+
mod.setParameters(**pvals)
5152
assert mod.getParameters() == {
5253
"e": "21.3",
5354
"g": "0.12",

0 commit comments

Comments
 (0)