From 29d99c7a90b1fbb4ef6deda345b8b6a1069b6928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joni=20P=C3=B6ll=C3=A4nen?= Date: Thu, 25 Jul 2024 15:05:12 +0300 Subject: [PATCH] Add to_string !eval tag (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Joni Pöllänen --- README.adoc | 5 ++++- param_configuration/tags/eval.py | 19 ++++++++++++++----- test/test_configuration.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/README.adoc b/README.adoc index 1113313..691c014 100644 --- a/README.adoc +++ b/README.adoc @@ -173,10 +173,13 @@ Examples and tutorials can be found in link:examples[examples] folder. |param_configuration.configuration.get_resolved_yaml |Resolves the given YAML file and returns a path to it. +|!eval to_string() +|str() +|Converts the given value to a string. Handy when using nested evaluations. + |!eval var. |- |Allows using variables which are defined in the beginning of the same file, under ".variables" -key. This key will be removed from the result file. - |=== diff --git a/param_configuration/tags/eval.py b/param_configuration/tags/eval.py index 375480a..a49602e 100644 --- a/param_configuration/tags/eval.py +++ b/param_configuration/tags/eval.py @@ -39,18 +39,19 @@ def __getattr__(self, item, *args, **kwargs) -> Any: return dict.get(item, *args, **kwargs) -def names(var: dict[str, Any]) -> dict[str, Any]: - """Function proving additional variables for simple eval.""" +def additional_names(var: dict[str, Any]) -> dict[str, Any]: + """Function providing additional variables for simple eval.""" return {"env": os.environ, "m": math, "np": numpy, "var": Dotdict(var)} -def functions() -> dict[str, Any]: +def additional_functions() -> dict[str, Any]: """Function providing additional function for simple eval.""" return { "path_to": get_package_share_directory, "join": os.path.join, "round": round, "get_resolved_yaml": get_resolved_yaml, + "to_string": str, } @@ -71,8 +72,10 @@ def constructor(self, tag_value: str, file: str, loader: BaseConstructor) -> Any self._vars = self.extract_variables(loader) default_functions = simpleeval.DEFAULT_FUNCTIONS - default_functions.update(functions()) - res = simpleeval.simple_eval(expr=tag_value, functions=default_functions, names=names(var=self._vars)) + default_functions.update(additional_functions()) + res = self.eval_with_compound_types( + tag_value, functions=default_functions, names=additional_names(var=self._vars) + ) return res @staticmethod @@ -103,6 +106,12 @@ def extract_variables(loader) -> dict: variables |= var return variables + @staticmethod + def eval_with_compound_types(tag_value: str, functions: dict[str, Any], names: dict[str, Any]) -> Any: + """Evaluates the tag value with compound types.""" + obj = simpleeval.EvalWithCompoundTypes(functions=functions, names=names) + return obj.eval(tag_value) + # Add the constructor. Configuration().add_config_constructor(const=EvalConfigConstructor) diff --git a/test/test_configuration.py b/test/test_configuration.py index 2400142..5438449 100644 --- a/test/test_configuration.py +++ b/test/test_configuration.py @@ -78,6 +78,38 @@ def test_eval_tag_from_string(yaml_string: str) -> None: assert data_str == expected_str +def test_eval_round() -> None: + """Test the !eval directive with the round function.""" + yaml_string = """ + round: !eval round(3.141592653589793, 2) + """ + expected_string = "round: 3.14" + + configuration = Configuration() + data = configuration.load(yaml_string) + data_str = configuration.dump(data) + assert data_str == expected_string + + +def test_eval_to_string() -> None: + """Test the !eval directive with the to_string function.""" + yaml_string = """ + float_to_string: !eval to_string(3.141592653589793) + array_to_string: !eval to_string([[1.0, 1.0], [2.0, 2.0]]) + """ + # fmt: off + expected_string = ( + "float_to_string: '3.141592653589793'\n" + "array_to_string: '[[1.0, 1.0], [2.0, 2.0]]'" + ) + # fmt: on + + configuration = Configuration() + data = configuration.load(yaml_string) + data_str = configuration.dump(data) + assert data_str == expected_string + + def test_eval_small_numbers() -> None: """Test the loading and dumping !eval directive from a string.""" configuration = Configuration()