diff --git a/.codeclimate.yml b/.codeclimate.yml index 8ad88e4..bcf0cac 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,6 +1,40 @@ --- +version: "2" # required to adjust maintainability checks + exclude_patterns: - "tests/" - "docs/source/conf.py" - "noxfile.py" - "prep_release.py" + +checks: + argument-count: + config: + threshold: 6 + complex-logic: + config: + threshold: 4 + file-lines: + config: + threshold: 500 + identical-code: + config: + threshold: 8 + method-complexity: + config: + threshold: 10 + method-count: + config: + threshold: 20 + method-lines: + config: + threshold: 25 + nested-control-flow: + config: + threshold: 4 + return-statements: + config: + threshold: 4 + similar-code: + config: + threshold: 4 diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe6385..89edcc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,27 @@ For a list of all available releases see the ## Unreleased [diff v4.0.0...main](https://github.com/Cielquan/formelsammlung/compare/v4.0.0...main) +#### BREAKING CHANGES +- change min python version from `3.6.1` to `3.6.2` + +#### New features +- refactored `getenv_typed()` *backend* into `EnvVarGetter` class which holds the config + +#### Documentation +- improved docs for `envvar.py` +- improved docs for `flask_sphinx_docs.py` +- improved docs for `nox_session.py` + +#### Miscellaneous +- updated code-climate config for maintainability checks + [#26](https://github.com/Cielquan/formelsammlung/issues/26) +- refactored `env_var_runner.py` to reduce cognitive complexity and added tests for new + code +- refactored `envvar.py` to reduce cognitive complexity and added tests for new code +- refactored `flask_sphinx_docs.py` to reduce cognitive complexity and added tests for + new code + + ## [4.0.0](https://github.com/Cielquan/formelsammlung/releases/v4.0.0) (2021-02-04) [diff v3.2.0...v4.0.0](https://github.com/Cielquan/formelsammlung/compare/v3.2.0...v4.0.0) diff --git a/docs/source/conf.py b/docs/source/conf.py index cd39cc9..9308f3d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -112,6 +112,7 @@ "flask": ("https://flask.palletsprojects.com/en/1.1.x/api/", "inv_flask.inv"), "nox": ("https://nox.thea.codes/en/stable/", "inv_nox.inv"), } +nitpick_ignore = [("py:class", "SessionRunner")] extensions.append("sphinx.ext.extlinks") extlinks = { @@ -131,6 +132,7 @@ apidoc_extra_args = [] if Path("_apidoc_templates").is_dir(): apidoc_extra_args += ["--templatedir", "apidoc_templates"] +autoclass_content = "both" if find_spec("sphinxcontrib.apidoc") is not None: diff --git a/docs/source/inv_nox.inv b/docs/source/inv_nox.inv index 8993c91..fc018f3 100644 Binary files a/docs/source/inv_nox.inv and b/docs/source/inv_nox.inv differ diff --git a/docs/source/spelling_dict.txt b/docs/source/spelling_dict.txt index 04aed6e..e7b797c 100644 --- a/docs/source/spelling_dict.txt +++ b/docs/source/spelling_dict.txt @@ -3,6 +3,7 @@ arg args attr bool +boolean bugfixes cfg changelog @@ -13,6 +14,7 @@ compat conf config csv +deleter dev dir dirs @@ -29,6 +31,7 @@ executables expr fmt func +getter github html importlib @@ -57,6 +60,7 @@ repo riedel rst sdist +setter st submodule submodules diff --git a/noxfile.py b/noxfile.py index 24da909..f50e5d3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -234,10 +234,12 @@ def test_code(session: Session) -> None: extras = "testing" session.poetry_install( extras, - no_root=(TOX_CALLS or SKIP_INSTALL), + no_root=True, no_dev=(TOX_CALLS or IN_CI), pip_require_venv=poetry_require_venv(session), ) + if not (TOX_CALLS or SKIP_INSTALL): + session.install(".") else: session.log("Skipping install step.") #: Remove processed posargs @@ -470,10 +472,12 @@ def docs(session: Session) -> None: if "skip_install" not in session.posargs: session.poetry_install( extras, - no_root=(TOX_CALLS or SKIP_INSTALL), + no_root=True, no_dev=(TOX_CALLS or IN_CI), pip_require_venv=poetry_require_venv(session), ) + if not (TOX_CALLS or SKIP_INSTALL): + session.install(".") else: session.log("Skipping install step.") @@ -500,10 +504,12 @@ def test_docs(session: Session, builder: str) -> None: extras = "docs" session.poetry_install( extras, - no_root=(TOX_CALLS or SKIP_INSTALL), + no_root=True, no_dev=(TOX_CALLS or IN_CI), pip_require_venv=poetry_require_venv(session), ) + if not (TOX_CALLS or SKIP_INSTALL): + session.install(".") else: session.log("Skipping install step.") #: Remove processed posargs diff --git a/prep_release.py b/prep_release.py index 986238b..7e813e4 100644 --- a/prep_release.py +++ b/prep_release.py @@ -36,7 +36,7 @@ class PyprojectError(Exception): """Exception for lookup errors in pyproject.toml file.""" -def _get_config_value(section: str, key: str) -> str: +def _get_config_value(section: str, key: str) -> str: # noqa: CCR001 """Extract a config value from pyproject.toml file. :return: config value diff --git a/pyproject.toml b/pyproject.toml index 6887651..7b17458 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ "Source" = "https://github.com/Cielquan/formelsammlung" [tool.poetry.dependencies] - python = "^3.6.1" + python = "^3.6.2" # NOTE: update min_python_version below also importlib-metadata = {version = ">=1.6, <4.0", python = "<3.8"} flask = {version = "^1.1.2", optional = true} nox = {version = "^2020.12.31", optional = true} @@ -228,7 +228,8 @@ format="colored" max_line_length = 88 max_complexity = 20 - max_cognitive_complexity = 20 + max_cognitive_complexity = 10 + min_python_version = "3.6.2" pytest_fixture_no_parentheses = true [tool.flakehell.plugins] @@ -278,6 +279,9 @@ flake8-bandit = [ "-S101", # assert used ] + pylint = [ + "-W0212", # accesss protected member + ] [tool.flakehell.exceptions."noxfile.py"] flake8-cognitive-complexity = [ diff --git a/src/formelsammlung/env_exe_runner.py b/src/formelsammlung/env_exe_runner.py index 590e7af..3e74659 100755 --- a/src/formelsammlung/env_exe_runner.py +++ b/src/formelsammlung/env_exe_runner.py @@ -14,6 +14,27 @@ from typing import List, Optional +IS_WIN = sys.platform == "win32" +EXE = "Scripts/{tool}.exe" if IS_WIN else "bin/{tool}" + + +def _check_runner_envs(venv_runner: str, envs: List[str], tool: str) -> Optional[Path]: + """Check if executable is found in any of the envs for given env runner.""" + for env in envs: + path = Path(f".{venv_runner}") / env / EXE.format(tool=tool) + if path.is_file(): + return path + return None + + +def _check_venv(venv: str, tool: str) -> Optional[Path]: + """Check if executable is found in venv.""" + path = Path(venv) / EXE.format(tool=tool) + if path.is_file(): + return path + return None + + def env_exe_runner( venv_runner: List[str], envs: List[str], @@ -31,42 +52,28 @@ def env_exe_runner( :param tool_args: Arguments to give to the ``tool``. :return: Exit code 127 if no executable is found or the exit code of the called cmd. """ - is_win = sys.platform == "win32" - - exe = Path(f"Scripts/{tool}.exe") if is_win else Path(f"bin/{tool}") - cmd = None - - if not tool_args: - tool_args = [] - + cmd_path = None error_msgs = [] for runner in venv_runner: if runner in ("tox", "nox"): error_msgs.append(f"- '{runner}' envs: {envs}") - - for env in envs: - path = Path(f".{runner}") / env / exe - if path.is_file(): - cmd = (str(path), *tool_args) - break - + cmd_path = _check_runner_envs(runner, envs, tool) else: error_msgs.append(f"- virtual env: ['{runner}']") + cmd_path = _check_venv(runner, tool) - path = Path(runner) / exe - if path.is_file(): - cmd = (str(path), *tool_args) - break + if cmd_path is not None: + break - if cmd is None: + if cmd_path is None: print(f"No '{tool}' executable found. Searched in:") for msg in error_msgs: print(msg) return 127 - return subprocess.call(cmd) # noqa: S603 + return subprocess.call([str(cmd_path), *(tool_args or [])]) # noqa: S603 def cli_caller() -> int: diff --git a/src/formelsammlung/envvar.py b/src/formelsammlung/envvar.py index d3622a5..c6af694 100644 --- a/src/formelsammlung/envvar.py +++ b/src/formelsammlung/envvar.py @@ -10,118 +10,314 @@ import os import re -from typing import Any, Iterable, Optional +from typing import Any, Iterable, NoReturn, Optional, Pattern, Set, Union +#: Default values to convert to ``True`` for environment variables. TRUE_BOOL_VALUES = ("1", "y", "yes", "t", "True") +#: Default values to convert to ``False`` for environment variables. FALSE_BOOL_VALUES = ("0", "n", "no", "f", "False") +#: Default regex to check if a string could be an :class:`int`. +INT_REGEX = r"^[\d]+(_\d+)*$" +#: Default regex to check if a string could be an :class:`float`. +FLOAT_REGEX = r"^[\d]+(_\d+)*\.\d+$" -def getenv_typed( - var_name: str, - default: Any = None, - rv_type: Optional[type] = None, - *, - raise_error_if_no_value: bool = False, - true_bool_values: Optional[Iterable] = None, - false_bool_values: Optional[Iterable] = None, -) -> Any: - """Wrap :func:`os.getenv` to adjust the type of the values. +class EnvVarGetter: # noqa: R0903 + """Class containing the config for :meth:`EnvVarGetter.getenv_typed`.""" - Instead of returning the environments variable's value as :class:`str` like - :func:`os.getenv` you can set ``rv_type`` to a type to convert the value to. If - ``rv_type`` is not set the type gets guessed and used for conversion. + def __init__( # noqa: R0913 + self, + *, + raise_error_if_no_value: bool = False, + true_bool_values: Optional[Iterable] = None, + false_bool_values: Optional[Iterable] = None, + int_regex: Optional[str] = None, + float_regex: Optional[str] = None, + ) -> None: + """Initialize :class:`EnvVarGetter` with config values. - Guessable types are: + Use the :class:`EnvVarGetter` instance to call :meth:`EnvVarGetter.getenv_typed` + with the set config. - - :class:`bool` - - :class:`int` - - :class:`float` - - :class:`str` (fallback) + .. Note:: Parameters below are all keyword only. - How to use: + :param raise_error_if_no_value: If ``True`` raises an :exc:`KeyError` when no + value is found by :meth:`EnvVarGetter.getenv_typed` for ``var_name`` and + ``default`` is ``None``. - .. testsetup:: + Default: ``False`` + :param true_bool_values: Set of objects whose string representations are + matched case-insensitive against the environment variable's value to check + if the value is a ``True`` bool. - import os - from formelsammlung.envvar import getenv_typed + Default: :const:`TRUE_BOOL_VALUES` + :param false_bool_values: Set of objects whose string representations are + matched case-insensitive against the environment variable's value to check + if the value is a ``False`` bool. - .. doctest:: + Default: :const:`FALSE_BOOL_VALUES` + :param int_regex: Regex string to identify integers. - >>> os.environ["TEST_ENV_VAR"] = "2" - >>> getenv_typed("TEST_ENV_VAR", 1, int) - 2 + Default: :const:`INT_REGEX` + :param float_regex: Regex string to identify floats. - .. testcleanup:: + Default: :const:`FLOAT_REGEX` + """ + self.raise_error_if_no_value = raise_error_if_no_value + self._true_bool_values = set(true_bool_values or TRUE_BOOL_VALUES) + self._false_bool_values = set(false_bool_values or FALSE_BOOL_VALUES) + self._int_regex = int_regex or INT_REGEX + self._int_regex_pattern = re.compile(self._int_regex) + self._float_regex = float_regex or FLOAT_REGEX + self._float_regex_pattern = re.compile(self._float_regex) - os.environ["TEST_ENV_VAR"] = "" + @property + def true_bool_values(self) -> Set[str]: + """Set of objects to identify a ``True`` boolean. + + See parameters of :class:`EnvVarGetter`. + """ + # Get value for ``_true_bool_values``. + return self._true_bool_values + + @true_bool_values.setter + def true_bool_values(self, value: Iterable) -> None: + """Set new value for ``_true_bool_values``.""" + self._true_bool_values = set(value) + + @property + def false_bool_values(self) -> Set[str]: + """Set of objects to identify a ``False`` boolean. + + See parameters of :class:`EnvVarGetter`. + """ + # Get value for ``_false_bool_values``. + return self._false_bool_values + + @false_bool_values.setter + def false_bool_values(self, value: Iterable) -> None: + """Set new value for ``_false_bool_values``.""" + self._false_bool_values = set(value) + + @property + def int_regex(self) -> str: + """Regex string used for checking if a string is an :class:`int`. + + See parameters of :class:`EnvVarGetter`. + """ + # Get value for ``_int_regex``. + return self._int_regex + + @int_regex.setter + def int_regex(self, value: str) -> None: + """Set new value for ``int_regex`` and update ``int_regex_pattern``.""" + self._int_regex = value + self._int_regex_pattern = re.compile(self._int_regex) + + @property + def int_regex_pattern(self) -> Pattern[str]: + """Regex pattern of :meth:`EnvVarGetter.int_regex`. + + Cannot be set. Set via :meth:`EnvVarGetter.int_regex`. + """ + return self._int_regex_pattern + + @int_regex_pattern.setter + def int_regex_pattern(self, value: Any) -> NoReturn: # noqa: R0201 + """Error if called.""" + raise AttributeError( + "`int_regex_pattern` cannot be set directly. " + "Set as string via `int_regex`." + ) + + @property + def float_regex(self) -> str: + """Regex string used for checking if a string is a :class:`float`. + + See parameters of :class:`EnvVarGetter`. + """ + # Get value for ``_float_regex``. + return self._float_regex + + @float_regex.setter + def float_regex(self, value: str) -> None: + """Set new value for ``float_regex`` and update ``float_regex_pattern``.""" + self._float_regex = value + self._float_regex_pattern = re.compile(self._float_regex) + + @property + def float_regex_pattern(self) -> Pattern[str]: + """Regex pattern of :meth:`EnvVarGetter.float_regex`. + + Cannot be set. Set via :meth:`EnvVarGetter.float_regex`. + """ + return self._float_regex_pattern + + @float_regex_pattern.setter + def float_regex_pattern(self, value: Any) -> NoReturn: # noqa: R0201 + """Error if called.""" + raise AttributeError( + "`float_regex_pattern` cannot be set directly. " + "Set as string via `float_regex`." + ) + + def _guess_bool(self, value: str) -> Optional[bool]: + """Guess if value is a ``bool``.""" + #: Guess if `True` + if value.casefold() in (str(b).casefold() for b in self._true_bool_values): + return True + #: Guess if `False` + if value.casefold() in (str(b).casefold() for b in self._false_bool_values): + return False + return None + + def _guess_num(self, value: str) -> Optional[Union[int, float]]: + """Guess if value is an ``int`` or ``float``.""" + #: Guess if `int` + if self._int_regex_pattern.fullmatch(value): + return int(value) + #: Guess if `float` + if self._float_regex_pattern.fullmatch(value): + return float(value) + return None + + def getenv_typed( + self, + var_name: str, + default: Any = None, + rv_type: Optional[type] = None, + ) -> Any: + """Wrap :func:`os.getenv` to adjust the type of the return values. + + Instead of returning the environments variable's value as :class:`str` like + :func:`os.getenv` you can set ``rv_type`` to a :class:`type` to convert the + value into. If ``rv_type`` is not set the :class:`type` gets guessed and used + for conversion. + + **Guessable types are (checked in this order):** + + - :class:`bool` + - :class:`int` + - :class:`float` + - :class:`str` (fallback) - :param var_name: Name of the environment variable. - :param default: Default value if no value is found for ``var_name``. - Default: ``None``. - :param rv_type: Type the value of the environment variable should be changed into. - If not set or set to ``None`` the type gets guessed. - Default: ``None``. - :param raise_error_if_no_value: If ``True`` raises an :class:`KeyError` when no - value is found for ``var_name`` and ``default`` is ``None``. - Parameter is keyword only. - Default: ``False`` - :param true_bool_values: Set of objects whose string representations are - matched case-insensitive against the environment variable's value if the - ``rv_type`` is :class:`bool` or the type gets guessed. If a match is found - ``True`` is returned. - Parameter is keyword only. - Default: ``(1, "y", "yes", "t", True)`` - :param false_bool_values: Set of objects whose string representations are - matched case-insensitive against the environment variable's value if the - ``rv_type`` is :class:`bool` or the type gets guessed. If a match is found - ``False`` is returned. - Parameter is keyword only. - Default: ``(0, "n", "no", "f", False)`` - :raises KeyError: If ``raise_error_if_no_value`` is ``True`` and no value is found - for ``var_name`` and ``default`` is ``None``. - :raises KeyError: If ``rv_type`` is set to :class:`bool` and value from - ``var_name`` or ``default`` is not found in ``true_bool_values`` or - ``false_bool_values``. - :return: Value for ``var_name`` or ``default`` converted to ``rv_type`` - or guessed type. - """ - env_var = os.getenv(var_name, default) - - if not env_var and default is None: - if raise_error_if_no_value: + For :class:`bool` guessing the value returned by :func:`os.getenv` is compared + against :meth:`EnvVarGetter.true_bool_values` and + :meth:`EnvVarGetter.false_bool_values` and if a match is found returns the + corresponding boolean. + + For :class:`int` guessing the value returned by :func:`os.getenv` is checked by + the regex :meth:`EnvVarGetter.int_regex_pattern` which can be changed by setting + :meth:`EnvVarGetter.int_regex`. + + For :class:`float` guessing the value returned by :func:`os.getenv` is checked + by the regex :meth:`EnvVarGetter.float_regex_pattern` which can be changed by + setting :meth:`EnvVarGetter.float_regex`. + + .. Warning:: Because :class:`bool` is guessed before :class:`int` ``0`` and + ``1`` are converted into :class:`bool` instead of :class:`int` when + ``rv_type`` is not set. + + **How to use:** + + .. testsetup:: + + import os + from formelsammlung.envvar import EnvVarGetter + + .. doctest:: + + >>> os.environ["TEST_ENV_VAR"] = "2" + >>> getter = EnvVarGetter() + >>> getter.getenv_typed("TEST_ENV_VAR", 1, int) + 2 + + .. testcleanup:: + + os.environ["TEST_ENV_VAR"] = "" + + :param var_name: Name of the environment variable. + :param default: Default value if no value is found for ``var_name``. + + Default: ``None``. + :param rv_type: Type the value of the environment variable should be changed + into. If not set or set to ``None`` the type gets guessed. + + Default: ``None``. + :raises KeyError: If ``raise_error_if_no_value`` is ``True`` and no value is + found for ``var_name`` and ``default`` is ``None``. + :raises KeyError: If ``rv_type`` is set to :class:`bool` and value from + ``var_name`` or ``default`` is not found in ``true_bool_values`` or + ``false_bool_values``. + :return: Value for ``var_name`` or ``default`` converted to ``rv_type`` + or guessed type. + """ + env_var = os.getenv(var_name, default) + + if not env_var and default is None: + if self.raise_error_if_no_value: + raise KeyError( + f"Environment variable '{var_name}' not set " + "or empty and no default." + ) from None + return None + + #: Convert to given `rv_type` if set. + if rv_type and rv_type is not bool: + return rv_type(env_var) + + env_var = str(env_var) + + #: Guess bool value + bool_val = self._guess_bool(env_var) + if bool_val is not None: + return bool_val + + if rv_type: raise KeyError( - f"Environment variable '{var_name}' not set or empty and no default." + f"Environment variable '{var_name}' has an invalid Boolean value.\n" + f"For true use any of: {self._true_bool_values}\n" + f"For false use any of: {self._false_bool_values}" ) from None - return None - #: Convert to given `rv_type` if set. - if rv_type and rv_type is not bool: - return rv_type(env_var) + #: Guess num value + num_val = self._guess_num(env_var) + if num_val is not None: + return num_val - env_var = str(env_var) + return env_var - #: Guess bool value - true_bool_values = set(true_bool_values or TRUE_BOOL_VALUES) - false_bool_values = set(false_bool_values or FALSE_BOOL_VALUES) - if env_var.casefold() in (str(b).casefold() for b in true_bool_values): - return True - if env_var.casefold() in (str(b).casefold() for b in false_bool_values): - return False - if rv_type: - raise KeyError( - f"Environment variable '{var_name}' has an invalid Boolean value.\n" - f"For true use any of: {true_bool_values}\n" - f"For false use any of: {false_bool_values}" - ) from None +def getenv_typed( + var_name: str, default: Any = None, rv_type: Optional[type] = None, **kwargs: Any +) -> Any: + """Shortcut for ``EnvVarGetter(...).getenv_typed(...)``. - #: Guess if `int` - if re.fullmatch(r"^\d+$", env_var): - env_var = int(env_var) + **How to use:** + + .. testsetup:: - #: Guess if `float` - elif re.fullmatch(r"^\d+\.\d+$", env_var): - env_var = float(env_var) + import os + from formelsammlung.envvar import getenv_typed + + .. doctest:: + + >>> os.environ["TEST_ENV_VAR"] = "2" + >>> getenv_typed("TEST_ENV_VAR", 1, int) + 2 + + .. testcleanup:: + + os.environ["TEST_ENV_VAR"] = "" - return env_var + :param var_name: Same argument as for and gets given to + :meth:`EnvVarGetter.getenv_typed`. + :param default: Same argument as for and gets given to + :meth:`EnvVarGetter.getenv_typed`. + :param rv_type: Same argument as for and gets given to + :meth:`EnvVarGetter.getenv_typed`. + :param kwargs: Arguments taken by :class:`EnvVarGetter` + :return: Return value of :meth:`EnvVarGetter.getenv_typed`. + """ # noqa: D402 + return EnvVarGetter(**kwargs).getenv_typed(var_name, default, rv_type) diff --git a/src/formelsammlung/flask_sphinx_docs.py b/src/formelsammlung/flask_sphinx_docs.py index dd07acf..dc37baa 100644 --- a/src/formelsammlung/flask_sphinx_docs.py +++ b/src/formelsammlung/flask_sphinx_docs.py @@ -14,40 +14,47 @@ class SphinxDocServer: # noqa: R0903 - """Serve your sphinx docs under `/docs/` on your own flask app. + """Serve your sphinx docs under `/docs/` on your own flask app.""" - .. highlight:: python + def __init__( + self, + app: Optional[Flask] = None, + *, + doc_dir: Optional[str] = None, + index_file: str = "index.html", + ) -> None: + """Init SphinxDocServer class. - You can invoke it in your app factory:: + .. highlight:: python - sds = SphinxDocServer() + You can invoke it in your app factory:: - def create_app(): - app = Flask(__name__) - sds.init_app(app) - return app + sds = SphinxDocServer() - or you can include the plugin directly without setting a ``doc_dir``:: + def create_app(): + app = Flask(__name__) + sds.init_app(app) + return app - app = Flask(__name__) - SphinxDocServer(app) + or you can include the plugin directly without setting a ``doc_dir``:: - or with setting a ``doc_dir``:: + app = Flask(__name__) + SphinxDocServer(app) - app = Flask(__name__) - SphinxDocServer(app, doc_dir="../../docs/build/html") + or with setting a ``doc_dir``:: - .. highlight:: default - """ + app = Flask(__name__) + SphinxDocServer(app, doc_dir="../../docs/build/html") - def __init__( - self, - app: Optional[Flask] = None, - *, - doc_dir: Optional[str] = None, - index_file: str = "index.html", - ) -> None: - """Init SphinxDocServer.""" + .. highlight:: default + + :param app: Same argument as for and gets given to + :meth:`SphinxDocServer.init_app`. + :param doc_dir: Same argument as for and gets given to + :meth:`SphinxDocServer.init_app`. + :param index_file: Same argument as for and gets given to + :meth:`SphinxDocServer.init_app`. + """ if app is not None: self.init_app(app, doc_dir, index_file) @@ -79,6 +86,28 @@ def web_docs(filename: str) -> Response: # noqa: W0612 app.static_folder = static_folder return doc_file + @staticmethod + def _find_build_dir(doc_dir: Path) -> Path: + """Find build dir in given doc dir. + + :param doc_dir: Path to doc dir + :raises IOError: if no '_build' or 'build' directory is found in the + doc/docs dir. + :return: Path to build dir + """ + build_dir = None + if (doc_dir / "_build").is_dir(): + build_dir = doc_dir / "_build" + if (doc_dir / "build").is_dir(): + build_dir = doc_dir / "build" + + if not build_dir: + raise OSError( + f"No '_build' or 'build' directory found in {doc_dir}. " + "Maybe you forgot to build the docs." + ) + return build_dir + @staticmethod def _find_built_docs(app_root: str, steps_up_the_tree: int = 3) -> Path: """Find built sphinx html docs. @@ -87,8 +116,6 @@ def _find_built_docs(app_root: str, steps_up_the_tree: int = 3) -> Path: :param steps_up_the_tree: Amount of steps to go up the file tree, defaults to 3 :raises IOError: if no root dir path for the app is given. :raises IOError: if no 'doc' or 'docs' directory is found. - :raises IOError: if no '_build' or 'build' directory is found in the - doc/docs dir. :raises IOError: if no 'html' directory is found in the _build/build dir. :return: Path to directory holding the build sphinx docs. """ @@ -114,17 +141,7 @@ def _find_built_docs(app_root: str, steps_up_the_tree: int = 3) -> Path: raise OSError("No 'doc' or 'docs' directory found.") #: search for (_)build dir - build_dir = None - if (doc_dir / "_build").is_dir(): - build_dir = doc_dir / "_build" - if (doc_dir / "build").is_dir(): - build_dir = doc_dir / "build" - - if not build_dir: - raise OSError( - f"No '_build' or 'build' directory found in {doc_dir}. " - "Maybe you forgot to build the docs." - ) + build_dir = SphinxDocServer._find_build_dir(doc_dir) #: check for html dir if (build_dir / "html").is_dir(): diff --git a/src/formelsammlung/nox_session.py b/src/formelsammlung/nox_session.py index cad550b..b8752eb 100644 --- a/src/formelsammlung/nox_session.py +++ b/src/formelsammlung/nox_session.py @@ -80,13 +80,18 @@ def poetry_install( """Wrap ``poetry install`` command for usage in ``nox`` sessions. :param extras: string of space separated extras to install - :param no_dev: if ``--no-dev`` should be set; defaults to: False - :param no_root: if ``--no-root`` should be set; defaults to: False + :param no_dev: if ``--no-dev`` should be set + + Default: ``False`` + :param no_root: if ``--no-root`` should be set + + Default: ``False`` :param pip_require_venv: if ``True`` sets environment variable ``PIP_REQUIRE_VIRTUALENV`` to ``true`` for this call. ``pip`` then requires to be run inside a venv. ``pip`` is used to install ``poetry`` inside the venv, if no ``poetry`` executable can be found. - :param kwargs: same kwargs as ``Session.install()`` (see ``nox`` docs) + :param kwargs: same kwargs as :meth:`nox.sessions.Session.install` (see ``nox`` + docs) """ #: Safety hurdle copied from nox.sessions.Session.install() if not isinstance(self._runner.venv, (CondaEnv, VirtualEnv, PassthroughEnv)): diff --git a/tests/test_env_exe_runner.py b/tests/test_env_exe_runner.py index 0dfc2b8..6674b93 100644 --- a/tests/test_env_exe_runner.py +++ b/tests/test_env_exe_runner.py @@ -7,13 +7,25 @@ :copyright: (c) 2020, Christian Riedel and AUTHORS :license: GPL-3.0-or-later, see LICENSE for details """ # noqa: D205,D208,D400 +import os import subprocess # noqa: S404 +from contextlib import contextmanager from pathlib import Path +from typing import Generator import pytest -from formelsammlung.env_exe_runner import env_exe_runner as runner +from formelsammlung import env_exe_runner + + +@contextmanager +def change_cwd(target: Path) -> Generator: + """Change cwd with a contextmanager.""" + cwd = os.getcwd() + os.chdir(target) + yield + os.chdir(cwd) def test_runner_without_args(monkeypatch: pytest.MonkeyPatch) -> None: @@ -21,7 +33,7 @@ def test_runner_without_args(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(subprocess, "call", lambda _: 32) monkeypatch.setattr(Path, "is_file", lambda _: True) - result = runner(["tox"], ["toxenv"], "tool") + result = env_exe_runner.env_exe_runner(["tox"], ["toxenv"], "tool") assert result == 32 @@ -31,7 +43,7 @@ def test_runner_with_args(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(subprocess, "call", lambda _: 32) monkeypatch.setattr(Path, "is_file", lambda _: True) - result = runner(["tox"], ["toxenv"], "tool", ["argument"]) + result = env_exe_runner.env_exe_runner(["tox"], ["toxenv"], "tool", ["argument"]) assert result == 32 @@ -41,7 +53,7 @@ def test_venv_without_args(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(subprocess, "call", lambda _: 32) monkeypatch.setattr(Path, "is_file", lambda _: True) - result = runner(["venv"], [], "tool") + result = env_exe_runner.env_exe_runner(["venv"], [], "tool") assert result == 32 @@ -51,17 +63,100 @@ def test_venv_with_args(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(subprocess, "call", lambda _: 32) monkeypatch.setattr(Path, "is_file", lambda _: True) - result = runner(["venv"], [], "tool", ["argument"]) + result = env_exe_runner.env_exe_runner(["venv"], [], "tool", ["argument"]) assert result == 32 def test_no_cmd_found(capsys: pytest.CaptureFixture) -> None: """Test exit code 127 on missing cmd.""" - result = runner(["tox", "venv"], ["non_existing_toxenv"], "no_existing_tool") + result = env_exe_runner.env_exe_runner( + ["tox", "venv"], ["non_existing_toxenv"], "no_existing_tool" + ) assert result == 127 output = capsys.readouterr().out assert "No 'no_existing_tool' executable found." in output assert "- 'tox' envs: ['non_existing_toxenv']" in output assert "- virtual env: ['venv']" in output + + +def test__check_venv_success(tmp_path: Path) -> None: + """Test return value of _check_venv for success.""" + venv_name = ".venv" + venv = tmp_path / venv_name + venv.mkdir() + filename = "testfile" + exe = venv / env_exe_runner.EXE.format(tool=filename) + exe.parent.mkdir() + exe.write_text("This is a file.") + #: Change cwd to tmp dir + with change_cwd(tmp_path): + + result = env_exe_runner._check_venv(venv_name, filename) + + assert result is not None + assert result.absolute() == Path(exe) + + +def test__check_venv_fail(tmp_path: Path) -> None: + """Test return value of _check_venv for failure.""" + venv_name = ".venv" + venv = tmp_path / venv_name + venv.mkdir() + filename = "testfile" + #: Change cwd to tmp dir + with change_cwd(tmp_path): + + result = env_exe_runner._check_venv(venv_name, filename) + + assert result is None + + +@pytest.mark.parametrize("runner_name", ["tox", "nox"]) +def test__check_runner_envs_success(runner_name: str, tmp_path: Path) -> None: + """Test return value of _check_runner_envs for success.""" + #: Create runner dir + runner_dir = tmp_path / ("." + runner_name) + runner_dir.mkdir() + #: Create env dirs + env1_dir = runner_dir / "env1" + env1_dir.mkdir() + env2_dir = runner_dir / "env2" + env2_dir.mkdir() + #: Create testfile in second env + filename = "testfile" + exe = env2_dir / env_exe_runner.EXE.format(tool=filename) + exe.parent.mkdir() + exe.write_text("This is a file.") + #: Change cwd to tmp dir + with change_cwd(tmp_path): + + result = env_exe_runner._check_runner_envs( + runner_name, ["env1", "env2"], filename + ) + + assert result is not None + assert result.absolute() == Path(exe) + + +@pytest.mark.parametrize("runner_name", ["tox", "nox"]) +def test__check_runner_envs_fail(runner_name: str, tmp_path: Path) -> None: + """Test return value of _check_runner_envs for failure.""" + #: Create runner dir + runner_dir = tmp_path / ("." + runner_name) + runner_dir.mkdir() + #: Create env dirs + env1_dir = runner_dir / "env1" + env1_dir.mkdir() + env2_dir = runner_dir / "env2" + env2_dir.mkdir() + filename = "testfile" + #: Change cwd to tmp dir + with change_cwd(tmp_path): + + result = env_exe_runner._check_runner_envs( + runner_name, ["env1", "env2"], filename + ) + + assert result is None diff --git a/tests/test_envvar.py b/tests/test_envvar.py index aacbe44..883de98 100644 --- a/tests/test_envvar.py +++ b/tests/test_envvar.py @@ -7,12 +7,20 @@ :copyright: (c) 2020, Christian Riedel and AUTHORS :license: GPL-3.0-or-later, see LICENSE for details """ # noqa: D205,D208,D400 +import re + from decimal import Decimal from typing import Union import pytest -from formelsammlung.envvar import FALSE_BOOL_VALUES, TRUE_BOOL_VALUES +from formelsammlung.envvar import ( + FALSE_BOOL_VALUES, + FLOAT_REGEX, + INT_REGEX, + TRUE_BOOL_VALUES, + EnvVarGetter, +) from formelsammlung.envvar import getenv_typed as get @@ -160,3 +168,185 @@ def test_raise_wrong_bool_value(monkeypatch: pytest.MonkeyPatch) -> None: with pytest.raises(KeyError): get("TEST_BOOL_ERROR", rv_type=bool) + + +# Test class + + +def test_EnvVarGetter_init() -> None: # noqa: C0103,N802 + """Test constructor of EnvVarGetter.""" + instance = EnvVarGetter() + + result = instance.int_regex_pattern + + assert result == re.compile(INT_REGEX) + + +def test_EnvVarGetter_true_bool_values_getter() -> None: # noqa: C0103,N802 + """Test true_bool_values getter of EnvVarGetter.""" + instance = EnvVarGetter() + + result = instance.true_bool_values + + assert result == set(TRUE_BOOL_VALUES) + + +def test_EnvVarGetter_true_bool_values_setter() -> None: # noqa: C0103,N802 + """Test true_bool_values setter of EnvVarGetter.""" + instance = EnvVarGetter() + new_value = {"fake_true"} + instance.true_bool_values = new_value + + result = instance.true_bool_values + + assert result == new_value + + +def test_EnvVarGetter_false_bool_values_getter() -> None: # noqa: C0103,N802 + """Test false_bool_values getter of EnvVarGetter.""" + instance = EnvVarGetter() + + result = instance.false_bool_values + + assert result == set(FALSE_BOOL_VALUES) + + +def test_EnvVarGetter_false_bool_values_setter() -> None: # noqa: C0103,N802 + """Test false_bool_values setter of EnvVarGetter.""" + instance = EnvVarGetter() + new_value = {"fake_false"} + instance.false_bool_values = new_value + + result = instance.false_bool_values + + assert result == new_value + + +def test_EnvVarGetter_int_regex_getter() -> None: # noqa: C0103,N802 + """Test int_regex getter of EnvVarGetter.""" + instance = EnvVarGetter() + + result = instance.int_regex + + assert result == INT_REGEX + + +def test_EnvVarGetter_int_regex_setter() -> None: # noqa: C0103,N802 + """Test int_regex setter of EnvVarGetter.""" + instance = EnvVarGetter() + new_value = "int-regex" + instance.int_regex = new_value + + result = instance + + assert result.int_regex == new_value + assert result.int_regex_pattern == re.compile(new_value) + + +def test_EnvVarGetter_int_regex_pattern_getter() -> None: # noqa: C0103,N802 + """Test int_regex_pattern getter of EnvVarGetter.""" + instance = EnvVarGetter() + + result = instance.int_regex_pattern + + assert result == re.compile(INT_REGEX) + + +def test_EnvVarGetter_int_regex_pattern_setter() -> None: # noqa: C0103,N802 + """Test int_regex_pattern setter of EnvVarGetter.""" + instance = EnvVarGetter() + + with pytest.raises(AttributeError, match="int_regex_pattern"): + instance.int_regex_pattern = re.compile("") + + +def test_EnvVarGetter_float_regex_getter() -> None: # noqa: C0103,N802 + """Test float_regex getter of EnvVarGetter.""" + instance = EnvVarGetter() + + result = instance.float_regex + + assert result == FLOAT_REGEX + + +def test_EnvVarGetter_float_regex_setter() -> None: # noqa: C0103,N802 + """Test float_regex setter of EnvVarGetter.""" + instance = EnvVarGetter() + new_value = "int-regex" + instance.float_regex = new_value + + result = instance + + assert result.float_regex == new_value + assert result.float_regex_pattern == re.compile(new_value) + + +def test_EnvVarGetter_float_regex_pattern_getter() -> None: # noqa: C0103,N802 + """Test float_regex_pattern getter of EnvVarGetter.""" + instance = EnvVarGetter() + + result = instance.float_regex_pattern + + assert result == re.compile(FLOAT_REGEX) + + +def test_EnvVarGetter_float_regex_pattern_setter() -> None: # noqa: C0103,N802 + """Test float_regex_pattern setter of EnvVarGetter.""" + instance = EnvVarGetter() + + with pytest.raises(AttributeError, match="float_regex_pattern"): + instance.float_regex_pattern = re.compile("") + + +def test_EnvVarGetter__guess_bool_true() -> None: # noqa: C0103,N802 + """Test _guess_bool of EnvVarGetter for True.""" + instance = EnvVarGetter() + + result = instance._guess_bool("yes") + + assert result is True + + +def test_EnvVarGetter__guess_bool_false() -> None: # noqa: C0103,N802 + """Test _guess_bool of EnvVarGetter for False.""" + instance = EnvVarGetter() + + result = instance._guess_bool("no") + + assert result is False + + +def test_EnvVarGetter__guess_bool_none() -> None: # noqa: C0103,N802 + """Test _guess_bool of EnvVarGetter for None.""" + instance = EnvVarGetter() + + result = instance._guess_bool("no-bool") + + assert result is None + + +def test_EnvVarGetter__guess_num_int() -> None: # noqa: C0103,N802 + """Test _guess_num of EnvVarGetter for int.""" + instance = EnvVarGetter() + + result = instance._guess_num("32") + + assert result == 32 + + +def test_EnvVarGetter__guess_num_float() -> None: # noqa: C0103,N802 + """Test _guess_num of EnvVarGetter for float.""" + instance = EnvVarGetter() + + result = instance._guess_num("32.69") + + assert result == 32.69 + + +def test_EnvVarGetter__guess_num_none() -> None: # noqa: C0103,N802 + """Test _guess_num of EnvVarGetter for none.""" + instance = EnvVarGetter() + + result = instance._guess_num("no-number") + + assert result is None diff --git a/tests/test_flask_sphinx_docs.py b/tests/test_flask_sphinx_docs.py index 4aa7773..f066eab 100644 --- a/tests/test_flask_sphinx_docs.py +++ b/tests/test_flask_sphinx_docs.py @@ -85,13 +85,13 @@ def test_no_app_root() -> None: @pytest.mark.parametrize( ("doc_dir_name", "build_dir_name"), [("doc", "_build"), ("docs", "build")] ) -def test_doc_dir_guessing_option_1( +def test_doc_dir_guessing_option( doc_dir_name: str, build_dir_name: str, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: - """Test guessing of doc dir with 'doc/_build'.""" + """Test guessing of doc dir.""" test_repo = tmp_path / "testrepo" test_repo.mkdir() py_code_dir = test_repo / "src" / "testrepo" @@ -112,3 +112,27 @@ def test_doc_dir_guessing_option_1( html_dir = build_dir / "html" html_dir.mkdir() assert SphinxDocServer._find_built_docs("fake_app_root") == html_dir + + +@pytest.mark.parametrize("build_dir_name", ["_build", "build"]) +def test__find_build_dir(build_dir_name: str, tmp_path: Path) -> None: + """Test finding of build dir with _find_build_dir.""" + doc_dir = tmp_path / "docdir" + doc_dir.mkdir() + build_dir = doc_dir / build_dir_name + build_dir.mkdir() + + result = SphinxDocServer._find_build_dir(doc_dir) + + assert result == build_dir + + +def test__find_build_dir_error(tmp_path: Path) -> None: + """Test error when not finding build dir with _find_build_dir.""" + doc_dir = tmp_path / "docdir" + doc_dir.mkdir() + build_dir = doc_dir / "fake_build" + build_dir.mkdir() + + with pytest.raises(OSError, match="No '_build' or 'build'"): + SphinxDocServer._find_build_dir(doc_dir) diff --git a/tests/test_venv_utils.py b/tests/test_venv_utils.py index 3ba8f8a..b8b04ae 100644 --- a/tests/test_venv_utils.py +++ b/tests/test_venv_utils.py @@ -16,7 +16,7 @@ import pytest -import formelsammlung.venv_utils as vu +from formelsammlung import venv_utils #: Test get_venv_path() @@ -25,7 +25,7 @@ def test_get_venv_path_w_real_prefix(monkeypatch: pytest.MonkeyPatch) -> None: sys.real_prefix = "" # type: ignore[attr-defined] monkeypatch.setattr(sys, "real_prefix", "path-to-venv-via-real_prefix") - result = vu.get_venv_path() + result = venv_utils.get_venv_path() assert result == Path("path-to-venv-via-real_prefix") @@ -35,7 +35,7 @@ def test_get_venv_path_w_prefix(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delattr(sys, "real_prefix", raising=False) monkeypatch.setattr(sys, "prefix", "path-to-venv-via-prefix") - result = vu.get_venv_path() + result = venv_utils.get_venv_path() assert result == Path("path-to-venv-via-prefix") @@ -46,7 +46,7 @@ def test_get_venv_path_raise(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(sys, "prefix", sys.base_prefix) with pytest.raises(FileNotFoundError) as excinfo: - vu.get_venv_path() + venv_utils.get_venv_path() assert "No calling venv could" in str(excinfo.value) @@ -55,10 +55,10 @@ def test_get_venv_path_raise(monkeypatch: pytest.MonkeyPatch) -> None: def test_get_venv_bin_dir(tmp_path: Path) -> None: """Test get_venv_bin_dir return a venv's bin/Scripts dir.""" fake_venv = tmp_path / ".venv" - bin_dir = fake_venv / vu.OS_BIN + bin_dir = fake_venv / venv_utils.OS_BIN bin_dir.mkdir(parents=True) - result = vu.get_venv_bin_dir(fake_venv) + result = venv_utils.get_venv_bin_dir(fake_venv) assert result == bin_dir @@ -69,7 +69,7 @@ def test_get_venv_bin_dir_raise(tmp_path: Path) -> None: fake_venv.mkdir(parents=True) with pytest.raises(FileNotFoundError) as excinfo: - vu.get_venv_bin_dir(fake_venv) + venv_utils.get_venv_bin_dir(fake_venv) assert "Given venv has no" in str(excinfo.value) @@ -82,7 +82,7 @@ def test_get_venv_tmp_dir(tmp_dir_name: str, tmp_path: Path) -> None: tmp_dir = fake_venv / tmp_dir_name tmp_dir.mkdir(parents=True) - result = vu.get_venv_tmp_dir(fake_venv) + result = venv_utils.get_venv_tmp_dir(fake_venv) assert result == tmp_dir @@ -93,7 +93,7 @@ def test_get_venv_tmp_dir_custom_search(tmp_path: Path) -> None: tmp_dir = fake_venv / "custom_tmp" tmp_dir.mkdir(parents=True) - result = vu.get_venv_tmp_dir(fake_venv, search_tmp_dirs=("custom_tmp",)) + result = venv_utils.get_venv_tmp_dir(fake_venv, search_tmp_dirs=("custom_tmp",)) assert result == tmp_dir @@ -109,7 +109,7 @@ def test_get_venv_tmp_dir_create_if_missing( fake_venv.mkdir(parents=True) tmp_dir = fake_venv / tmp_dir_name - result = vu.get_venv_tmp_dir( + result = venv_utils.get_venv_tmp_dir( fake_venv, create_if_missing=True, create_dir_name=create_dir_name ) @@ -122,7 +122,7 @@ def test_get_venv_tmp_dir_raise(tmp_path: Path) -> None: fake_venv.mkdir(parents=True) with pytest.raises(FileNotFoundError) as excinfo: - vu.get_venv_tmp_dir(fake_venv) + venv_utils.get_venv_tmp_dir(fake_venv) assert "Given venv has no" in str(excinfo.value) @@ -134,7 +134,7 @@ def test_get_venv_site_packages_dir(tmp_path: Path) -> None: site_pkg_dir = fake_venv / "lib" / "pythonX.Y" / "site-packages" site_pkg_dir.mkdir(parents=True) - result = vu.get_venv_site_packages_dir(fake_venv) + result = venv_utils.get_venv_site_packages_dir(fake_venv) assert result == site_pkg_dir @@ -145,7 +145,7 @@ def test_get_venv_site_packages_dir_raise(tmp_path: Path) -> None: fake_venv.mkdir(parents=True) with pytest.raises(FileNotFoundError) as excinfo: - vu.get_venv_site_packages_dir(fake_venv) + venv_utils.get_venv_site_packages_dir(fake_venv) assert "Given venv has no" in str(excinfo.value) @@ -155,7 +155,7 @@ def test_where_installed_nowhere(monkeypatch: pytest.MonkeyPatch) -> None: """Test where_installed with not existing program.""" monkeypatch.setattr(shutil, "which", lambda _: None) - result = vu.where_installed("no_existing_program") + result = venv_utils.where_installed("no_existing_program") assert result == (0, None, None) @@ -167,17 +167,17 @@ def test_where_installed_only_venv( fake_glob_bin = tmp_path / "bin" #: create fake venv dir fake_venv = tmp_path / ".venv" - monkeypatch.setattr(vu, "get_venv_path", lambda: fake_venv) + monkeypatch.setattr(venv_utils, "get_venv_path", lambda: fake_venv) #: create fake exe file - fake_venv_bin = fake_venv / vu.OS_BIN + fake_venv_bin = fake_venv / venv_utils.OS_BIN fake_exe = fake_venv_bin / "venv_program" #: adjust PATH os.environ["PATH"] = str(fake_venv_bin) + os.pathsep + str(fake_glob_bin) monkeypatch.setattr( - vu.shutil, "which", lambda _, path=None: None if path else str(fake_exe) + venv_utils.shutil, "which", lambda _, path=None: None if path else str(fake_exe) ) - result = vu.where_installed("venv_program") + result = venv_utils.where_installed("venv_program") assert result == (1, str(fake_exe), None) @@ -188,9 +188,9 @@ def test_integr_where_installed_only_venv( """Test where_installed with only global existing program and with venv.""" #: create fake venv dir fake_venv = tmp_path / ".venv" - monkeypatch.setattr(vu, "get_venv_path", lambda: fake_venv) + monkeypatch.setattr(venv_utils, "get_venv_path", lambda: fake_venv) #: create fake venv bin dir - fake_venv_bin = fake_venv / vu.OS_BIN + fake_venv_bin = fake_venv / venv_utils.OS_BIN fake_venv_bin.mkdir(parents=True) #: create fake global bin dir fake_glob_bin = tmp_path / "bin" @@ -203,7 +203,7 @@ def test_integr_where_installed_only_venv( #: adjust PATH os.environ["PATH"] = str(fake_venv_bin) + os.pathsep + str(fake_glob_bin) - result = vu.where_installed("venv_program") + result = venv_utils.where_installed("venv_program") assert result == (1, str(fake_exe), None) @@ -212,16 +212,16 @@ def test_where_installed_only_global_no_venv( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """Test where_installed with only global existing program and no venv.""" - monkeypatch.setattr(vu, "get_venv_path", lambda: None) + monkeypatch.setattr(venv_utils, "get_venv_path", lambda: None) #: create fake global bin dir fake_glob_bin = tmp_path / "bin" #: create fake exe file fake_exe = fake_glob_bin / "global_program" #: adjust PATH os.environ["PATH"] = str(fake_glob_bin) - monkeypatch.setattr(vu.shutil, "which", lambda _, path=None: str(fake_exe)) + monkeypatch.setattr(venv_utils.shutil, "which", lambda _, path=None: str(fake_exe)) - result = vu.where_installed("global_program") + result = venv_utils.where_installed("global_program") assert result == (2, None, str(fake_exe)) @@ -230,7 +230,7 @@ def test_integr_where_installed_only_global_no_venv( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """Test where_installed with only global existing program and no venv.""" - monkeypatch.setattr(vu, "get_venv_path", lambda: None) + monkeypatch.setattr(venv_utils, "get_venv_path", lambda: None) #: create fake global bin dir fake_glob_bin = tmp_path / "bin" fake_glob_bin.mkdir() @@ -242,7 +242,7 @@ def test_integr_where_installed_only_global_no_venv( #: adjust PATH os.environ["PATH"] = str(fake_glob_bin) - result = vu.where_installed("global_program") + result = venv_utils.where_installed("global_program") assert result == (2, None, str(fake_exe)) @@ -253,18 +253,18 @@ def test_where_installed_only_global_with_venv( """Test where_installed with only global existing program and with venv.""" #: create fake venv dir fake_venv = tmp_path / ".venv" - monkeypatch.setattr(vu, "get_venv_path", lambda: fake_venv) + monkeypatch.setattr(venv_utils, "get_venv_path", lambda: fake_venv) #: create fake venv bin dir - fake_venv_bin = fake_venv / vu.OS_BIN + fake_venv_bin = fake_venv / venv_utils.OS_BIN #: create fake global bin dir fake_glob_bin = tmp_path / "bin" #: create fake exe file fake_exe = fake_glob_bin / "global_program" #: adjust PATH os.environ["PATH"] = str(fake_venv_bin) + os.pathsep + str(fake_glob_bin) - monkeypatch.setattr(vu.shutil, "which", lambda _, path=None: str(fake_exe)) + monkeypatch.setattr(venv_utils.shutil, "which", lambda _, path=None: str(fake_exe)) - result = vu.where_installed("global_program") + result = venv_utils.where_installed("global_program") assert result == (2, None, str(fake_exe)) @@ -275,9 +275,9 @@ def test_integr_where_installed_only_global_with_venv( """Test where_installed with only global existing program and with venv.""" #: create fake venv dir fake_venv = tmp_path / ".venv" - monkeypatch.setattr(vu, "get_venv_path", lambda: fake_venv) + monkeypatch.setattr(venv_utils, "get_venv_path", lambda: fake_venv) #: create fake venv bin dir - fake_venv_bin = fake_venv / vu.OS_BIN + fake_venv_bin = fake_venv / venv_utils.OS_BIN fake_venv_bin.mkdir(parents=True) #: create fake global bin dir fake_glob_bin = tmp_path / "bin" @@ -290,7 +290,7 @@ def test_integr_where_installed_only_global_with_venv( #: adjust PATH os.environ["PATH"] = str(fake_venv_bin) + os.pathsep + str(fake_glob_bin) - result = vu.where_installed("global_program") + result = venv_utils.where_installed("global_program") assert result == (2, None, str(fake_exe)) @@ -299,9 +299,9 @@ def test_where_installed_both(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) - """Test where_installed with only global existing program and with venv.""" #: create fake venv dir fake_venv = tmp_path / ".venv" - monkeypatch.setattr(vu, "get_venv_path", lambda: fake_venv) + monkeypatch.setattr(venv_utils, "get_venv_path", lambda: fake_venv) #: create fake venv bin dir - fake_venv_bin = fake_venv / vu.OS_BIN + fake_venv_bin = fake_venv / venv_utils.OS_BIN #: create fake exe file in venv bin dir venv_fake_exe = fake_venv_bin / "program" #: create fake global bin dir @@ -311,12 +311,12 @@ def test_where_installed_both(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) - #: adjust PATH os.environ["PATH"] = str(fake_venv_bin) + os.pathsep + str(fake_glob_bin) monkeypatch.setattr( - vu.shutil, + venv_utils.shutil, "which", lambda _, path=None: str(glob_fake_exe) if path else str(venv_fake_exe), ) - result = vu.where_installed("program") + result = venv_utils.where_installed("program") assert result == (3, str(venv_fake_exe), str(glob_fake_exe)) @@ -327,9 +327,9 @@ def test_integr_where_installed_both( """Test where_installed with only global existing program and with venv.""" #: create fake venv dir fake_venv = tmp_path / ".venv" - monkeypatch.setattr(vu, "get_venv_path", lambda: fake_venv) + monkeypatch.setattr(venv_utils, "get_venv_path", lambda: fake_venv) #: create fake venv bin dir - fake_venv_bin = fake_venv / vu.OS_BIN + fake_venv_bin = fake_venv / venv_utils.OS_BIN fake_venv_bin.mkdir(parents=True) #: create fake exe file in venv bin dir program = "program" if sys.platform != "win32" else "program.EXE" @@ -346,6 +346,6 @@ def test_integr_where_installed_both( #: adjust PATH os.environ["PATH"] = str(fake_venv_bin) + os.pathsep + str(fake_glob_bin) - result = vu.where_installed("program") + result = venv_utils.where_installed("program") assert result == (3, str(venv_fake_exe), str(glob_fake_exe))