From 804a8321d0e62b8d750c2609ec4c307b4ead713e Mon Sep 17 00:00:00 2001 From: Johan Herland Date: Thu, 27 Jul 2023 16:45:52 +0200 Subject: [PATCH] Fix Settings.pyenvs to _actually_ use basepath by default This is a rather embarrasing omission from PR #326 (Teach FawltyDeps to automatically discover Python environments inside the project): Although that PR did include code to traverse within a given --pyenv path to find Python environments, the PR "forgot" to actually switch the _default_ behavior of --pyenv: The default value of settings.pyenvs remained an empty set, and there were no changes to have basepath influence the value of settings.pyenvs. This commit fixes that: - When neither --pyenv nor basepath is given, settings.pyenvs should default to the current directory. - When --pyenv is not given, but basepath is given, basepath should override the default settings.pyenvs. - When --pyenv is given, it overrides basepath. In short settings.pyenvs should behave exactly like .code and .deps. --- fawltydeps/packages.py | 2 +- fawltydeps/settings.py | 16 +++++++++++----- tests/test_cmdline.py | 4 ++-- tests/test_settings.py | 29 ++++++++++++++++++----------- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/fawltydeps/packages.py b/fawltydeps/packages.py index 619954a3..dc3406f6 100644 --- a/fawltydeps/packages.py +++ b/fawltydeps/packages.py @@ -342,7 +342,7 @@ def pyenv_sources(*pyenv_paths: Path) -> Set[PyEnvSource]: for path in pyenv_paths: package_dirs = set(LocalPackageResolver.find_package_dirs(path)) if not package_dirs: - logger.warning(f"Could not find a Python env at {path}!") + logger.debug(f"Could not find a Python env at {path}!") ret.update(PyEnvSource(d) for d in package_dirs) if pyenv_paths and not ret: raise ValueError(f"Could not find any Python env in {pyenv_paths}!") diff --git a/fawltydeps/settings.py b/fawltydeps/settings.py index 79dc2b93..c106dc2e 100644 --- a/fawltydeps/settings.py +++ b/fawltydeps/settings.py @@ -110,7 +110,7 @@ class Settings(BaseSettings): # type: ignore output_format: OutputFormat = OutputFormat.HUMAN_SUMMARY code: Set[PathOrSpecial] = {Path(".")} deps: Set[Path] = {Path(".")} - pyenvs: Set[Path] = set() + pyenvs: Set[Path] = {Path(".")} custom_mapping: Optional[CustomMapping] = None ignore_undeclared: Set[str] = set() ignore_unused: Set[str] = set() @@ -189,11 +189,17 @@ def create(cls, cmdline_args: argparse.Namespace) -> "Settings": if base_paths: code_paths = args_dict.setdefault("code", base_paths) deps_paths = args_dict.setdefault("deps", base_paths) - if code_paths != base_paths and deps_paths != base_paths: + pyenv_paths = args_dict.setdefault("pyenvs", base_paths) + if ( + code_paths != base_paths + and deps_paths != base_paths + and pyenv_paths != base_paths + ): msg = ( - "All three path specifications (code, deps, and base)" - f"have been used. Use at most 2. basepaths={base_paths}, " - f"code_paths={code_paths}, deps_paths={deps_paths}" + "All four path specifications (code, deps, pyenvs, and base)" + f"have been used. Use at most 3. basepaths={base_paths}, " + f"code_paths={code_paths}, deps_paths={deps_paths}, " + f"pyenv_paths={pyenv_paths}" ) raise argparse.ArgumentError(argument=None, message=msg) diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 3586bbee..cabd5a89 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -35,7 +35,7 @@ def make_json_settings_dict(**kwargs): "actions": ["check_undeclared", "check_unused"], "code": ["."], "deps": ["."], - "pyenvs": [], + "pyenvs": ["."], "custom_mapping": None, "output_format": "human_summary", "ignore_undeclared": [], @@ -850,7 +850,7 @@ def test_cmdline_on_ignored_undeclared_option( output_format = 'human_detailed' # code = ['.'] deps = ['foobar'] - # pyenvs = [] + # pyenvs = ['.'] # ignore_undeclared = [] # ignore_unused = [] # deps_parser_choice = ... diff --git a/tests/test_settings.py b/tests/test_settings.py index 93629838..06a40d52 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -39,7 +39,7 @@ actions={Action.REPORT_UNDECLARED, Action.REPORT_UNUSED}, code={Path(".")}, deps={Path(".")}, - pyenvs=set(), + pyenvs={Path(".")}, custom_mapping_file=set(), custom_mapping=None, output_format=OutputFormat.HUMAN_SUMMARY, @@ -81,15 +81,15 @@ def _inner(**kwargs: str): safe_string = strategies.text(alphabet=string.ascii_letters + string.digits, min_size=1) nonempty_string_set = strategies.sets(safe_string, min_size=1) -three_different_string_groups = strategies.tuples( - nonempty_string_set, nonempty_string_set, nonempty_string_set -).filter(lambda ss: ss[0] != ss[1] and ss[0] != ss[2] and ss[1] != ss[2]) +four_different_string_groups = strategies.tuples( + *([nonempty_string_set] * 4), +).filter(lambda ss: all(a != b for a, b in combinations(ss, 2))) -@given(code_deps_base=three_different_string_groups) -def test_code_deps_and_base_unequal__raises_error(code_deps_base): - code, deps, base = code_deps_base - args = list(base) + ["--code"] + list(code) + ["--deps"] + list(deps) +@given(code_deps_pyenvs_base=four_different_string_groups) +def test_code_deps_pyenvs_and_base_unequal__raises_error(code_deps_pyenvs_base): + code, deps, pyenvs, base = code_deps_pyenvs_base + args = [*base, "--code", *code, "--deps", *deps, "--pyenv", *pyenvs] with pytest.raises(argparse.ArgumentError): run_build_settings(args) @@ -97,6 +97,7 @@ def test_code_deps_and_base_unequal__raises_error(code_deps_base): path_options = { # options (-> settings members) that interact with basepath "--code": "code", "--deps": "deps", + "--pyenv": "pyenvs", } Item = TypeVar("Item") @@ -151,13 +152,19 @@ def test_base_path_fills_path_options_when_other_path_settings_are_absent(basepa pytest.param(conf_sett, base, id=test_name) for conf_sett, base, test_name in [ (None, {"single-base"}, "empty-config"), - (dict(code=["test-code"]), {"base1", "base2"}, "only-code-set"), - (dict(deps=["deps-test"]), {"single-base"}, "only-deps-set"), + ({"code": ["test-code"]}, {"base1", "base2"}, "only-code-set"), + ({"deps": ["deps-test"]}, {"single-base"}, "only-deps-set"), + ({"pyenvs": ["pyenvs-test"]}, {"single-base"}, "only-pyenvs-set"), ( - dict(code=["code-test"], deps=["test-deps"]), + {"code": ["code-test"], "deps": ["test-deps"]}, {"base1", "base2"}, "code-and-deps-set", ), + ( + {"code": ["code-test"], "deps": ["test-deps"], "pyenvs": ["abc"]}, + {"base1", "base2"}, + "all-three-set", + ), ] ], )