Skip to content

Commit a5ffb97

Browse files
authored
Reorganize import for better support for optional rich dependency (#420)
1 parent 586df50 commit a5ffb97

File tree

10 files changed

+131
-107
lines changed

10 files changed

+131
-107
lines changed

docs/releasenotes/3.3.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,7 @@ English language is supported by default.
3030
* Updated NormalizeSectionHeaderName transformer to work with translated headers
3131
* Updated MergeAndOrderSections to not merge language: <lang> header with other comment sections
3232
* Sections with only language: <lang> marker and less than ``section_lines`` empty lines will be not transformed by NormalizeNewLines
33+
34+
Other
35+
-----
36+
* Reorganized our imports to better support optional rich dependency (#419)

robotidy/api.py

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,8 @@
33
"""
44
from typing import Optional
55

6-
from robotidy.app import Robotidy
7-
from robotidy.cli import TransformType, find_and_read_config, validate_regex
6+
from robotidy import app, disablers, files, skip, transformers, utils
87
from robotidy.config import Config, FormattingConfig
9-
from robotidy.disablers import RegisterDisablers
10-
from robotidy.files import DEFAULT_EXCLUDES
11-
from robotidy.skip import SkipConfig
12-
from robotidy.utils import ROBOT_VERSION
138

149

1510
def get_skip_config(config):
@@ -25,7 +20,7 @@ def get_skip_config(config):
2520
skip_timeout = config.get("skip_timeout", False)
2621
skip_return = config.get("skip_return", False)
2722
skip_tags = config.get("skip_tags", False)
28-
return SkipConfig(
23+
return skip.SkipConfig(
2924
documentation=skip_documentation,
3025
return_values=skip_return_values,
3126
keyword_call=skip_keyword_call,
@@ -61,20 +56,20 @@ def get_formatting_config(config, kwargs):
6156
def get_robotidy(src: str, output: Optional[str], **kwargs):
6257
# TODO Refactor - Config should be read in one place both for API and CLI
6358
# TODO Remove kwargs usage - other SDKs are not using this feature
64-
config = find_and_read_config((src,))
59+
config = files.find_and_read_config((src,))
6560
config = {k: str(v) if not isinstance(v, (list, dict)) else v for k, v in config.items()}
66-
converter = TransformType()
67-
transformers = [converter.convert(tr, None, None) for tr in config.get("transform", ())]
61+
converter = transformers.TransformType()
62+
transformer_list = [converter.convert(tr, None, None) for tr in config.get("transform", ())]
6863
configurations = [converter.convert(c, None, None) for c in config.get("configure", ())]
6964
formatting_config = get_formatting_config(config, kwargs)
7065
exclude = config.get("exclude", None)
7166
extend_exclude = config.get("extend_exclude", None)
72-
exclude = validate_regex(exclude if exclude is not None else DEFAULT_EXCLUDES)
73-
extend_exclude = validate_regex(extend_exclude)
67+
exclude = utils.validate_regex(exclude if exclude is not None else files.DEFAULT_EXCLUDES)
68+
extend_exclude = utils.validate_regex(extend_exclude)
7469
global_skip = get_skip_config(config)
7570
language = config.get("language", None)
7671
configuration = Config(
77-
transformers=transformers,
72+
transformers=transformer_list,
7873
transformers_config=configurations,
7974
skip=global_skip,
8075
src=(),
@@ -88,11 +83,11 @@ def get_robotidy(src: str, output: Optional[str], **kwargs):
8883
check=False,
8984
output=output,
9085
force_order=False,
91-
target_version=ROBOT_VERSION.major,
86+
target_version=utils.ROBOT_VERSION.major,
9287
color=False,
9388
language=language,
9489
)
95-
return Robotidy(config=configuration)
90+
return app.Robotidy(config=configuration)
9691

9792

9893
def transform_model(model, root_dir: str, output: Optional[str] = None, **kwargs) -> Optional[str]:
@@ -106,7 +101,7 @@ def transform_model(model, root_dir: str, output: Optional[str] = None, **kwargs
106101
:return: The transformed model converted to string or None if no transformation took place.
107102
"""
108103
robotidy_class = get_robotidy(root_dir, output, **kwargs)
109-
disabler_finder = RegisterDisablers(
104+
disabler_finder = disablers.RegisterDisablers(
110105
robotidy_class.config.formatting.start_line, robotidy_class.config.formatting.end_line
111106
)
112107
disabler_finder.visit(model)

robotidy/app.py

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,21 @@
88
except ImportError: # Fails on vendored-in LSP plugin
99
import click
1010

11-
escape = None
12-
1311
from robot.api import get_model
1412
from robot.errors import DataError
1513

14+
from robotidy import utils
1615
from robotidy.config import Config
1716
from robotidy.disablers import RegisterDisablers
18-
from robotidy.utils import (
19-
ModelWriter,
20-
StatementLinesCollector,
21-
decorate_diff_with_color,
22-
escape_rich_markup,
23-
rf_supports_lang,
24-
)
25-
26-
try:
27-
from robotidy.rich_console import console
28-
except ImportError: # Fails on vendored-in LSP plugin
29-
console = None
17+
from robotidy.rich_console import console
3018

3119

3220
class Robotidy:
3321
def __init__(self, config: Config):
3422
self.config = config
3523

3624
def get_model(self, source):
37-
if rf_supports_lang():
25+
if utils.rf_supports_lang():
3826
return get_model(source, lang=self.config.language)
3927
return get_model(source)
4028

@@ -73,11 +61,11 @@ def transform_files(self):
7361
return 1
7462

7563
def transform(self, model, disablers):
76-
old_model = StatementLinesCollector(model)
64+
old_model = utils.StatementLinesCollector(model)
7765
for transformer in self.config.transformers:
7866
setattr(transformer, "disablers", disablers) # set dynamically to allow using external transformers
7967
transformer.visit(model)
80-
new_model = StatementLinesCollector(model)
68+
new_model = utils.StatementLinesCollector(model)
8169
return new_model != old_model, old_model, new_model
8270

8371
@staticmethod
@@ -91,7 +79,7 @@ def print_to_stdout(self, collected_lines):
9179
def save_model(self, source, model):
9280
if self.config.overwrite:
9381
output = self.config.output or model.source
94-
ModelWriter(output=output, newline=self.get_line_ending(source)).write(model)
82+
utils.ModelWriter(output=output, newline=self.get_line_ending(source)).write(model)
9583

9684
def get_line_ending(self, path: str):
9785
if self.config.formatting.line_sep == "auto":
@@ -108,8 +96,8 @@ def get_line_ending(self, path: str):
10896
def output_diff(
10997
self,
11098
path: str,
111-
old_model: StatementLinesCollector,
112-
new_model: StatementLinesCollector,
99+
old_model: utils.StatementLinesCollector,
100+
new_model: utils.StatementLinesCollector,
113101
):
114102
if not self.config.show_diff:
115103
return
@@ -119,8 +107,8 @@ def output_diff(
119107
if not lines:
120108
return
121109
if self.config.color:
122-
output = decorate_diff_with_color(lines)
110+
output = utils.decorate_diff_with_color(lines)
123111
else:
124-
output = escape_rich_markup(lines)
112+
output = utils.escape_rich_markup(lines)
125113
for line in output:
126114
console.print(line, end="", highlight=False, soft_wrap=True)

robotidy/cli.py

Lines changed: 43 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,23 @@
11
import os
22
import re
33
import sys
4-
import textwrap
54
from pathlib import Path
65
from typing import Any, Dict, List, Optional, Pattern, Tuple, Union
76

8-
import rich_click as click
7+
try:
8+
import rich_click as click
99

10-
from robotidy import skip
11-
from robotidy.app import Robotidy
10+
RICH_PRESENT = True
11+
except ImportError: # Fails on vendored-in LSP plugin
12+
import click
13+
14+
RICH_PRESENT = False
15+
16+
from robotidy import app, decorators, exceptions, files, skip, utils, version
1217
from robotidy.config import Config, FormattingConfig
13-
from robotidy.decorators import catch_exceptions
14-
from robotidy.files import DEFAULT_EXCLUDES, find_and_read_config, read_pyproject_config
1518
from robotidy.rich_console import console
16-
from robotidy.transformers import load_transformers
17-
from robotidy.utils import ROBOT_VERSION, RecommendationFinder, TargetVersion, split_args_from_name_or_path
18-
from robotidy.version import __version__
19-
20-
click.rich_click.USE_RICH_MARKUP = True
21-
click.rich_click.USE_MARKDOWN = True
22-
click.rich_click.STYLE_OPTION = "bold sky_blue3"
23-
click.rich_click.STYLE_SWITCH = "bold sky_blue3"
24-
click.rich_click.STYLE_METAVAR = "bold white"
25-
click.rich_click.STYLE_OPTION_DEFAULT = "grey37"
26-
click.rich_click.STYLE_OPTIONS_PANEL_BORDER = "grey66"
27-
click.rich_click.STYLE_USAGE = "magenta"
19+
from robotidy.transformers import TransformType, load_transformers
20+
2821
CLI_OPTIONS_LIST = [
2922
{
3023
"name": "Run only selected transformers",
@@ -62,21 +55,23 @@
6255
"options": ["--target-version", "--language", "--verbose", "--color", "--output", "--version", "--help"],
6356
},
6457
]
65-
click.rich_click.OPTION_GROUPS = {
66-
"robotidy": CLI_OPTIONS_LIST,
67-
"python -m robotidy": CLI_OPTIONS_LIST,
68-
}
6958

70-
71-
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
59+
if RICH_PRESENT:
60+
click.rich_click.USE_RICH_MARKUP = True
61+
click.rich_click.USE_MARKDOWN = True
62+
click.rich_click.STYLE_OPTION = "bold sky_blue3"
63+
click.rich_click.STYLE_SWITCH = "bold sky_blue3"
64+
click.rich_click.STYLE_METAVAR = "bold white"
65+
click.rich_click.STYLE_OPTION_DEFAULT = "grey37"
66+
click.rich_click.STYLE_OPTIONS_PANEL_BORDER = "grey66"
67+
click.rich_click.STYLE_USAGE = "magenta"
68+
click.rich_click.OPTION_GROUPS = {
69+
"robotidy": CLI_OPTIONS_LIST,
70+
"python -m robotidy": CLI_OPTIONS_LIST,
71+
}
7272

7373

74-
class TransformType(click.ParamType):
75-
name = "transform"
76-
77-
def convert(self, value, param, ctx):
78-
name, args = split_args_from_name_or_path(value.replace(" ", ""))
79-
return name, args
74+
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
8075

8176

8277
def parse_opt(opt):
@@ -91,17 +86,17 @@ def validate_config_options(params, config):
9186
allowed = {parse_opt(opt) for param in params for opt in param.opts}
9287
for conf in config:
9388
if conf not in allowed:
94-
rec_finder = RecommendationFinder()
89+
rec_finder = utils.RecommendationFinder()
9590
similar = rec_finder.find(conf, list(allowed))
9691
raise click.NoSuchOption(conf, possibilities=similar)
9792

9893

9994
def read_config(ctx: click.Context, param: click.Parameter, value: Optional[str]) -> Optional[str]:
10095
# if --config was not used, try to find pyproject.toml or robotidy.toml file
10196
if value:
102-
config = read_pyproject_config(value)
97+
config = files.read_pyproject_config(value)
10398
else:
104-
config = find_and_read_config(ctx.params["src"] or (str(Path(".").resolve()),))
99+
config = files.find_and_read_config(ctx.params["src"] or (str(Path(".").resolve()),))
105100
if not config:
106101
return
107102
# Sanitize the values to be Click friendly. For more information please see:
@@ -123,7 +118,7 @@ def validate_regex_callback(
123118
param: click.Parameter,
124119
value: Optional[str],
125120
) -> Optional[Pattern]:
126-
return validate_regex(value)
121+
return utils.validate_regex(value)
127122

128123

129124
def validate_target_version(
@@ -132,22 +127,16 @@ def validate_target_version(
132127
value: Optional[str],
133128
) -> Optional[int]:
134129
if value is None:
135-
return ROBOT_VERSION.major
136-
version = TargetVersion[value.upper()].value
137-
if version > ROBOT_VERSION.major:
130+
return utils.ROBOT_VERSION.major
131+
version = utils.TargetVersion[value.upper()].value
132+
if version > utils.ROBOT_VERSION.major:
138133
raise click.BadParameter(
139-
f"Target Robot Framework version ({version}) should not be higher than installed version ({ROBOT_VERSION})."
134+
f"Target Robot Framework version ({version}) should not be higher than "
135+
f"installed version ({utils.ROBOT_VERSION})."
140136
)
141137
return version
142138

143139

144-
def validate_regex(value: Optional[str]) -> Optional[Pattern]:
145-
try:
146-
return re.compile(value) if value is not None else None
147-
except re.error:
148-
raise click.BadParameter("Not a valid regular expression")
149-
150-
151140
def csv_list_type(ctx: click.Context, param: Union[click.Option, click.Parameter], value: Optional[str]) -> List[str]:
152141
if not value:
153142
return []
@@ -161,6 +150,7 @@ def print_transformer_docs(transformer):
161150
console.print(md)
162151

163152

153+
@decorators.optional_rich
164154
def print_description(name: str, target_version: int):
165155
transformers = load_transformers(None, {}, allow_disabled=True, target_version=target_version)
166156
transformer_by_names = {transformer.name: transformer for transformer in transformers}
@@ -170,13 +160,14 @@ def print_description(name: str, target_version: int):
170160
elif name in transformer_by_names:
171161
print_transformer_docs(transformer_by_names[name])
172162
else:
173-
rec_finder = RecommendationFinder()
163+
rec_finder = utils.RecommendationFinder()
174164
similar = rec_finder.find_similar(name, transformer_by_names.keys())
175165
click.echo(f"Transformer with the name '{name}' does not exist.{similar}")
176166
return 1
177167
return 0
178168

179169

170+
@decorators.optional_rich
180171
def print_transformers_list(target_version: int):
181172
from rich.table import Table
182173

@@ -232,7 +223,7 @@ def print_transformers_list(target_version: int):
232223
" excluded on recursive searches. An empty value means no paths are excluded."
233224
" Use forward slashes for directories on all platforms."
234225
),
235-
show_default=f"{DEFAULT_EXCLUDES}",
226+
show_default=f"{files.DEFAULT_EXCLUDES}",
236227
)
237228
@click.option(
238229
"--extend-exclude",
@@ -388,7 +379,7 @@ def print_transformers_list(target_version: int):
388379
@click.option(
389380
"--target-version",
390381
"-tv",
391-
type=click.Choice([v.name.lower() for v in TargetVersion], case_sensitive=False),
382+
type=click.Choice([v.name.lower() for v in utils.TargetVersion], case_sensitive=False),
392383
callback=validate_target_version,
393384
help="Only enable transformers supported in set target version",
394385
show_default="installed Robot Framework version",
@@ -413,9 +404,9 @@ def print_transformers_list(target_version: int):
413404
@skip.return_option
414405
@skip.tags_option
415406
@skip.block_comments_option
416-
@click.version_option(version=__version__, prog_name="robotidy")
407+
@click.version_option(version=version.__version__, prog_name="robotidy")
417408
@click.pass_context
418-
@catch_exceptions
409+
@decorators.catch_exceptions
419410
def cli(
420411
ctx: click.Context,
421412
transform: List[Tuple[str, List]],
@@ -476,7 +467,7 @@ def cli(
476467
sys.exit(1)
477468

478469
if exclude is None:
479-
exclude = re.compile(DEFAULT_EXCLUDES)
470+
exclude = re.compile(files.DEFAULT_EXCLUDES)
480471

481472
if config and verbose:
482473
click.echo(f"Loaded {config} configuration file")
@@ -533,6 +524,6 @@ def cli(
533524
color=color,
534525
language=language,
535526
)
536-
tidy = Robotidy(config=config)
527+
tidy = app.Robotidy(config=config)
537528
status = tidy.transform_files()
538529
sys.exit(status)

0 commit comments

Comments
 (0)