Skip to content

Commit b0f6914

Browse files
committed
Configurable variable separator
1 parent 9b3fe75 commit b0f6914

File tree

4 files changed

+198
-2
lines changed

4 files changed

+198
-2
lines changed

docs/source/transformers/RenameVariables.rst

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Conventions can be configured or switched off using parameters - read more in th
7676

7777
RenameVariables is still under development and is not considered feature complete. Following syntax is not yet supported:
7878

79-
- changing variable scope with ``Śet Test/Suite/Global Variable``
79+
- changing variable scope with ``Set Test/Suite/Global Variable``
8080
- variable evaluation with ``${variable * 2}`` (following will be replaced to ``${variable_*_2}``
8181

8282
Robotidy can be locally disabled with # robotidy: off if you want to ignore specific cases.
@@ -213,6 +213,56 @@ Variable names written in camelCase are converted to snake_case. You can disable
213213
${camelcase_name} Set Variable value
214214
Keyword Call ${camelcase_name}
215215
216+
Variable separator
217+
-------------------
218+
219+
Separators inside variable name are converted to underscore (``_``). You can configure it using ``variable_separator``::
220+
221+
> robotidy -c RenameVariables:variable_separator=underscore
222+
223+
Allowed values are:
224+
225+
- ``underscore`` (default)
226+
- ``space``
227+
228+
.. tab-set::
229+
230+
.. tab-item:: Before
231+
232+
.. code:: robotframework
233+
234+
*** Variables ***
235+
${camelCase} value
236+
237+
*** Keywords ***
238+
Keyword
239+
${variable_name} Set Variable value
240+
Keyword Call ${variable name}
241+
242+
.. tab-item:: After - default (variable_separator = underscore)
243+
244+
.. code:: robotframework
245+
246+
*** Variables ***
247+
${CAMEL_CASE} value
248+
249+
*** Keywords ***
250+
Keyword
251+
${variable_name} Set Variable value
252+
Keyword Call ${variable_name}
253+
254+
.. tab-item:: After (variable_separator = space)
255+
256+
.. code:: robotframework
257+
258+
*** Variables ***
259+
${CAMEL CASE} value
260+
261+
*** Keywords ***
262+
Keyword
263+
${variable name} Set Variable value
264+
Keyword Call ${variable name}
265+
216266
Skip formatting
217267
----------------
218268

robotidy/transformers/RenameVariables.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,16 +133,20 @@ def __init__(
133133
settings_section_case: str = "upper",
134134
variables_section_case: str = "upper",
135135
unknown_variables_case: str = "upper",
136+
variable_separator: str = "underscore",
136137
convert_camel_case: bool = True,
137138
skip: Skip = None,
138139
):
139140
super().__init__(skip)
140141
self.validate_case("settings_section_case", settings_section_case, ["upper", "lower", "ignore"])
141142
self.validate_case("variables_section_case", variables_section_case, ["upper", "lower", "ignore"])
142143
self.validate_case("unknown_variables_case", unknown_variables_case, ["upper", "lower", "ignore"])
144+
self.validate_variable_separator(variable_separator)
143145
self.settings_section_case = settings_section_case
144146
self.variables_section_case = variables_section_case
145147
self.unknown_variables_case = unknown_variables_case
148+
# we always convert to space, so we only need to check if we need to convert back to _
149+
self.replace_variable_separator = variable_separator == "underscore"
146150
self.convert_camel_case = convert_camel_case
147151
self.variables_scope = VariablesScope()
148152

@@ -156,6 +160,15 @@ def validate_case(self, param_name: str, case: str, allowed_case: List):
156160
f"Invalid case type. Allowed case types are: {case_types}",
157161
)
158162

163+
def validate_variable_separator(self, variable_separator):
164+
if variable_separator not in ("underscore", "space"):
165+
raise InvalidParameterValueError(
166+
self.__class__.__name__,
167+
"variable_separator",
168+
variable_separator,
169+
"Allowed values are: underscore, space",
170+
)
171+
159172
@skip_section_if_disabled
160173
def visit_Section(self, node): # noqa
161174
return self.generic_visit(node)
@@ -368,7 +381,8 @@ def rename(self, variable_value: str, case: str, strip_fn: str = "strip"):
368381
variable_name = variable_name.lstrip()
369382
elif strip_fn == "rstrip":
370383
variable_name = variable_name.rstrip()
371-
variable_name = variable_name.replace(" ", "_")
384+
if self.replace_variable_separator:
385+
variable_name = variable_name.replace(" ", "_")
372386
return variable_name + item_access
373387

374388

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
*** Settings ***
2+
Library ExampleLibrary @{LIB ARGS}
3+
Library ${LIBRARY} @{LIB ARGS}
4+
5+
Variables ${NAME}.py
6+
Resource ${CURDIR}/${NAME}.robot
7+
8+
Suite Setup Some Keyword @{KW ARGS}
9+
Suite Teardown ${KEYWORD} @{KW ARGS}
10+
Test Setup Some Keyword @{KW ARGS}
11+
Test Teardown ${KEYWORD} @{KW ARGS}
12+
13+
Default Tags @{TAGS} tag_${NAME}
14+
Test Timeout ${TIMEOUT}
15+
16+
Metadata ${ITEM} ${VALUE}
17+
18+
19+
*** Variables ***
20+
${VARIABLE} value_
21+
${VAR IABLE} ${VA LUE}
22+
${VARIABLE} This is string with ${VARIABLE}
23+
${${VAR}} value
24+
${VARIABLE} ${${VARIABLE}}
25+
${VARIABLE} ${VAR ${VARIABLE} VAR}
26+
${VARIABLE} String with ${${VARIABLE}}
27+
${VARIABLE} ${VARIABLE['item_access']}
28+
${VARIABLE} ${VARIABLE}[item_access]
29+
${VARIABLE} ${VARIABLE}[${ITEM}_access]
30+
${VARIABLE} ${VARIABLE['${VARIABLE}']}
31+
${VARIABLE} ${}____
32+
${VARI ABLE} ${WO RD}
33+
${VARIABLE} \${escaped}
34+
${INLINE EVAL} ${{ eval }}
35+
36+
&{DICT} item=value
37+
... item=${VALUE}
38+
@{LIST} value
39+
... other ${VALUE}
40+
... ${{embedd_ ed}
41+
42+
${CAMEL CASE NAME} ${CAMEL CASE NAME}
43+
${CAMEL CASE NAME} ${CAMEL CASE NAME}
44+
${CAMEL CASE NAME} ${CAMEL CASE NAME}
45+
${CAMEL CASE NAME WORD CAMEL CASE} ${CAMEL CASE NAME WORD CAMEL CASE}
46+
47+
48+
*** Test Cases ***
49+
Assign
50+
${variable} Keyword
51+
${multiple}
52+
... ${variables} Keyword
53+
${variable} = Keyword
54+
${variable}= Keyword
55+
Keyword ${NESTED ${variable}}
56+
57+
Args
58+
Keyword ${VARIABLE}
59+
Keyword ${V A RI ABLES}
60+
... value with ${VARIABLE}
61+
62+
For header
63+
${local} Set Variable item
64+
FOR ${item} IN @{LIST}
65+
Log ${item}
66+
Do Stuff String with ${local} value
67+
... ${lo cal} # TODO We could normalize it to look as first local matching variable
68+
END
69+
Log ${GLOBAL}
70+
Log ${item}
71+
FOR ${index} ${item} IN ENUMERATE @{LIST}
72+
Log ${index} ${item}
73+
END
74+
Log ${local['item']}
75+
Log ${GLOBAL['item']}
76+
77+
Test With Variables In Keyword Call
78+
[Setup] ${KEYWORD}
79+
${local} Set Variable local value
80+
Keyword Call With ${VARIABLE}
81+
Keyword Call With ${local}
82+
${global} Keyword Call With ${GLOBAL}
83+
[Teardown] ${local}
84+
85+
Test case with ${VARIABLE} in name
86+
[Documentation] The RF surprises me vol. 678
87+
Step
88+
89+
90+
*** Keywords ***
91+
Arguments
92+
[Arguments] ${arg} ${arg2}
93+
Step ${arg}
94+
Step ${ARG3}
95+
96+
Kwargs
97+
[Arguments] ${arg} &{kwargs}
98+
Step
99+
100+
Defaults
101+
[Arguments] ${arg} ${arg2} = 'default'
102+
Step
103+
104+
Defaults With Global
105+
[Arguments] ${arg} ${arg2} =${GLOBAL}
106+
Step
107+
108+
Defaults With Other Arg
109+
[Arguments] ${arg} ${arg2} = ${arg}
110+
Step
111+
112+
Embedded ${arguments} that ${should be lower} and also ${pattern:\S}
113+
Log ${should be lower}
114+
Log ${GLOBAL}
115+
Log ${pattern}

tests/atest/transformers/RenameVariables/test_transformer.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ def test_ignore_settings_case(self):
3939
def test_ignore_camel_case(self):
4040
self.compare(source="test.robot", expected="test_ignore_camel_case.robot", config=":convert_camel_case=False")
4141

42+
def test_separator_underscore(self):
43+
self.compare(
44+
source="test.robot", expected="test_separator_underscore.robot", config=":variable_separator=space"
45+
)
46+
4247
@pytest.mark.parametrize(
4348
"param_name, allowed",
4449
[
@@ -58,3 +63,15 @@ def test_invalid_param(self, param_name, allowed):
5863
f"Invalid case type. Allowed case types are: {allowed}\n"
5964
)
6065
assert expected_output == result.output
66+
67+
def test_invalid_variable_separator(self):
68+
result = self.run_tidy(
69+
args=f"--transform {self.TRANSFORMER_NAME}:variable_separator=invalid".split(),
70+
source="test.robot",
71+
exit_code=1,
72+
)
73+
expected_output = (
74+
f"Error: {self.TRANSFORMER_NAME}: Invalid 'variable_separator' parameter value: 'invalid'. "
75+
f"Allowed values are: underscore, space\n"
76+
)
77+
assert expected_output == result.output

0 commit comments

Comments
 (0)