Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix --config and --rootdir inconsistencies #176

Merged
merged 3 commits into from
Dec 22, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ keywords = [
]
dependencies = [
"importlib-metadata-argparse-version",
"contextlib-chdir",
"tomli-w~=1.0",
'pyjson5',
"colored",
Expand Down
12 changes: 12 additions & 0 deletions src/project_config/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def build_main_parser() -> argparse.ArgumentParser: # noqa: D103
"--root",
"--rootdir",
dest="rootdir",
default=os.getcwd(),
type=str,
help=(
"Root directory of the project. Useful if you want to"
Expand Down Expand Up @@ -243,6 +244,17 @@ def parse_cli_args_and_subargs( # noqa: D103

def parse_args(argv: list[str]) -> argparse.Namespace: # noqa: D103
args, subargs = parse_cli_args_and_subargs(build_main_parser(), argv)

# Manage config and rootdir paths
if not os.path.isabs(args.rootdir):
args.rootdir = os.path.abspath(
os.path.relpath(args.rootdir, os.getcwd()),
)
if args.config is not None and not os.path.isabs(args.config):
args.config = os.path.abspath(
os.path.relpath(args.config, os.getcwd()),
)

return argparse.Namespace(**vars(args), **vars(subargs))


Expand Down
11 changes: 7 additions & 4 deletions src/project_config/commands/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import shutil
from typing import TYPE_CHECKING, Any

from contextlib_chdir import chdir as chdir_ctx

from project_config import tree
from project_config.config import Config, reporter_from_config
from project_config.constants import Error, InterruptingError, ResultValue
Expand Down Expand Up @@ -314,7 +316,8 @@ def check(args: argparse.Namespace) -> None: # noqa: U100

Raises errors if reported.
"""
ProjectConfigChecker(
Config(args),
fix_mode=args.command == "fix",
).run()
with chdir_ctx(args.rootdir):
ProjectConfigChecker(
Config(args),
fix_mode=args.command == "fix",
).run()
20 changes: 19 additions & 1 deletion src/project_config/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ def read_config(
"""
if custom_fpath:
if not os.path.isfile(custom_fpath):
raise CustomConfigFileNotFound(custom_fpath)
raise CustomConfigFileNotFound(
os.path.relpath(custom_fpath, rootdir),
)
tree.cache_file(custom_fpath)
return custom_fpath, tree.cached_local_file(
custom_fpath,
Expand Down Expand Up @@ -131,8 +133,19 @@ def validate_config_style(config: Any) -> list[str]:
)
elif not style:
error_messages.append(f"style[{i}] -> must not be empty")
else:
fpath = os.path.join(
os.path.dirname(config["_path"]),
style,
)
if os.path.isfile(fpath):
config["style"][i] = fpath
elif not config["style"]:
error_messages.append("style -> must not be empty")
else:
fpath = os.path.join(os.path.dirname(config["_path"]), config["style"])
if os.path.isfile(fpath):
config["style"] = fpath
return error_messages


Expand Down Expand Up @@ -284,12 +297,17 @@ def __init__(

self.raw_: RawConfigType = copy.deepcopy(config)

# Temporally store configuration path to use in validation
config["_path"] = self.path

validate_config(self.path, config)
config["cache"] = _cache_string_to_seconds(config["cache"])

# cli configuration in file
config["cli"] = validate_cli_config(self.path, config.get("cli", {}))

del config["_path"]

# set the cache expiration time globally
Cache.set_expiration_time(config["cache"])

Expand Down
4 changes: 3 additions & 1 deletion src/project_config/config/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ class CustomConfigFileNotFound(ProjectConfigInvalidConfig):
"""A custom configuration file has not been found."""

def __init__(self, fpath: str) -> None:
super().__init__(f"Custom configuration file '{fpath}' not found")
super().__init__(
f"Custom configuration file '{fpath}' not found",
)


class PyprojectTomlFoundButHasNoConfig(ProjectConfigInvalidConfig):
Expand Down
71 changes: 31 additions & 40 deletions tests/test_acceptance/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,57 +70,52 @@ def _run_example(
expected_stderr,
fixable,
interface,
chdir,
capsys,
tmp_path,
fake_cli_namespace,
):
if interface == "CLI":
with chdir(example_dir):
exitcode = run(["--nocolor", "check"])
out, err = capsys.readouterr()
assert exitcode == expected_exitcode, err
assert err == expected_stderr
assert out == ""
exitcode = run(["--nocolor", "--rootdir", example_dir, "check"])
out, err = capsys.readouterr()
assert exitcode == expected_exitcode, err
assert err == expected_stderr
assert out == ""

temp_example_dir = str(tmp_path / os.path.basename(example_dir))
temp_example_dir = shutil.copytree(
example_dir,
tmp_path / os.path.basename(example_dir),
temp_example_dir,
)

if exitcode == 0:
with chdir(temp_example_dir):
exitcode = run(["--nocolor", "fix"])
out, err = capsys.readouterr()
assert exitcode == 0, err
assert err == ""
assert out == ""
exitcode = run(["--nocolor", "--rootdir", temp_example_dir, "fix"])
out, err = capsys.readouterr()
assert exitcode == 0, err
assert err == ""
assert out == ""
elif fixable:
with chdir(temp_example_dir):
exitcode = run(["--nocolor", "fix"])
out, err = capsys.readouterr()
msg = f"{out}\n---\n{err}"
assert out == "", msg
assert "(FIXED)" in err, msg
assert exitcode == 1, msg

with chdir(temp_example_dir):
exitcode = run(["--nocolor", "fix"])
out, err = capsys.readouterr()
msg = f"{out}\n---\n{err}"
assert out == "", msg
assert err == "", msg
assert exitcode == 0, msg
exitcode = run(["--nocolor", "--rootdir", temp_example_dir, "fix"])
out, err = capsys.readouterr()
msg = f"{out}\n---\n{err}"
assert out == "", msg
assert "(FIXED)" in err, msg
assert exitcode == 1, msg

exitcode = run(["--nocolor", "--rootdir", temp_example_dir, "fix"])
out, err = capsys.readouterr()
msg = f"{out}\n---\n{err}"
assert out == "", msg
assert err == "", msg
assert exitcode == 0, msg

else:
with chdir(example_dir):
namespace = fake_cli_namespace(rootdir=example_dir, color=False)
if expected_stderr:
with pytest.raises(ProjectConfigException) as exc:
check(namespace)
assert str(exc.value) == expected_stderr.rstrip("\n")
else:
namespace = fake_cli_namespace(rootdir=example_dir, color=False)
if expected_stderr:
with pytest.raises(ProjectConfigException) as exc:
check(namespace)
assert str(exc.value) == expected_stderr.rstrip("\n")
else:
check(namespace)


@pytest.mark.parametrize("interface", ("CLI", "API"))
Expand All @@ -134,7 +129,6 @@ def test_examples(
expected_stderr,
fixable,
interface,
chdir,
capsys,
tmp_path,
fake_cli_namespace,
Expand All @@ -150,7 +144,6 @@ def test_examples(
expected_stderr,
fixable,
interface,
chdir,
capsys,
tmp_path,
fake_cli_namespace,
Expand All @@ -169,7 +162,6 @@ def test_examples_with_online_sources_check(
expected_stderr,
fixable,
interface,
chdir,
capsys,
tmp_path,
fake_cli_namespace,
Expand All @@ -180,7 +172,6 @@ def test_examples_with_online_sources_check(
expected_stderr,
fixable,
interface,
chdir,
capsys,
tmp_path,
fake_cli_namespace,
Expand Down
15 changes: 12 additions & 3 deletions tests/test_unit/test_config/test_validate_config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os

import pytest

from project_config.config import (
Expand Down Expand Up @@ -82,12 +84,19 @@
)


def cfg_with_path(config):
config["_path"] = os.getcwd()
return config


@pytest.mark.parametrize(
("config", "expected_error_messages"),
VALIDATE_CONFIG_STYLE_CASES,
)
def test_validate_config_style(config, expected_error_messages):
assert validate_config_style(config) == expected_error_messages
assert (
validate_config_style(cfg_with_path(config)) == expected_error_messages
)


@pytest.mark.parametrize(
Expand All @@ -104,10 +113,10 @@ def test_validate_config_cache(config, expected_error_messages):
)
def test_validate_config(config, expected_error_messages):
if not expected_error_messages:
validate_config("foo", config)
validate_config("foo", cfg_with_path(config))
else:
with pytest.raises(ProjectConfigInvalidConfigSchema) as exc_info:
validate_config("foo", config)
validate_config("foo", cfg_with_path(config))

# replace because there are escape characters in regexes
exc_messages = str(exc_info.value).replace("\\\\", "\\")
Expand Down