Skip to content

Commit 66a94bc

Browse files
committed
fix(config): corrected handling of relative items in python path and other variables if current folder is different then root folder of the project
fixes #359
1 parent 2c8ed56 commit 66a94bc

File tree

8 files changed

+260
-208
lines changed

8 files changed

+260
-208
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import os
2+
from contextlib import AbstractContextManager
3+
from pathlib import Path
4+
from typing import Any, Callable, List, Literal, Optional, Union
5+
6+
from .path import same_file
7+
8+
9+
class ChDir(AbstractContextManager[Any]):
10+
def __init__(
11+
self, path: "Union[str, os.PathLike[str], None]", verbose_callback: Optional[Callable[[str], None]] = None
12+
) -> None:
13+
self.path = path
14+
self._old_cwd: List[Optional[Path]] = []
15+
self._verbose_callback = verbose_callback
16+
17+
def __enter__(self) -> Optional[Path]:
18+
result = Path.cwd()
19+
20+
if self.path is None or (self._old_cwd and same_file(self.path, Path.cwd())):
21+
self._old_cwd.append(None)
22+
else:
23+
self._old_cwd.append(result)
24+
25+
if self.path:
26+
if self._verbose_callback:
27+
self._verbose_callback(f"Changing directory to {self.path}")
28+
29+
os.chdir(self.path)
30+
31+
return result
32+
33+
def __exit__(self, _exc_type: Any, _exc_value: Any, _traceback: Any) -> Literal[False]:
34+
old_path = self._old_cwd.pop()
35+
if old_path is not None:
36+
if self._verbose_callback:
37+
self._verbose_callback(f"Changing directory back to {old_path}")
38+
39+
os.chdir(old_path)
40+
41+
return False
42+
43+
44+
chdir = ChDir

packages/plugin/src/robotcode/plugin/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import dataclasses
22
import sys
3+
from contextlib import contextmanager
34
from dataclasses import dataclass
45
from enum import Enum, unique
56
from pathlib import Path
@@ -9,6 +10,8 @@
910
AnyStr,
1011
Callable,
1112
Iterable,
13+
Iterator,
14+
Literal,
1215
Optional,
1316
Sequence,
1417
TypeVar,
@@ -20,6 +23,7 @@
2023
import pluggy
2124
import tomli_w
2225

26+
from robotcode.core.utils.contextlib import chdir
2327
from robotcode.core.utils.dataclasses import as_dict, as_json
2428

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

299+
@contextmanager
300+
def chdir(self, path: Union[str, Path, None]) -> Iterator[Optional[Path]]:
301+
with chdir(path, self.verbose) as result:
302+
yield result
303+
304+
@contextmanager
305+
def save_syspath(self) -> Iterator[Literal[None]]:
306+
self._syspath = sys.path[:]
307+
try:
308+
yield None
309+
finally:
310+
sys.path = self._syspath
311+
295312

296313
pass_application = click.make_pass_decorator(Application, ensure=True)

packages/robot/src/robotcode/robot/config/loader.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Any, Callable, Dict, Optional, Sequence, Tuple, Type, TypeVar, Union
66

77
from robotcode.core.utils.dataclasses import from_dict
8+
from robotcode.core.utils.path import normalized_path
89

910
if sys.version_info >= (3, 11):
1011
import tomllib
@@ -224,7 +225,7 @@ def find_project_root(
224225
if not sources:
225226
sources = (str(Path.cwd().absolute()),)
226227

227-
path_srcs = [Path(Path.cwd(), src).absolute() for src in sources]
228+
path_srcs = [normalized_path(Path(Path.cwd(), src).absolute()) for src in sources]
228229

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

packages/runner/src/robotcode/runner/cli/discover/discover.py

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -476,81 +476,84 @@ def handle_options(
476476
) -> Tuple[TestSuite, Collector, Optional[Dict[str, List[Diagnostic]]]]:
477477
root_folder, profile, cmd_options = handle_robot_options(app, robot_options_and_args)
478478

479-
diagnostics_logger = DiagnosticsLogger()
480-
try:
481-
_patch()
479+
with app.chdir(root_folder) as orig_folder:
482480

483-
options, arguments = RobotFrameworkEx(
484-
app,
485-
(
486-
[*(app.config.default_paths if app.config.default_paths else ())]
487-
if profile.paths is None
488-
else profile.paths if isinstance(profile.paths, list) else [profile.paths]
489-
),
490-
app.config.dry,
491-
root_folder,
492-
by_longname,
493-
exclude_by_longname,
494-
).parse_arguments((*cmd_options, "--runemptysuite", *robot_options_and_args))
481+
diagnostics_logger = DiagnosticsLogger()
482+
try:
483+
_patch()
495484

496-
settings = RobotSettings(options)
485+
options, arguments = RobotFrameworkEx(
486+
app,
487+
(
488+
[*(app.config.default_paths if app.config.default_paths else ())]
489+
if profile.paths is None
490+
else profile.paths if isinstance(profile.paths, list) else [profile.paths]
491+
),
492+
app.config.dry,
493+
root_folder,
494+
orig_folder,
495+
by_longname,
496+
exclude_by_longname,
497+
).parse_arguments((*cmd_options, "--runemptysuite", *robot_options_and_args))
497498

498-
if app.show_diagnostics:
499-
LOGGER.register_console_logger(**settings.console_output_config)
500-
else:
501-
LOGGER.unregister_console_logger()
502-
503-
LOGGER.register_logger(diagnostics_logger)
504-
505-
if get_robot_version() >= (5, 0):
506-
if settings.pythonpath:
507-
sys.path = settings.pythonpath + sys.path
508-
509-
if get_robot_version() > (6, 1):
510-
builder = TestSuiteBuilder(
511-
included_extensions=settings.extension,
512-
included_files=settings.parse_include,
513-
custom_parsers=settings.parsers,
514-
rpa=settings.rpa,
515-
lang=settings.languages,
516-
allow_empty_suite=settings.run_empty_suite,
517-
)
518-
elif get_robot_version() >= (6, 0):
519-
builder = TestSuiteBuilder(
520-
settings["SuiteNames"],
521-
included_extensions=settings.extension,
522-
rpa=settings.rpa,
523-
lang=settings.languages,
524-
allow_empty_suite=settings.run_empty_suite,
525-
)
526-
else:
527-
builder = TestSuiteBuilder(
528-
settings["SuiteNames"],
529-
included_extensions=settings.extension,
530-
rpa=settings.rpa,
531-
allow_empty_suite=settings.run_empty_suite,
532-
)
499+
settings = RobotSettings(options)
500+
501+
if app.show_diagnostics:
502+
LOGGER.register_console_logger(**settings.console_output_config)
503+
else:
504+
LOGGER.unregister_console_logger()
505+
506+
LOGGER.register_logger(diagnostics_logger)
507+
508+
if get_robot_version() >= (5, 0):
509+
if settings.pythonpath:
510+
sys.path = settings.pythonpath + sys.path
511+
512+
if get_robot_version() > (6, 1):
513+
builder = TestSuiteBuilder(
514+
included_extensions=settings.extension,
515+
included_files=settings.parse_include,
516+
custom_parsers=settings.parsers,
517+
rpa=settings.rpa,
518+
lang=settings.languages,
519+
allow_empty_suite=settings.run_empty_suite,
520+
)
521+
elif get_robot_version() >= (6, 0):
522+
builder = TestSuiteBuilder(
523+
settings["SuiteNames"],
524+
included_extensions=settings.extension,
525+
rpa=settings.rpa,
526+
lang=settings.languages,
527+
allow_empty_suite=settings.run_empty_suite,
528+
)
529+
else:
530+
builder = TestSuiteBuilder(
531+
settings["SuiteNames"],
532+
included_extensions=settings.extension,
533+
rpa=settings.rpa,
534+
allow_empty_suite=settings.run_empty_suite,
535+
)
533536

534-
suite = builder.build(*arguments)
535-
settings.rpa = suite.rpa
536-
if settings.pre_run_modifiers:
537-
suite.visit(ModelModifier(settings.pre_run_modifiers, settings.run_empty_suite, LOGGER))
538-
suite.configure(**settings.suite_config)
537+
suite = builder.build(*arguments)
538+
settings.rpa = suite.rpa
539+
if settings.pre_run_modifiers:
540+
suite.visit(ModelModifier(settings.pre_run_modifiers, settings.run_empty_suite, LOGGER))
541+
suite.configure(**settings.suite_config)
539542

540-
collector = Collector()
543+
collector = Collector()
541544

542-
suite.visit(collector)
545+
suite.visit(collector)
543546

544-
return suite, collector, build_diagnostics(diagnostics_logger.messages)
547+
return suite, collector, build_diagnostics(diagnostics_logger.messages)
545548

546-
except Information as err:
547-
app.echo(str(err))
548-
app.exit(INFO_PRINTED)
549-
except DataError as err:
550-
app.error(str(err))
551-
app.exit(DATA_ERROR)
549+
except Information as err:
550+
app.echo(str(err))
551+
app.exit(INFO_PRINTED)
552+
except DataError as err:
553+
app.error(str(err))
554+
app.exit(DATA_ERROR)
552555

553-
raise UnknownError("Unexpected error happened.")
556+
raise UnknownError("Unexpected error happened.")
554557

555558

556559
def print_statistics(app: Application, suite: TestSuite, collector: Collector) -> None:

packages/runner/src/robotcode/runner/cli/libdoc.py

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import os
2-
from pathlib import Path
3-
from typing import Any, Optional, Tuple, cast
1+
from typing import Any, Tuple, cast
42

53
import click
64
from robot.errors import DataError, Information
@@ -16,10 +14,9 @@
1614

1715

1816
class LibDocEx(LibDoc):
19-
def __init__(self, dry: bool, root_folder: Optional[Path]) -> None:
17+
def __init__(self, dry: bool) -> None:
2018
super().__init__()
2119
self.dry = dry
22-
self.root_folder = root_folder
2320

2421
def parse_arguments(self, cli_args: Any) -> Any:
2522
options, arguments = super().parse_arguments(cli_args)
@@ -35,9 +32,6 @@ def parse_arguments(self, cli_args: Any) -> Any:
3532
return options, arguments
3633

3734
def main(self, arguments: Any, **options: Any) -> Any:
38-
if self.root_folder is not None:
39-
os.chdir(self.root_folder)
40-
4135
return super().main(arguments, **options)
4236

4337

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

6357
robot_arguments = None
6458
try:
65-
_, robot_arguments = LibDoc().parse_arguments(robot_options_and_args)
59+
with app.save_syspath():
60+
_, robot_arguments = LibDoc().parse_arguments(robot_options_and_args)
6661
except (DataError, Information):
6762
pass
6863

@@ -73,32 +68,34 @@ def libdoc(app: Application, robot_options_and_args: Tuple[str, ...]) -> None:
7368
no_vcs=app.config.no_vcs,
7469
verbose_callback=app.verbose,
7570
)
76-
try:
77-
profile = (
78-
load_robot_config_from_path(*config_files, verbose_callback=app.verbose)
79-
.combine_profiles(*(app.config.profiles or []), verbose_callback=app.verbose, error_callback=app.error)
80-
.evaluated_with_env(verbose_callback=app.verbose, error_callback=app.error)
81-
)
8271

83-
except (TypeError, ValueError) as e:
84-
raise click.ClickException(str(e)) from e
72+
with app.chdir(root_folder):
73+
try:
74+
profile = (
75+
load_robot_config_from_path(*config_files, verbose_callback=app.verbose)
76+
.combine_profiles(*(app.config.profiles or []), verbose_callback=app.verbose, error_callback=app.error)
77+
.evaluated_with_env(verbose_callback=app.verbose, error_callback=app.error)
78+
)
8579

86-
libdoc_options = profile.libdoc
87-
if libdoc_options is None:
88-
libdoc_options = LibDocProfile()
80+
except (TypeError, ValueError) as e:
81+
raise click.ClickException(str(e)) from e
8982

90-
libdoc_options.add_options(profile)
83+
libdoc_options = profile.libdoc
84+
if libdoc_options is None:
85+
libdoc_options = LibDocProfile()
9186

92-
options = libdoc_options.build_command_line()
87+
libdoc_options.add_options(profile)
9388

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

99-
app.exit(
100-
cast(
101-
int,
102-
LibDocEx(app.config.dry, root_folder).execute_cli((*options, *robot_options_and_args), exit=False),
91+
app.verbose(
92+
lambda: "Executing libdoc robot with the following options:\n "
93+
+ " ".join(f'"{o}"' for o in (options + list(robot_options_and_args)))
94+
)
95+
96+
app.exit(
97+
cast(
98+
int,
99+
LibDocEx(app.config.dry).execute_cli((*options, *robot_options_and_args), exit=False),
100+
)
103101
)
104-
)

0 commit comments

Comments
 (0)