summaryrefslogtreecommitdiff
path: root/src/tox/tox_env/api.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/tox/tox_env/api.py')
-rw-r--r--src/tox/tox_env/api.py148
1 files changed, 89 insertions, 59 deletions
diff --git a/src/tox/tox_env/api.py b/src/tox/tox_env/api.py
index 25f29226..841e09c8 100644
--- a/src/tox/tox_env/api.py
+++ b/src/tox/tox_env/api.py
@@ -1,15 +1,16 @@
"""
Defines the abstract base traits of a tox environment.
"""
-import itertools
import logging
import os
+import re
import shutil
import sys
from abc import ABC, abstractmethod
from pathlib import Path
-from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Union, cast
+from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Union
+from tox.config.main import Config
from tox.config.sets import ConfigSet
from tox.execute.api import Execute, Outcome
from tox.execute.request import ExecuteRequest
@@ -19,23 +20,6 @@ from .info import Info
if TYPE_CHECKING:
from tox.config.cli.parser import Parsed
-if sys.platform == "win32":
- PASS_ENV_ALWAYS = [
- "SYSTEMDRIVE", # needed for pip6
- "SYSTEMROOT", # needed for python's crypto module
- "PATHEXT", # needed for discovering executables
- "COMSPEC", # needed for distutils cygwin compiler
- "PROCESSOR_ARCHITECTURE", # platform.machine()
- "USERPROFILE", # needed for `os.path.expanduser()`
- "MSYSTEM", # controls paths printed format
- "TEMP",
- "TMP",
- ]
-else:
- PASS_ENV_ALWAYS = [
- "TMPDIR",
- ]
-
class ToxEnv(ABC):
def __init__(self, conf: ConfigSet, core: ConfigSet, options: "Parsed"):
@@ -47,7 +31,7 @@ class ToxEnv(ABC):
self._cache = Info(self.conf["env_dir"])
self._paths: List[Path] = []
self.logger = logging.getLogger(self.conf["env_name"])
- self._env_vars: Dict[str, str] = {}
+ self._env_vars: Optional[Dict[str, str]] = None
def __repr__(self) -> str:
return f"{self.__class__.__name__}(name={self.conf['env_name']})"
@@ -63,18 +47,6 @@ class ToxEnv(ABC):
value=self.conf.name,
)
self.conf.add_config(
- keys=["set_env", "setenv"],
- of_type=Dict[str, str],
- default={},
- desc="environment variables to set when running commands in the tox environment",
- )
- self.conf.add_config(
- keys=["pass_env", "passenv"],
- of_type=List[str],
- default=[],
- desc="environment variables to pass on to the tox environment",
- )
- self.conf.add_config(
keys=["env_dir", "envdir"],
of_type=Path,
default=lambda conf, name: conf.core["work_dir"] / conf[name]["env_name"],
@@ -87,47 +59,106 @@ class ToxEnv(ABC):
desc="a folder that is always reset at the start of the run",
)
+ def set_env_post_process(values: Dict[str, str], config: Config) -> Dict[str, str]:
+ env = self.default_set_env()
+ env.update(values)
+ return env
+
+ self.conf.add_config(
+ keys=["set_env", "setenv"],
+ of_type=Dict[str, str],
+ default={},
+ desc="environment variables to set when running commands in the tox environment",
+ post_process=set_env_post_process,
+ )
+
+ def pass_env_post_process(values: List[str], config: Config) -> List[str]:
+ values.extend(self.default_pass_env())
+ return sorted(list({k: None for k in values}.keys()))
+
+ self.conf.add_config(
+ keys=["pass_env", "passenv"],
+ of_type=List[str],
+ default=[],
+ desc="environment variables to pass on to the tox environment",
+ post_process=pass_env_post_process,
+ )
+
+ def default_set_env(self) -> Dict[str, str]:
+ return {}
+
+ def default_pass_env(self) -> List[str]:
+ env = [
+ "https_proxy",
+ "http_proxy",
+ "no_proxy",
+ ]
+ if sys.platform == "win32":
+ env.extend(
+ [
+ "TEMP",
+ "TMP",
+ ]
+ )
+ else:
+ env.append("TMPDIR")
+ return env
+
def setup(self) -> None:
"""
1. env dir exists
2. contains a runner with the same type.
"""
- env_tmp_dir = cast(Path, self.conf["env_tmp_dir"])
- if env_tmp_dir.exists():
- shutil.rmtree(str(env_tmp_dir), ignore_errors=True)
- env_dir = cast(Path, self.conf["env_dir"])
+ env_dir: Path = self.conf["env_dir"]
conf = {"name": self.conf.name, "type": type(self).__name__}
- with self._cache.compare(conf, ToxEnv.__name__) as (eq, old):
- try:
- if eq is True:
- return
- # if either the name or type changed and already exists start over
- self.clean()
- finally:
- env_dir.mkdir(exist_ok=True, parents=True)
+ try:
+ with self._cache.compare(conf, ToxEnv.__name__) as (eq, old):
+ try:
+ if eq is True:
+ return
+ # if either the name or type changed and already exists start over
+ self.clean()
+ finally:
+ env_dir.mkdir(exist_ok=True, parents=True)
+ finally:
+ self._handle_env_tmp_dir()
+
+ def _handle_env_tmp_dir(self) -> None:
+ """Ensure exists and empty"""
+ env_tmp_dir: Path = self.conf["env_tmp_dir"]
+ if env_tmp_dir.exists():
+ logging.debug("removing %s", env_tmp_dir)
+ shutil.rmtree(env_tmp_dir, ignore_errors=True)
+ env_tmp_dir.mkdir(parents=True)
def clean(self) -> None:
- env_dir = self.conf["env_dir"]
+ env_dir: Path = self.conf["env_dir"]
if env_dir.exists():
- logging.info("removing %s", env_dir)
- shutil.rmtree(cast(Path, env_dir))
+ logging.info("remove tox env folder %s", env_dir)
+ shutil.rmtree(env_dir)
+ self._cache.reset()
@property
def environment_variables(self) -> Dict[str, str]:
- if self._env_vars:
+ if self._env_vars is not None:
return self._env_vars
- pass_env: List[str] = self.conf["pass_env"]
- pass_env.extend(PASS_ENV_ALWAYS)
+ result: Dict[str, str] = {}
+ pass_env: List[str] = self.conf["pass_env"]
+ glob_pass_env = [re.compile(e.replace("*", ".*")) for e in pass_env if "*" in e]
+ literal_pass_env = [e for e in pass_env if "*" not in e]
+ for env in literal_pass_env:
+ if env in os.environ:
+ result[env] = os.environ[env]
+ if glob_pass_env:
+ for env, value in os.environ.items():
+ if any(g.match(env) is not None for g in glob_pass_env):
+ result[env] = value
set_env: Dict[str, str] = self.conf["set_env"]
- for key, value in os.environ.items():
- if key in pass_env:
- set_env[key] = value
- self._env_vars.update(set_env)
- self._env_vars["PATH"] = os.pathsep.join(
- itertools.chain((str(i) for i in self._paths), os.environ.get("PATH", "").split(os.pathsep))
- )
- return self._env_vars
+ result.update(set_env)
+ result["PATH"] = os.pathsep.join([str(i) for i in self._paths] + os.environ.get("PATH", "").split(os.pathsep))
+ self._env_vars = result
+ return result
def execute(
self,
@@ -143,7 +174,6 @@ class ToxEnv(ABC):
request = ExecuteRequest(cmd, cwd, self.environment_variables, allow_stdin)
self.logger.warning("%s run => %s$ %s", self.conf["env_name"], request.cwd, request.shell_cmd)
outcome = self._executor(request=request, show_on_standard=show_on_standard, colored=self.options.colored)
- self.logger.info("done => code %d in %s for %s", outcome.exit_code, outcome.elapsed, outcome.shell_cmd)
return outcome
@staticmethod