diff --git a/docs/releasenotes/unreleased/fixes.1.rst b/docs/releasenotes/unreleased/fixes.1.rst new file mode 100644 index 00000000..dc8c7f5c --- /dev/null +++ b/docs/releasenotes/unreleased/fixes.1.rst @@ -0,0 +1,5 @@ +Robot Framework 7.0 backward incompatible changes: VariableIterator refactor +---------------------------------------------------------------------------- + +Robotidy variables handling relied upon ``VariableIterator`` class imported from Robot Framework package. +It caused ImportError which should be now fixed. diff --git a/docs/releasenotes/unreleased/fixes.2.rst b/docs/releasenotes/unreleased/fixes.2.rst new file mode 100644 index 00000000..8141526c --- /dev/null +++ b/docs/releasenotes/unreleased/fixes.2.rst @@ -0,0 +1,5 @@ +Robot Framework 7.0 backward incompatible changes: ForceTags deprecation (#584) +-------------------------------------------------------------------------------- + +Our tag transformers imports ``Force Tags`` class from ``robot`` module. It was deprecated in Robot Framework 7 +and caused ImportError when using Robotidy. It should be now fixed. diff --git a/robotidy/transformers/RenameKeywords.py b/robotidy/transformers/RenameKeywords.py index 39f29232..df17b95e 100644 --- a/robotidy/transformers/RenameKeywords.py +++ b/robotidy/transformers/RenameKeywords.py @@ -1,15 +1,13 @@ import re -import string from typing import Optional from robot.api.parsing import Token -from robot.variables.search import VariableIterator from robotidy.disablers import skip_if_disabled, skip_section_if_disabled from robotidy.exceptions import InvalidParameterValueError from robotidy.transformers import Transformer from robotidy.transformers.run_keywords import get_run_keywords -from robotidy.utils import misc +from robotidy.utils import misc, variable_matcher class RenameKeywords(Transformer): @@ -101,16 +99,17 @@ def rename_node(self, token, is_keyword_call): def normalize_name(self, value, is_keyword_call): var_found = False parts = [] - remaining = "" - for prefix, match, remaining in VariableIterator(value, ignore_errors=True): + after = "" + for match in variable_matcher.VariableMatches(value, ignore_errors=True): var_found = True # rename strips whitespace, so we need to preserve it if needed - if not prefix.strip() and parts: - parts.extend([" ", match]) + if not match.before.strip() and parts: + parts.extend([" ", match.match]) else: - parts.extend([self.rename_part(prefix, is_keyword_call), match]) + parts.extend([self.rename_part(match.before, is_keyword_call), match.match]) + after = match.after if var_found: - parts.append(self.rename_part(remaining, is_keyword_call)) + parts.append(self.rename_part(after, is_keyword_call)) return "".join(parts).strip() return self.rename_part(value, is_keyword_call) @@ -142,8 +141,8 @@ def rename_with_pattern(self, value: str, is_keyword_call: bool): if is_keyword_call and "." in value: # rename only non lib part found_lib = -1 - for prefix, _, _ in VariableIterator(value): - found_lib = prefix.find(".") + for match in variable_matcher.VariableMatches(value): + found_lib = match.before.find(".") break if found_lib != -1: lib_name = value[: found_lib + 1] diff --git a/robotidy/transformers/RenameVariables.py b/robotidy/transformers/RenameVariables.py index 260e29e2..12bdd88c 100644 --- a/robotidy/transformers/RenameVariables.py +++ b/robotidy/transformers/RenameVariables.py @@ -3,14 +3,13 @@ from robot.api.parsing import Arguments, Token from robot.errors import VariableError -from robot.variables import VariableIterator from robot.variables.search import search_variable from robotidy.disablers import skip_if_disabled, skip_section_if_disabled from robotidy.exceptions import InvalidParameterValueError from robotidy.skip import Skip from robotidy.transformers import Transformer -from robotidy.utils import misc +from robotidy.utils import misc, variable_matcher SET_GLOBAL_VARIABLES = {"settestvariable", "settaskvariable", "setsuitevariable", "setglobalvariable"} @@ -32,7 +31,7 @@ def is_nested_variable(variable: str) -> bool: if not match.base: return False match = search_variable(match.base, ignore_errors=True) - return match.base + return bool(match.base) def resolve_var_name(name: str) -> str: @@ -383,23 +382,20 @@ def visit_While(self, node): # noqa def rename_value(self, value: str, variable_case: str, is_var: bool = False): try: - variables = list(VariableIterator(value)) + variables = list(variable_matcher.VariableMatches(value)) except VariableError: # for example ${variable which wasn't closed properly variables = [] if not variables: if is_var: return self.rename(value, case=variable_case, strip_fn="strip") return value - name = "" - remaining = "" - for before, variable, remaining in variables: - if before: + name, after = "", "" + for match in variables: + if match.before: if is_var: - name += self.rename(before, case=variable_case, strip_fn="lstrip") + name += self.rename(match.before, case=variable_case, strip_fn="lstrip") else: - name += before - # handle ${variable}[item][${syntax}] - match = search_variable(variable, ignore_errors=True) + name += match.before # inline eval will start and end with {} if not (match.base.startswith("{") and match.base.endswith("}")): base = self.rename_value(match.base, variable_case=variable_case, is_var=True) @@ -410,11 +406,12 @@ def rename_value(self, value: str, variable_case: str, is_var: bool = False): renamed_item = self.rename_value(item, variable_case=variable_case, is_var=False) base += f"[{renamed_item}]" name += base - if remaining: + after = match.after + if after: if is_var: - name += self.rename(remaining, case=variable_case, strip_fn="rstrip") + name += self.rename(after, case=variable_case, strip_fn="rstrip") else: - name += remaining + name += after return name def set_name_case(self, name: str, case: str): diff --git a/tests/utest/test_cli.py b/tests/utest/test_cli.py index b6d8871e..9a2ff993 100644 --- a/tests/utest/test_cli.py +++ b/tests/utest/test_cli.py @@ -323,7 +323,7 @@ def test_help(self, flag): @pytest.mark.parametrize("source, return_status", [("golden.robot", 0), ("not_golden.robot", 1)]) def test_check(self, source, return_status): source = TEST_DATA_DIR / "check" / source - with patch("robotidy.misc.ModelWriter") as mock_writer: + with patch("robotidy.utils.misc.ModelWriter") as mock_writer: run_tidy( ["--check", "--transform", "NormalizeSectionHeaderName", str(source)], exit_code=return_status, @@ -333,7 +333,7 @@ def test_check(self, source, return_status): @pytest.mark.parametrize("source, return_status", [("golden.robot", 0), ("not_golden.robot", 1)]) def test_check_overwrite(self, source, return_status): source = TEST_DATA_DIR / "check" / source - with patch("robotidy.misc.ModelWriter") as mock_writer: + with patch("robotidy.utils.misc.ModelWriter") as mock_writer: run_tidy( ["--check", "--overwrite", "--transform", "NormalizeSectionHeaderName", str(source)], exit_code=return_status, @@ -353,7 +353,7 @@ def test_disable_coloring(self, color_flag, color_env): if color_flag: command.append(color_flag) command.extend(["--transform", "NormalizeSectionHeaderName", str(source)]) - with patch.dict("os.environ", mocked_env), patch("robotidy.misc.decorate_diff_with_color") as mock_color: + with patch.dict("os.environ", mocked_env), patch("robotidy.utils.misc.decorate_diff_with_color") as mock_color: run_tidy(command) if should_be_colored: mock_color.assert_called() diff --git a/tests/utest/test_load_transformers.py b/tests/utest/test_load_transformers.py index 44779e76..68a97c5f 100644 --- a/tests/utest/test_load_transformers.py +++ b/tests/utest/test_load_transformers.py @@ -230,7 +230,7 @@ def test_overwriting_disabled_in_version(self, target_version, version, transfor TransformConfig(config, force_include=False, custom_transformer=False, is_config=True) for config in configure ] - with patch("robotidy.transformers.ROBOT_VERSION", mocked_version): + with patch("robotidy.utils.misc.ROBOT_VERSION", mocked_version): transformers = load_transformers( TransformConfigMap(transform_transformers, [], configure_transformers), skip=skip_config,