diff options
author | Bernát Gábor <bgabor8@bloomberg.net> | 2021-01-21 09:42:45 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-21 09:42:45 +0000 |
commit | 1476fa617aca7563201b62df495767fa7bf40b70 (patch) | |
tree | 1369506b9bb58a75fccf3c58a602d1df3b066514 /src | |
parent | 5f172bc47b85dc24c706a2f33c3917089e0554f4 (diff) | |
download | tox-git-1476fa617aca7563201b62df495767fa7bf40b70.tar.gz |
Add support for virtualenv configuration options (#1862)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/tox/config/cli/parser.py | 2 | ||||
-rw-r--r-- | src/tox/execute/local_sub_process/read_via_thread_windows.py | 2 | ||||
-rw-r--r-- | src/tox/plugin/manager.py | 2 | ||||
-rw-r--r-- | src/tox/tox_env/api.py | 18 | ||||
-rw-r--r-- | src/tox/tox_env/python/api.py | 10 | ||||
-rw-r--r-- | src/tox/tox_env/python/virtual_env/api.py | 69 | ||||
-rw-r--r-- | src/tox/tox_env/python/virtual_env/package/api.py | 6 |
7 files changed, 81 insertions, 28 deletions
diff --git a/src/tox/config/cli/parser.py b/src/tox/config/cli/parser.py index b1804793..f47029ba 100644 --- a/src/tox/config/cli/parser.py +++ b/src/tox/config/cli/parser.py @@ -87,7 +87,7 @@ class HelpFormatter(ArgumentDefaultsHelpFormatter): text: str = super()._get_help_string(action) or "" # noqa if hasattr(action, "default_source"): default = " (default: %(default)s)" - if text.endswith(default): + if text.endswith(default): # pragma: no branch text = f"{text[: -len(default)]} (default: %(default)s -> from %(default_source)s)" return text diff --git a/src/tox/execute/local_sub_process/read_via_thread_windows.py b/src/tox/execute/local_sub_process/read_via_thread_windows.py index 9bec8962..1f4d29a1 100644 --- a/src/tox/execute/local_sub_process/read_via_thread_windows.py +++ b/src/tox/execute/local_sub_process/read_via_thread_windows.py @@ -15,7 +15,7 @@ class ReadViaThreadWindows(ReadViaThread): # pragma: win32 cover def __init__(self, file_no: int, handler: Callable[[bytes], None], name: str, drain: bool) -> None: super().__init__(file_no, handler, name, drain) self.closed = False - self._ov: Optional[_overlapped.Overlapped] = None # type: ignore[no-any-unimported] + self._ov: Optional[_overlapped.Overlapped] = None self._waiting_for_read = False def _read_stream(self) -> None: diff --git a/src/tox/plugin/manager.py b/src/tox/plugin/manager.py index ee3e42f8..55edb57c 100644 --- a/src/tox/plugin/manager.py +++ b/src/tox/plugin/manager.py @@ -21,7 +21,7 @@ from . import NAME, spec class Plugin: def __init__(self) -> None: - self.manager: pluggy.PluginManager = pluggy.PluginManager(NAME) # type: ignore[no-any-unimported] + self.manager: pluggy.PluginManager = pluggy.PluginManager(NAME) self.manager.add_hookspecs(spec) internal_plugins = ( diff --git a/src/tox/tox_env/api.py b/src/tox/tox_env/api.py index fe8888a5..1bff9fec 100644 --- a/src/tox/tox_env/api.py +++ b/src/tox/tox_env/api.py @@ -210,10 +210,26 @@ class ToxEnv(ABC): set_env: SetEnv = self.conf["set_env"] for key in set_env: result[key] = set_env.load(key) - result["PATH"] = os.pathsep.join([str(i) for i in self._paths] + os.environ.get("PATH", "").split(os.pathsep)) + result["PATH"] = self.paths_env() self._env_vars = result return result + @property + def paths(self) -> List[Path]: + return self._paths + + @paths.setter + def paths(self, value: List[Path]) -> None: + self._paths = value + if self._env_vars is not None: # pragma: no branch # also update the environment variables with the new value + self._env_vars["PATH"] = self.paths_env() + + def paths_env(self) -> str: + # remove duplicates and prepend the tox env paths + values = dict.fromkeys(str(i) for i in self.paths) + values.update(dict.fromkeys(os.environ.get("PATH", "").split(os.pathsep))) + return os.pathsep.join(values) + def execute( self, cmd: Sequence[Union[Path, str]], diff --git a/src/tox/tox_env/python/api.py b/src/tox/tox_env/python/api.py index f2950dd7..cc2ffd68 100644 --- a/src/tox/tox_env/python/api.py +++ b/src/tox/tox_env/python/api.py @@ -158,9 +158,13 @@ class Python(ToxEnv, ABC): with self._cache.compare(conf, Python.__name__) as (eq, old): if eq is False: # if changed create self.create_python_env() - self._paths = self.paths() + self.paths = self.python_env_paths() # now that the environment exist we can add them to the path super().setup() + @abstractmethod + def python_env_paths(self) -> List[Path]: + raise NotImplementedError + def setup_has_been_done(self) -> None: """called when setup is done""" super().setup_has_been_done() @@ -228,10 +232,6 @@ class Python(ToxEnv, ABC): raise NotImplementedError @abstractmethod - def paths(self) -> List[Path]: - raise NotImplementedError - - @abstractmethod def install_python_packages(self, packages: PythonDeps, of_type: str, no_deps: bool = False) -> None: raise NotImplementedError diff --git a/src/tox/tox_env/python/virtual_env/api.py b/src/tox/tox_env/python/virtual_env/api.py index 5af0a504..fb28a5b7 100644 --- a/src/tox/tox_env/python/virtual_env/api.py +++ b/src/tox/tox_env/python/virtual_env/api.py @@ -10,6 +10,7 @@ from virtualenv.create.creator import Creator from virtualenv.run.session import Session from tox.config.cli.parser import DEFAULT_VERBOSITY, Parsed +from tox.config.loader.str_convert import StrConvert from tox.config.sets import CoreConfigSet, EnvConfigSet from tox.execute.api import Execute, Outcome, StdinSource from tox.execute.local_sub_process import LocalSubProcessExecutor @@ -25,9 +26,38 @@ class VirtualEnv(Python, ABC): def __init__( self, conf: EnvConfigSet, core: CoreConfigSet, options: Parsed, journal: EnvJournal, log_handler: ToxHandler ) -> None: - self._virtualenv_session: Optional[Session] = None # type: ignore[no-any-unimported] + self._virtualenv_session: Optional[Session] = None super().__init__(conf, core, options, journal, log_handler) + def register_config(self) -> None: + super().register_config() + self.conf.add_config( + keys=["system_site_packages", "sitepackages"], + of_type=bool, + default=lambda conf, name: StrConvert().to_bool( + self.environment_variables.get("VIRTUALENV_SYSTEM_SITE_PACKAGES", "False") + ), + desc="create virtual environments that also have access to globally installed packages.", + ) + self.conf.add_config( + keys=["always_copy", "alwayscopy"], + of_type=bool, + default=lambda conf, name: StrConvert().to_bool( + self.environment_variables.get( + "VIRTUALENV_COPIES", self.environment_variables.get("VIRTUALENV_ALWAYS_COPY", "False") + ) + ), + desc="force virtualenv to always copy rather than symlink", + ) + self.conf.add_config( + keys=["download"], + of_type=bool, + default=lambda conf, name: StrConvert().to_bool( + self.environment_variables.get("VIRTUALENV_DOWNLOAD", "False") + ), + desc="true if you want virtualenv to upgrade pip/wheel/setuptools to the latest version", + ) + def default_pass_env(self) -> List[str]: env = super().default_pass_env() env.append("PIP_*") # we use pip as installer @@ -37,28 +67,37 @@ class VirtualEnv(Python, ABC): def default_set_env(self) -> Dict[str, str]: env = super().default_set_env() env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1" - env["VIRTUALENV_NO_PERIODIC_UPDATE"] = "1" return env def build_executor(self) -> Execute: return LocalSubProcessExecutor(self.options.is_colored) @property - def session(self) -> Session: # type: ignore[no-any-unimported] + def session(self) -> Session: if self._virtualenv_session is None: - args = [ - "--clear", - "--no-periodic-update", - str(cast(Path, self.conf["env_dir"])), - ] - base_python: List[str] = self.conf["base_python"] - for base in base_python: - args.extend(["-p", base]) - self._virtualenv_session = session_via_cli(args, setup_logging=False) + self._virtualenv_session = session_via_cli( + [str(cast(Path, self.conf["env_dir"]))], + options=None, + setup_logging=False, + env=self.virtualenv_env_vars(), + ) return self._virtualenv_session + def virtualenv_env_vars(self) -> Dict[str, str]: + env = self.environment_variables.copy() + base_python: List[str] = self.conf["base_python"] + if "VIRTUALENV_CLEAR" not in env: + env["VIRTUALENV_CLEAR"] = "True" + if "VIRTUALENV_NO_PERIODIC_UPDATE" not in env: + env["VIRTUALENV_NO_PERIODIC_UPDATE"] = "True" + env["VIRTUALENV_SYSTEM_SITE_PACKAGES"] = str(self.conf["system_site_packages"]) + env["VIRTUALENV_COPIES"] = str(self.conf["always_copy"]) + env["VIRTUALENV_DOWNLOAD"] = str(self.conf["download"]) + env["VIRTUALENV_PYTHON"] = "\n".join(base_python) + return env + @property - def creator(self) -> Creator: # type: ignore[no-any-unimported] + def creator(self) -> Creator: return self.session.creator def create_python_env(self) -> None: @@ -79,10 +118,10 @@ class VirtualEnv(Python, ABC): extra_version_info=None, ) - def paths(self) -> List[Path]: + def python_env_paths(self) -> List[Path]: """Paths to add to the executable""" # we use the original executable as shims may be somewhere else - return list({self.creator.bin_dir, self.creator.script_dir}) + return list(dict.fromkeys((self.creator.bin_dir, self.creator.script_dir))) def env_site_package_dir(self) -> Path: return cast(Path, self.creator.purelib) diff --git a/src/tox/tox_env/python/virtual_env/package/api.py b/src/tox/tox_env/python/virtual_env/package/api.py index 8bf28c5b..b3a0a11b 100644 --- a/src/tox/tox_env/python/virtual_env/package/api.py +++ b/src/tox/tox_env/python/virtual_env/package/api.py @@ -90,7 +90,7 @@ class Pep517VirtualEnvPackage(VirtualEnv, PythonPackage, Frontend): ) -> None: VirtualEnv.__init__(self, conf, core, options, journal, log_handler) Frontend.__init__(self, *Frontend.create_args_from_folder(core["tox_root"])) - self._distribution_meta: Optional[PathDistribution] = None # type: ignore[no-any-unimported] + self._distribution_meta: Optional[PathDistribution] = None self._build_requires: Optional[Tuple[Requirement]] = None self._build_wheel_cache: Optional[WheelResult] = None self._backend_executor: Optional[LocalSubProcessPep517Executor] = None @@ -217,9 +217,7 @@ class Pep517VirtualEnvPackage(VirtualEnv, PythonPackage, Frontend): return self._package_dependencies @staticmethod - def discover_package_dependencies( # type: ignore[no-any-unimported] - meta: PathDistribution, extras: Set[str] - ) -> List[Requirement]: + def discover_package_dependencies(meta: PathDistribution, extras: Set[str]) -> List[Requirement]: result: List[Requirement] = [] requires = meta.requires or [] for req_str in requires: |