Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions robotidy/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
Methods for transforming Robot Framework ast model programmatically.
"""
from typing import Optional

from robotidy.app import Robotidy
from robotidy.cli import find_and_read_config, TransformType
from robotidy.utils import GlobalFormattingConfig


class RobotidyAPI(Robotidy):
def __init__(self, src: str, **kwargs):
config = find_and_read_config([src])
config = {
k: str(v) if not isinstance(v, (list, dict)) else v
for k, v in config.items()
}
converter = TransformType()
transformers = [converter.convert(tr, None, None) for tr in config.get('transform', ())]
configurations = [converter.convert(c, None, None) for c in config.get('configure', ())]
formatting_config = GlobalFormattingConfig(
space_count=kwargs.get('spacecount', None) or int(config.get('spacecount', 4)),
line_sep=config.get('lineseparator', 'native'),
start_line=kwargs.get('startline', None) or int(config['startline']) if 'startline' in config else None,
end_line=kwargs.get('endline', None) or int(config['endline']) if 'endline' in config else None
)
super().__init__(
transformers=transformers,
transformers_config=configurations,
src=(),
overwrite=False,
show_diff=False,
formatting_config=formatting_config,
verbose=False,
check=False
)


def transform_model(model, root_dir: str, **kwargs) -> Optional[str]:
"""
:param model: The model to be transformed.
:param root_dir: Root directory. Configuration file is searched based
on this directory or one of its parents.
:param kwargs: Default values for global formatting parameters
such as ``spacecount``, ``startline`` and ``endline``.
:return: The transformed model converted to string or None if no transformation took place.
"""
transformer = RobotidyAPI(root_dir, **kwargs)
diff, _, new_model = transformer.transform(model)
if not diff:
return None
return new_model.text
61 changes: 50 additions & 11 deletions robotidy/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from typing import List, Tuple, Dict, Set, Any
from collections import defaultdict
from difflib import unified_diff
from pathlib import Path
from typing import List, Tuple, Dict, Iterator, Iterable

import click
from robot.api import get_model
Expand All @@ -12,25 +14,31 @@
GlobalFormattingConfig
)

INCLUDE_EXT = ('.robot', '.resource')


class Robotidy:
def __init__(self,
transformers: List[Tuple[str, List]],
transformers_config: Dict[str, List],
src: Set,
transformers_config: List[Tuple[str, List]],
src: Tuple[str, ...],
overwrite: bool,
show_diff: bool,
formatting_config: GlobalFormattingConfig,
verbose: bool,
check: bool
):
self.sources = src
self.sources = self.get_paths(src)
self.overwrite = overwrite
self.show_diff = show_diff
self.check = check
self.verbose = verbose
self.formatting_config = formatting_config
transformers_config = self.convert_configure(transformers_config)
self.transformers = load_transformers(transformers, transformers_config)
for transformer in self.transformers:
# inject global settings TODO: handle it better
setattr(transformer, 'formatting_config', self.formatting_config)

def transform_files(self):
changed_files = 0
Expand All @@ -39,13 +47,8 @@ def transform_files(self):
if self.verbose:
click.echo(f'Transforming {source} file')
model = get_model(source)
old_model = StatementLinesCollector(model)
for transformer in self.transformers:
# inject global settings TODO: handle it better
setattr(transformer, 'formatting_config', self.formatting_config)
transformer.visit(model)
new_model = StatementLinesCollector(model)
if new_model != old_model:
diff, old_model, new_model = self.transform(model)
if diff:
changed_files += 1
self.output_diff(model.source, old_model, new_model)
if not self.check:
Expand All @@ -59,6 +62,13 @@ def transform_files(self):
return 0
return 1

def transform(self, model):
old_model = StatementLinesCollector(model)
for transformer in self.transformers:
transformer.visit(model)
new_model = StatementLinesCollector(model)
return new_model != old_model, old_model, new_model

def save_model(self, model):
if self.overwrite:
model.save()
Expand All @@ -71,3 +81,32 @@ def output_diff(self, path: str, old_model: StatementLinesCollector, new_model:
lines = list(unified_diff(old, new, fromfile=f'{path}\tbefore', tofile=f'{path}\tafter'))
colorized_output = decorate_diff_with_color(lines)
click.echo(colorized_output.encode('ascii', 'ignore').decode('ascii'), color=True)

def get_paths(self, src: Tuple[str, ...]):
sources = set()
for s in src:
path = Path(s).resolve()
if path.is_file():
sources.add(path)
elif path.is_dir():
sources.update(self.iterate_dir(path.iterdir()))
elif s == '-':
sources.add(path)

return sources

def iterate_dir(self, paths: Iterable[Path]) -> Iterator[Path]:
for path in paths:
if path.is_file():
if path.suffix not in INCLUDE_EXT:
continue
yield path
elif path.is_dir():
yield from self.iterate_dir(path.iterdir())

@staticmethod
def convert_configure(configure: List[Tuple[str, List]]) -> Dict[str, List]:
config_map = defaultdict(list)
for transformer, args in configure:
config_map[transformer].extend(args)
return config_map
46 changes: 6 additions & 40 deletions robotidy/cli.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
from pathlib import Path
from typing import (
Tuple,
Dict,
List,
Iterator,
Iterable,
Optional,
Any
)
from pathlib import Path

import click
import toml
from collections import defaultdict

from robotidy.version import __version__
from robotidy.app import Robotidy
from robotidy.transformers import load_transformers
from robotidy.utils import (
GlobalFormattingConfig,
split_args_from_name_or_path,
remove_rst_formatting
)
from robotidy.version import __version__


INCLUDE_EXT = ('.robot', '.resource')
HELP_MSG = f"""
Version: {__version__}

Expand Down Expand Up @@ -81,13 +79,6 @@ def convert(self, value, param, ctx):
return name, args


def convert_configure(configure: List[Tuple[str, List]]) -> Dict[str, List]:
config_map = defaultdict(list)
for transformer, args in configure:
config_map[transformer].extend(args)
return config_map


def find_project_root(srcs: Iterable[str]) -> Path:
"""Return a directory containing .git, or robotidy.toml.
That directory will be a common parent of all files and directories
Expand Down Expand Up @@ -132,6 +123,7 @@ def find_and_read_config(src_paths: Iterable[str]) -> Dict[str, Any]:
pyproject_path = project_root / 'pyproject.toml'
if pyproject_path.is_file():
return read_pyproject_config(str(pyproject_path))
return {}


def load_toml_file(path: str) -> Dict[str, Any]:
Expand Down Expand Up @@ -180,30 +172,6 @@ def read_config(ctx: click.Context, param: click.Parameter, value: Optional[str]
ctx.default_map = default_map


def iterate_dir(paths: Iterable[Path]) -> Iterator[Path]:
for path in paths:
if path.is_file():
if path.suffix not in INCLUDE_EXT:
continue
yield path
elif path.is_dir():
yield from iterate_dir(path.iterdir())


def get_paths(src: Tuple[str, ...]):
sources = set()
for s in src:
path = Path(s).resolve()
if path.is_file():
sources.add(path)
elif path.is_dir():
sources.update(iterate_dir(path.iterdir()))
elif s == '-':
sources.add(path)

return sources


@click.command(cls=RawHelp, help=HELP_MSG, epilog=EPILOG)
@click.option(
'--transform',
Expand Down Expand Up @@ -366,12 +334,10 @@ def cli(
start_line=startline,
end_line=endline
)
sources = get_paths(src)
configure_transform = convert_configure(configure)
tidy = Robotidy(
transformers=transform,
transformers_config=configure_transform,
src=sources,
transformers_config=configure,
src=src,
overwrite=overwrite,
show_diff=diff,
formatting_config=formatting_config,
Expand Down
46 changes: 46 additions & 0 deletions tests/utest/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from pathlib import Path

from robot.api import get_model

from robotidy.api import transform_model


class TestAPI:
def test_load_pyproject_and_transform(self):
expected = "*** Settings ***\n" \
"\n\n" \
"*** Test Cases ***\n" \
"Test\n" \
" [Documentation] doc\n" \
" [Tags] sometag\n" \
" Pass\n" \
" Keyword\n" \
" One More\n" \
"\n\n" \
"*** Comments ***\n" \
"robocop: disable=all"
config_path = str(Path(Path(__file__).parent, 'testdata', 'only_pyproject'))
source = str(Path(Path(__file__).parent.parent, 'atest', 'transformers', 'DiscardEmptySections', 'source',
'removes_empty_sections.robot'))
model = get_model(source)
transformed = transform_model(model, config_path)
assert transformed == expected

def test_with_default_parameters(self):
expected = "*** Comments ***\n" \
"robocop: disable=all\n" \
"\n" \
"*** Test Cases ***\n" \
"Test\n" \
" [Documentation] doc\n" \
" [Tags] sometag\n" \
" Pass\n" \
" Keyword\n" \
" One More\n"

config_path = '.'
source = str(Path(Path(__file__).parent.parent, 'atest', 'transformers', 'DiscardEmptySections', 'source',
'removes_empty_sections.robot'))
model = get_model(source)
transformed = transform_model(model, config_path, spacecount=8, linestart=10, endline=20)
assert transformed == expected
2 changes: 2 additions & 0 deletions tests/utest/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def test_read_pyproject_config(self):
'overwrite': False,
'diff': False,
'startline': 10,
'endline': 20,
'transform': [
'DiscardEmptySections:allow_only_comments=True',
'SplitTooLongLine'
Expand All @@ -104,6 +105,7 @@ def test_read_pyproject_config_e2e(self):
'overwrite': 'False',
'diff': 'False',
'startline': '10',
'endline': '20',
'transform': [
'DiscardEmptySections:allow_only_comments=True',
'SplitTooLongLine'
Expand Down
1 change: 1 addition & 0 deletions tests/utest/testdata/only_pyproject/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
overwrite = false
diff = false
startline = 10
endline = 20
transform = [
"DiscardEmptySections:allow_only_comments=True",
"SplitTooLongLine"
Expand Down