Skip to content

Commit

Permalink
fix(config): corrected handling of relative items in python path and …
Browse files Browse the repository at this point in the history
…other variables if current folder is different then root folder of the project

fixes #359
  • Loading branch information
d-biehl committed Nov 26, 2024
1 parent 2c8ed56 commit 66a94bc
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 208 deletions.
44 changes: 44 additions & 0 deletions packages/core/src/robotcode/core/utils/contextlib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import os
from contextlib import AbstractContextManager
from pathlib import Path
from typing import Any, Callable, List, Literal, Optional, Union

from .path import same_file


class ChDir(AbstractContextManager[Any]):
def __init__(
self, path: "Union[str, os.PathLike[str], None]", verbose_callback: Optional[Callable[[str], None]] = None
) -> None:
self.path = path
self._old_cwd: List[Optional[Path]] = []
self._verbose_callback = verbose_callback

def __enter__(self) -> Optional[Path]:
result = Path.cwd()

if self.path is None or (self._old_cwd and same_file(self.path, Path.cwd())):
self._old_cwd.append(None)
else:
self._old_cwd.append(result)

if self.path:
if self._verbose_callback:
self._verbose_callback(f"Changing directory to {self.path}")

os.chdir(self.path)

return result

def __exit__(self, _exc_type: Any, _exc_value: Any, _traceback: Any) -> Literal[False]:
old_path = self._old_cwd.pop()
if old_path is not None:
if self._verbose_callback:
self._verbose_callback(f"Changing directory back to {old_path}")

os.chdir(old_path)

return False


chdir = ChDir
17 changes: 17 additions & 0 deletions packages/plugin/src/robotcode/plugin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import dataclasses
import sys
from contextlib import contextmanager
from dataclasses import dataclass
from enum import Enum, unique
from pathlib import Path
Expand All @@ -9,6 +10,8 @@
AnyStr,
Callable,
Iterable,
Iterator,
Literal,
Optional,
Sequence,
TypeVar,
Expand All @@ -20,6 +23,7 @@
import pluggy
import tomli_w

from robotcode.core.utils.contextlib import chdir
from robotcode.core.utils.dataclasses import as_dict, as_json

__all__ = [
Expand Down Expand Up @@ -292,5 +296,18 @@ def exit(self, code: int = 0) -> None:
self.verbose(f"Exit with code {code}")
sys.exit(code)

@contextmanager
def chdir(self, path: Union[str, Path, None]) -> Iterator[Optional[Path]]:
with chdir(path, self.verbose) as result:
yield result

@contextmanager
def save_syspath(self) -> Iterator[Literal[None]]:
self._syspath = sys.path[:]
try:
yield None
finally:
sys.path = self._syspath


pass_application = click.make_pass_decorator(Application, ensure=True)
3 changes: 2 additions & 1 deletion packages/robot/src/robotcode/robot/config/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Any, Callable, Dict, Optional, Sequence, Tuple, Type, TypeVar, Union

from robotcode.core.utils.dataclasses import from_dict
from robotcode.core.utils.path import normalized_path

if sys.version_info >= (3, 11):
import tomllib
Expand Down Expand Up @@ -224,7 +225,7 @@ def find_project_root(
if not sources:
sources = (str(Path.cwd().absolute()),)

path_srcs = [Path(Path.cwd(), src).absolute() for src in sources]
path_srcs = [normalized_path(Path(Path.cwd(), src).absolute()) for src in sources]

src_parents = [list(path.parents) + ([path] if path.is_dir() else []) for path in path_srcs]

Expand Down
135 changes: 69 additions & 66 deletions packages/runner/src/robotcode/runner/cli/discover/discover.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,81 +476,84 @@ def handle_options(
) -> Tuple[TestSuite, Collector, Optional[Dict[str, List[Diagnostic]]]]:
root_folder, profile, cmd_options = handle_robot_options(app, robot_options_and_args)

diagnostics_logger = DiagnosticsLogger()
try:
_patch()
with app.chdir(root_folder) as orig_folder:

options, arguments = RobotFrameworkEx(
app,
(
[*(app.config.default_paths if app.config.default_paths else ())]
if profile.paths is None
else profile.paths if isinstance(profile.paths, list) else [profile.paths]
),
app.config.dry,
root_folder,
by_longname,
exclude_by_longname,
).parse_arguments((*cmd_options, "--runemptysuite", *robot_options_and_args))
diagnostics_logger = DiagnosticsLogger()
try:
_patch()

settings = RobotSettings(options)
options, arguments = RobotFrameworkEx(
app,
(
[*(app.config.default_paths if app.config.default_paths else ())]
if profile.paths is None
else profile.paths if isinstance(profile.paths, list) else [profile.paths]
),
app.config.dry,
root_folder,
orig_folder,
by_longname,
exclude_by_longname,
).parse_arguments((*cmd_options, "--runemptysuite", *robot_options_and_args))

if app.show_diagnostics:
LOGGER.register_console_logger(**settings.console_output_config)
else:
LOGGER.unregister_console_logger()

LOGGER.register_logger(diagnostics_logger)

if get_robot_version() >= (5, 0):
if settings.pythonpath:
sys.path = settings.pythonpath + sys.path

if get_robot_version() > (6, 1):
builder = TestSuiteBuilder(
included_extensions=settings.extension,
included_files=settings.parse_include,
custom_parsers=settings.parsers,
rpa=settings.rpa,
lang=settings.languages,
allow_empty_suite=settings.run_empty_suite,
)
elif get_robot_version() >= (6, 0):
builder = TestSuiteBuilder(
settings["SuiteNames"],
included_extensions=settings.extension,
rpa=settings.rpa,
lang=settings.languages,
allow_empty_suite=settings.run_empty_suite,
)
else:
builder = TestSuiteBuilder(
settings["SuiteNames"],
included_extensions=settings.extension,
rpa=settings.rpa,
allow_empty_suite=settings.run_empty_suite,
)
settings = RobotSettings(options)

if app.show_diagnostics:
LOGGER.register_console_logger(**settings.console_output_config)
else:
LOGGER.unregister_console_logger()

LOGGER.register_logger(diagnostics_logger)

if get_robot_version() >= (5, 0):
if settings.pythonpath:
sys.path = settings.pythonpath + sys.path

if get_robot_version() > (6, 1):
builder = TestSuiteBuilder(
included_extensions=settings.extension,
included_files=settings.parse_include,
custom_parsers=settings.parsers,
rpa=settings.rpa,
lang=settings.languages,
allow_empty_suite=settings.run_empty_suite,
)
elif get_robot_version() >= (6, 0):
builder = TestSuiteBuilder(
settings["SuiteNames"],
included_extensions=settings.extension,
rpa=settings.rpa,
lang=settings.languages,
allow_empty_suite=settings.run_empty_suite,
)
else:
builder = TestSuiteBuilder(
settings["SuiteNames"],
included_extensions=settings.extension,
rpa=settings.rpa,
allow_empty_suite=settings.run_empty_suite,
)

suite = builder.build(*arguments)
settings.rpa = suite.rpa
if settings.pre_run_modifiers:
suite.visit(ModelModifier(settings.pre_run_modifiers, settings.run_empty_suite, LOGGER))
suite.configure(**settings.suite_config)
suite = builder.build(*arguments)
settings.rpa = suite.rpa
if settings.pre_run_modifiers:
suite.visit(ModelModifier(settings.pre_run_modifiers, settings.run_empty_suite, LOGGER))
suite.configure(**settings.suite_config)

collector = Collector()
collector = Collector()

suite.visit(collector)
suite.visit(collector)

return suite, collector, build_diagnostics(diagnostics_logger.messages)
return suite, collector, build_diagnostics(diagnostics_logger.messages)

except Information as err:
app.echo(str(err))
app.exit(INFO_PRINTED)
except DataError as err:
app.error(str(err))
app.exit(DATA_ERROR)
except Information as err:
app.echo(str(err))
app.exit(INFO_PRINTED)
except DataError as err:
app.error(str(err))
app.exit(DATA_ERROR)

raise UnknownError("Unexpected error happened.")
raise UnknownError("Unexpected error happened.")


def print_statistics(app: Application, suite: TestSuite, collector: Collector) -> None:
Expand Down
59 changes: 28 additions & 31 deletions packages/runner/src/robotcode/runner/cli/libdoc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import os
from pathlib import Path
from typing import Any, Optional, Tuple, cast
from typing import Any, Tuple, cast

import click
from robot.errors import DataError, Information
Expand All @@ -16,10 +14,9 @@


class LibDocEx(LibDoc):
def __init__(self, dry: bool, root_folder: Optional[Path]) -> None:
def __init__(self, dry: bool) -> None:
super().__init__()
self.dry = dry
self.root_folder = root_folder

def parse_arguments(self, cli_args: Any) -> Any:
options, arguments = super().parse_arguments(cli_args)
Expand All @@ -35,9 +32,6 @@ def parse_arguments(self, cli_args: Any) -> Any:
return options, arguments

def main(self, arguments: Any, **options: Any) -> Any:
if self.root_folder is not None:
os.chdir(self.root_folder)

return super().main(arguments, **options)


Expand All @@ -62,7 +56,8 @@ def libdoc(app: Application, robot_options_and_args: Tuple[str, ...]) -> None:

robot_arguments = None
try:
_, robot_arguments = LibDoc().parse_arguments(robot_options_and_args)
with app.save_syspath():
_, robot_arguments = LibDoc().parse_arguments(robot_options_and_args)
except (DataError, Information):
pass

Expand All @@ -73,32 +68,34 @@ def libdoc(app: Application, robot_options_and_args: Tuple[str, ...]) -> None:
no_vcs=app.config.no_vcs,
verbose_callback=app.verbose,
)
try:
profile = (
load_robot_config_from_path(*config_files, verbose_callback=app.verbose)
.combine_profiles(*(app.config.profiles or []), verbose_callback=app.verbose, error_callback=app.error)
.evaluated_with_env(verbose_callback=app.verbose, error_callback=app.error)
)

except (TypeError, ValueError) as e:
raise click.ClickException(str(e)) from e
with app.chdir(root_folder):
try:
profile = (
load_robot_config_from_path(*config_files, verbose_callback=app.verbose)
.combine_profiles(*(app.config.profiles or []), verbose_callback=app.verbose, error_callback=app.error)
.evaluated_with_env(verbose_callback=app.verbose, error_callback=app.error)
)

libdoc_options = profile.libdoc
if libdoc_options is None:
libdoc_options = LibDocProfile()
except (TypeError, ValueError) as e:
raise click.ClickException(str(e)) from e

libdoc_options.add_options(profile)
libdoc_options = profile.libdoc
if libdoc_options is None:
libdoc_options = LibDocProfile()

options = libdoc_options.build_command_line()
libdoc_options.add_options(profile)

app.verbose(
lambda: "Executing libdoc robot with the following options:\n "
+ " ".join(f'"{o}"' for o in (options + list(robot_options_and_args)))
)
options = libdoc_options.build_command_line()

app.exit(
cast(
int,
LibDocEx(app.config.dry, root_folder).execute_cli((*options, *robot_options_and_args), exit=False),
app.verbose(
lambda: "Executing libdoc robot with the following options:\n "
+ " ".join(f'"{o}"' for o in (options + list(robot_options_and_args)))
)

app.exit(
cast(
int,
LibDocEx(app.config.dry).execute_cli((*options, *robot_options_and_args), exit=False),
)
)
)
Loading

0 comments on commit 66a94bc

Please sign in to comment.