def tox_testenv_create(venv, action): """ Python is already installed, we just need to handle dependency installs, there are two major phases, we take care of 1 here: 1. extend base image -> create env image 1. install pip/setuptools/wheel 2. install dependencies 3. develop -> copy folder - install via -e non-develop -> copy sdist - install 2. run: 1. start container from env image and mount {toxinidir} under 2. install package (either develop or sdist) 3. run commands one by one 4. stop and remove container """ with safe_package_view(venv) as context: cwd = os.getcwd() os.chdir(context) try: image_id = build_image(venv, context, action) except Exception as exception: reporter.error(f"could not build image {exception}") # pragma: no cover raise # pragma: no cover finally: os.chdir(cwd) image = set_docker_image_tag(image_id, venv) if venv.envconfig.image is not None: # already had a previous image (e.g. different python) try: CLIENT.images.remove(image=venv.envconfig.image.id, force=True) except Exception as exception: # pragma: no cover reporter.warning(f"could not delete image {exception}") # pragma: no cover venv.envconfig.image = image return True
def get_latest_version_of_package(package_spec): if not os.path.isabs(str(package_spec)): return package_spec p = py.path.local(package_spec) if p.check(): return p if not p.dirpath().check(dir=1): raise tox.exception.MissingDirectory(p.dirpath()) reporter.info("determining {}".format(p)) candidates = p.dirpath().listdir(p.basename) if len(candidates) == 0: raise MissingDependency(package_spec) if len(candidates) > 1: version_package = [] for filename in candidates: version = get_version_from_filename(filename.basename) if version is not None: version_package.append((version, filename)) else: reporter.warning("could not determine version of: {}".format( str(filename))) if not version_package: raise tox.exception.MissingDependency(package_spec) version_package.sort() _, package_with_largest_version = version_package[-1] return package_with_largest_version else: return candidates[0]
def ensure_pip_os_environ_ok(self, env): for key in ( "PIP_RESPECT_VIRTUALENV", "PIP_REQUIRE_VIRTUALENV", "__PYVENV_LAUNCHER__", ): env.pop(key, None) if all("PYTHONPATH" not in i for i in (self.envconfig.passenv, self.envconfig.setenv)): # If PYTHONPATH not explicitly asked for, remove it. if "PYTHONPATH" in env: if sys.version_info < (3, 4) or bool(env["PYTHONPATH"]): # https://docs.python.org/3/whatsnew/3.4.html#changes-in-python-command-behavior # In a posix shell, setting the PATH environment variable to an empty value is # equivalent to not setting it at all. reporter.warning( "Discarding $PYTHONPATH from environment, to override " "specify PYTHONPATH in 'passenv' in your configuration.", ) env.pop("PYTHONPATH") # installing packages at user level may mean we're not installing inside the venv env["PIP_USER"] = "******" # installing without dependencies may lead to broken packages env["PIP_NO_DEPS"] = "0"
def _check_external_allowed_and_warn(self, path): if not self.is_allowed_external(path): reporter.warning( "test command found but not installed in testenv\n" " cmd: {}\n" " env: {}\n" "Maybe you forgot to specify a dependency? " "See also the whitelist_externals envconfig setting.\n\n" "DEPRECATION WARNING: this will be an error in tox 4 and above!" .format(path, self.envconfig.envdir))
def wheel_build_package(config, session, venv): if config.isolated_build: reporter.warning( "Disabling isolated_build, not supported with wheels.") pep517 = venv.envconfig.wheel_pep517 if pep517: wheel_package = wheel_build_pep517(config, session, venv) else: wheel_package = wheel_build_legacy(config, session, venv) return wheel_package
def tox_runtest(venv, redirect): _init_pipenv_environ() pipfile_path = _clone_pipfile(venv) action = venv.new_action("runtests") with wrap_pipenv_environment(venv, pipfile_path): action.setactivity( "runtests", "PYTHONHASHSEED=%r" % os.environ.get("PYTHONHASHSEED") ) for i, argv in enumerate(venv.envconfig.commands): # have to make strings as _pcall changes argv[0] to a local() # happens if the same environment is invoked twice cwd = venv.envconfig.changedir message = "commands[%s] | %s" % (i, " ".join([str(x) for x in argv])) action.setactivity("runtests", message) # check to see if we need to ignore the return code # if so, we need to alter the command line arguments if argv[0].startswith("-"): ignore_ret = True if argv[0] == "-": del argv[0] else: argv[0] = argv[0].lstrip("-") else: ignore_ret = False args = [sys.executable, "-m", "pipenv", "run"] + argv try: venv._pcall( args, venv=False, cwd=cwd, action=action, redirect=redirect, ignore_ret=ignore_ret ) except tox.exception.InvocationError as err: if venv.envconfig.ignore_outcome: reporter.warning( "command failed but result from testenv is ignored\n" " cmd: %s" % (str(err),) ) venv.status = "ignored failed command" continue # keep processing commands reporter.error(str(err)) venv.status = "commands failed" if not venv.envconfig.ignore_errors: break # Don't process remaining commands except KeyboardInterrupt: venv.status = "keyboardinterrupt" reporter.error(venv.status) raise return True
def tox_configure(config: config.Config): """ Parse the command line and ini options. """ args: List[List[str]] = [[]] while config.args: val: str = config.args.pop(0) if val == "--": break elif val.startswith('-'): args.append([val]) else: args[-1].append(val) envlist_set = False envlist_name_set = False # Parse envlists ini_config: "IniConfig" = config._cfg envlists: Dict[str, List[str]] = {} for envlist_name, envlist in ini_config.sections.get("envlists", {}).items(): envlist_elements = DELIMITERS.split(envlist) expanded_envlist = list( chain.from_iterable(map(braceexpand, envlist_elements))) envlists[envlist_name] = list(filter(None, expanded_envlist)) for idx, arg in enumerate(args): if arg and arg[0] in {"-e", "--envlist"}: envlist_set = True elif arg and arg[0] in option_names: envlist_name_set = True for idx, arg in enumerate(args): if arg and arg[0] in option_names: if envlist_name_set and envlist_set: reporter.warning( f"Ignoring '{' '.join(args.pop(idx))}' option as '-e / --envlist' option given.", ) elif envlist_name_set and arg[1] in envlists: config.envlist = envlists[arg[1]] elif envlist_name_set: config._parser.argparser.error( f"Unknown envlist {arg[1]!r}. (envlists are {word_join(envlists, use_repr=True)})" ) config.args = list(chain.from_iterable(args)) return config
def _pcall( self, args, cwd, venv=True, is_test_command=False, action=None, redirect=True, ignore_ret=False, returnout=False, env=None, capture_err=True, ): if env is None: env = self._get_os_environ(is_test_command=is_test_command) # construct environment variables env.pop("VIRTUALENV_PYTHON", None) bin_dir = str(self.envconfig.envbindir) path = self.envconfig.setenv.get("PATH") or os.environ["PATH"] env["PATH"] = os.pathsep.join([bin_dir, path]) reporter.verbosity2("setting PATH={}".format(env["PATH"])) # get command try: args[0] = self.getcommandpath(args[0], venv, cwd) except tox.exception.InvocationError: if ignore_ret: self.status = getattr(self, "status", 0) msg = "command not found but explicitly ignored" reporter.warning("{}\ncmd: {}".format(msg, args[0])) return "" # in case it's returnout else: raise if sys.platform != "win32" and "TOX_LIMITED_SHEBANG" in os.environ: args = prepend_shebang_interpreter(args) cwd.ensure(dir=1) # ensure the cwd exists return action.popen( args, cwd=cwd, env=env, redirect=redirect, ignore_ret=ignore_ret, returnout=returnout, report_fail=not is_test_command, capture_err=capture_err, )
def ensure_pip_os_environ_ok(self): for key in ("PIP_RESPECT_VIRTUALENV", "PIP_REQUIRE_VIRTUALENV", "__PYVENV_LAUNCHER__"): os.environ.pop(key, None) if "PYTHONPATH" not in self.envconfig.passenv: # If PYTHONPATH not explicitly asked for, remove it. if "PYTHONPATH" in os.environ: reporter.warning( "Discarding $PYTHONPATH from environment, to override " "specify PYTHONPATH in 'passenv' in your configuration.") os.environ.pop("PYTHONPATH") # installing packages at user level may mean we're not installing inside the venv os.environ["PIP_USER"] = "******" # installing without dependencies may lead to broken packages os.environ["PIP_NO_DEPS"] = "0"
def show_deprecation_warning_for_old_style_pypy_config(version): # type: (str) -> None if version not in {"pypy2", "pypy3"}: return warning( """PendingDeprecationWarning Support of old-style PyPy config keys will be removed in tox-gh-actions v3. Please use "pypy-2" and "pypy-3" instead of "pypy2" and "pypy3". Example of tox.ini: [gh-actions] python = pypy-2: pypy2 pypy-3: pypy3 # The followings won't work with tox-gh-actions v3 # pypy2: pypy2 # pypy3: pypy3 """ )
def acquire_package(config, session): """acquire a source distribution (either by loading a local file or triggering a build)""" if not config.option.sdistonly and (config.sdistsrc or config.option.installpkg): path = get_local_package(config) else: try: path = build_package(config, session) except tox.exception.InvocationError as exception: error("FAIL could not package project - v = {!r}".format(exception)) return None sdist_file = config.distshare.join(path.basename) if sdist_file != path: info("copying new sdistfile to {!r}".format(str(sdist_file))) try: sdist_file.dirpath().ensure(dir=1) except py.error.Error: warning("could not copy distfile to {}".format(sdist_file.dirpath())) else: path.copy(sdist_file) return path
def test( self, redirect=False, name="run-test", commands=None, ignore_outcome=None, ignore_errors=None, display_hash_seed=False, ): if commands is None: commands = self.envconfig.commands if ignore_outcome is None: ignore_outcome = self.envconfig.ignore_outcome if ignore_errors is None: ignore_errors = self.envconfig.ignore_errors with self.new_action(name) as action: cwd = self.envconfig.changedir if display_hash_seed: env = self._get_os_environ(is_test_command=True) # Display PYTHONHASHSEED to assist with reproducibility. action.setactivity( name, "PYTHONHASHSEED={!r}".format(env.get("PYTHONHASHSEED"))) for i, argv in enumerate(commands): # have to make strings as _pcall changes argv[0] to a local() # happens if the same environment is invoked twice message = "commands[{}] | {}".format( i, " ".join([pipes.quote(str(x)) for x in argv])) action.setactivity(name, message) # check to see if we need to ignore the return code # if so, we need to alter the command line arguments if argv[0].startswith("-"): ignore_ret = True if argv[0] == "-": del argv[0] else: argv[0] = argv[0].lstrip("-") else: ignore_ret = False try: self._pcall( argv, cwd=cwd, action=action, redirect=redirect, ignore_ret=ignore_ret, is_test_command=True, ) except tox.exception.InvocationError as err: if ignore_outcome: msg = "command failed but result from testenv is ignored\ncmd:" reporter.warning("{} {}".format(msg, err)) self.status = "ignored failed command" continue # keep processing commands reporter.error(str(err)) self.status = "commands failed" if not ignore_errors: break # Don't process remaining commands except KeyboardInterrupt: self.status = "keyboardinterrupt" raise
def tox_configure(config): """Check for the presence of the added options.""" if config.option.prepend_archraw_factor: from cpuinfo.cpuinfo import DataSource # noqa archraw_factor_name = DataSource.arch_string_raw.replace("-", "_").lower() if not config.option.prepend_factor: config.option.prepend_factor = [archraw_factor_name] else: config.option.prepend_factor.insert(0, archraw_factor_name) if config.option.prepend_cpuarch_factor: from cpuinfo.cpuinfo import _parse_arch, DataSource # noqa try: arch, _ = _parse_arch(DataSource.arch_string_raw) arch = arch.lower() if not config.option.prepend_factor: config.option.prepend_factor = [arch] else: config.option.prepend_factor.insert(0, arch) except Exception: archraw_factor_name = DataSource.arch_string_raw.replace( "-", "_").lower() warning('cpuarch not available for archraw "{}"'.format( archraw_factor_name)) if config.option.prepend_ostype_factor: from osinfo.osinfo import _get_os_type # noqa if not config.option.prepend_factor: config.option.prepend_factor = [_get_os_type().lower()] else: config.option.prepend_factor.insert(0, _get_os_type().lower()) if config.option.add_ci_factor and "CI" in os.environ: extra_factor = None if "APPVEYOR" in os.environ or "TRAVIS" in os.environ: config.option.prepend_username_factor = True elif "CIRRUS_CI" in os.environ: extra_factor = "cirrusci" else: extra_factor = "ci" if extra_factor: if not config.option.append_factor: config.option.append_factor = [extra_factor] else: config.option.append_factor.insert(0, extra_factor) if config.option.prepend_username_factor: import getpass # noqa username = getpass.getuser() if username: username = username.lower() if not config.option.prepend_factor: config.option.prepend_factor = [username] else: config.option.prepend_factor.insert(0, username) if config.option.prepend_factor: add_factors(config, config.option.prepend_factor, position=BEFORE) if config.option.append_factor: add_factors(config, config.option.append_factor, position=AFTER)
def wheel_build_package(config, session, venv): if config.isolated_build: reporter.warning("Disabling isolated_build, not supported with wheels.") return wheel_build(config, session, venv)