Skip to content

Commit ebc546d

Browse files
authored
Robotidy should always use the same order (#143)
1 parent 7ae88a4 commit ebc546d

File tree

7 files changed

+70
-25
lines changed

7 files changed

+70
-25
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
- New option ``--output`` option for saving transformed file to provided path instead of overwriting source file [#108](https://github.com/MarketSquare/robotframework-tidy/issues/108)
1212
- ``--desc`` now accepts ``all`` for printing out description of all transformers [#105](https://github.com/MarketSquare/robotframework-tidy/issues/105)
1313
- Robotidy will now suggest similar names for invalid transformer names used with ``--transform`` or ``--desc`` options [#107](https://github.com/MarketSquare/robotframework-tidy/issues/107)
14+
- ``--list`` now prints transformers in alphabetical order [#141](https://github.com/MarketSquare/robotframework-tidy/issues/141)
1415

1516
### Fixes
1617
- Renamed short version of ``--lineseparator`` to ``-ls`` to avoid collision with ``--list\-l``
1718
- Description for disabled transformers can be now displayed & disabled transformers are in ``--list`` output [#114](https://github.com/MarketSquare/robotframework-tidy/issues/114)
1819
- Robotidy should now correctly load configuration files from path when using ``--config`` [#138](https://github.com/MarketSquare/robotframework-tidy/issues/138)
20+
- Transformers should always use the same order. If you need to use custom order, provide --force-order flag [#142](https://github.com/MarketSquare/robotframework-tidy/issues/142)
1921

2022
### Other
2123
- Removed ``'--describe-transformer`` and ``--list-transformers`` aliases for ``--list`` and ``--desc``

docs/source/transformers/index.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ It will transform files according to internal order (in this example ``ReplaceRu
3232
robotidy --transform SplitTooLongLine src
3333
robotidy --transform ReplaceRunKeywordIf src
3434

35+
You can also add ``--force-order`` flag to use order provided in cli:
36+
robotidy --force-order --transform SplitTooLongLine --transform ReplaceRunKeywordIf src
37+
38+
External transformers are used last. If you want to change this behaviour (for example run your custom transformer
39+
before default ones) you need to use ``--force-order`` flag.
3540

3641
.. rubric:: List of transformers
3742

robotidy/api.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ def __init__(self, src: str, output: Optional[str], **kwargs):
3333
formatting_config=formatting_config,
3434
verbose=False,
3535
check=False,
36-
output=output
36+
output=output,
37+
force_order=False
3738
)
3839

3940

robotidy/app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def __init__(self,
2727
formatting_config: GlobalFormattingConfig,
2828
verbose: bool,
2929
check: bool,
30-
output: Optional[Path]
30+
output: Optional[Path],
31+
force_order: bool
3132
):
3233
self.sources = self.get_paths(src)
3334
self.overwrite = overwrite

robotidy/cli.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,12 @@ def print_transformers_list():
192192
click.echo('To see detailed docs run --desc <transformer_name> or --desc all. '
193193
'Transformers with (disabled) tag \nare executed only when selected explictly with --transform. '
194194
'Available transformers:\n')
195+
transformer_names = []
195196
for transformer in transformers:
196197
disabled = ' (disabled)' if not getattr(transformer, 'ENABLED', True) else ''
197-
click.echo(transformer.__class__.__name__ + disabled)
198+
transformer_names.append(transformer.__class__.__name__ + disabled)
199+
for name in sorted(transformer_names):
200+
click.echo(name)
198201

199202

200203
@click.command(cls=RawHelp, help=HELP_MSG, epilog=EPILOG, context_settings=CONTEXT_SETTINGS)
@@ -332,6 +335,11 @@ def print_transformers_list():
332335
'--describe-transformer',
333336
default=None
334337
)
338+
@click.option(
339+
'--force-order',
340+
is_flag=True,
341+
help='Transform files using transformers in order provided in cli'
342+
)
335343
@click.version_option(version=__version__, prog_name='robotidy')
336344
@click.pass_context
337345
def cli(
@@ -352,7 +360,8 @@ def cli(
352360
desc: Optional[str],
353361
output: Optional[Path],
354362
list_transformers: bool,
355-
describe_transformer: Optional[str]
363+
describe_transformer: Optional[str],
364+
force_order: bool
356365
):
357366
if list_transformers:
358367
print('--list-transformers is deprecated in 1.3.0. Use --list instead')
@@ -388,7 +397,8 @@ def cli(
388397
formatting_config=formatting_config,
389398
verbose=verbose,
390399
check=check,
391-
output=output
400+
output=output,
401+
force_order=force_order
392402
)
393403
status = tidy.transform_files()
394404
ctx.exit(status)

robotidy/transformers/__init__.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,28 +51,40 @@ def import_transformer(name, args):
5151
f"Verify if correct name or configuration was provided.{similar}") from None
5252

5353

54-
def load_transformers(allowed_transformers, config, allow_disabled=False):
54+
def load_transformer(name, args, config):
55+
# if we configure the same parameter for both --transform and --configure we need to overwrite it
56+
# it is done by converting to dict and back to list in format of key=value
57+
temp_args = {}
58+
for arg in chain(args, config.get(name, ())):
59+
param, value = arg.split('=', maxsplit=1)
60+
temp_args[param] = value
61+
args = [f'{key}={value}' for key, value in temp_args.items()]
62+
import_name = f'robotidy.transformers.{name}' if name in TRANSFORMERS else name
63+
return import_transformer(import_name, args)
64+
65+
66+
def load_transformers(allowed_transformers, config, allow_disabled=False, force_order=False):
5567
""" Dynamically load all classes from this file with attribute `name` defined in allowed_transformers """
5668
loaded_transformers = []
57-
if allowed_transformers:
58-
for name, args in allowed_transformers:
59-
# if we are configure the same param from both --transform and --configure we need to overwrite it
60-
# it is done by converting to dict and back to list in format of key=value
61-
temp_args = {}
62-
for arg in chain(args, config.get(name, ())):
63-
param, value = arg.split('=', maxsplit=1)
64-
temp_args[param] = value
65-
args = [f'{key}={value}' for key, value in temp_args.items()]
66-
import_name = f'robotidy.transformers.{name}' if name in TRANSFORMERS else name
67-
imported_class = import_transformer(import_name, args)
68-
if imported_class is None:
69-
return []
70-
loaded_transformers.append(imported_class)
71-
else:
69+
allowed_mapped = {name: args for name, args in allowed_transformers} if allowed_transformers else {}
70+
if not force_order:
7271
for name in TRANSFORMERS:
73-
imported_class = import_transformer(f'robotidy.transformers.{name}', config.get(name, ()))
72+
if allowed_mapped:
73+
if name in allowed_mapped:
74+
imported_class = load_transformer(name, allowed_mapped[name], config)
75+
if imported_class is None:
76+
return []
77+
loaded_transformers.append(imported_class)
78+
else:
79+
imported_class = import_transformer(f'robotidy.transformers.{name}', config.get(name, ()))
80+
if imported_class is None:
81+
return []
82+
if allow_disabled or getattr(imported_class, 'ENABLED', True):
83+
loaded_transformers.append(imported_class)
84+
for name in allowed_mapped:
85+
if force_order or name not in TRANSFORMERS:
86+
imported_class = load_transformer(name, allowed_mapped[name], config)
7487
if imported_class is None:
7588
return []
76-
if allow_disabled or getattr(imported_class, 'ENABLED', True):
77-
loaded_transformers.append(imported_class)
89+
loaded_transformers.append(imported_class)
7890
return loaded_transformers

tests/utest/test_cli.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,26 @@ def test_not_existing_transformer(self, name, similar):
4343
result = run_tidy(args, exit_code=1)
4444
assert expected_output in str(result.exception)
4545

46+
def test_transformer_order(self):
47+
order_1 = ['NormalizeSeparators', 'OrderSettings']
48+
order_2 = ['OrderSettings', 'NormalizeSeparators']
49+
transformers_1 = load_transformers([(transf, []) for transf in order_1], {})
50+
transformers_2 = load_transformers([(transf, []) for transf in order_2], {})
51+
assert all(t1.__class__.__name__ == t2.__class__.__name__ for t1, t2 in zip(transformers_1, transformers_2))
52+
53+
def test_transformer_force_order(self):
54+
# default_order = ['NormalizeSeparators', 'OrderSettings']
55+
custom_order = ['OrderSettings', 'NormalizeSeparators']
56+
transformers = load_transformers([(transf, []) for transf in custom_order], {}, force_order=True)
57+
assert all(t1.__class__.__name__ == t2 for t1, t2 in zip(transformers, custom_order))
58+
4659
# TODO: raise exception if kwarg does not match
4760
# def test_not_existing_configurable(self):
4861
# expected_output = "Usage: cli [OPTIONS] [PATH(S)]\n\n" \
4962
# "Error: Invalid configurable name: 'missing_configurable' for transformer: " \
5063
# "'DiscardEmptySections'\n"
5164
#
52-
# args = '--transform DiscardEmptySections:allow_only_commentss=True'.split()
65+
# args = '--transform DiscardEmptySections:allow_only_commentss=True -'.split()
5366
# result = run_tidy(args, exit_code=2)
5467
# assert expected_output == result.output
5568

@@ -189,6 +202,7 @@ def test_list_transformers(self, flag):
189202
in result.output
190203
assert 'ReplaceRunKeywordIf\n' in result.output
191204
assert 'SmartSortKeywords (disabled)\n' in result.output # this transformer is disabled by default
205+
assert 'Available transformers:\n\nAlignSettingsSection\n' in result.output # assert order
192206

193207
@pytest.mark.parametrize('flag', ['--desc', '-d'])
194208
@pytest.mark.parametrize('name, expected_doc', [

0 commit comments

Comments
 (0)