diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..0a18d5146 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +tidelift: pypi/tox diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f2bca78b..b9af5f6f7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,4 +24,4 @@ jobs: - name: Build package run: pyproject-build -s -w . -o dist - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.8.7 + uses: pypa/gh-action-pypi-publish@v1.8.8 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e8a033ba2..195c708ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black - repo: https://github.com/tox-dev/tox-ini-fmt @@ -14,22 +14,22 @@ repos: - id: tox-ini-fmt args: ["-p", "fix"] - repo: https://github.com/tox-dev/pyproject-fmt - rev: "0.12.1" + rev: "0.13.0" hooks: - id: pyproject-fmt - additional_dependencies: ["tox>=4.6.3"] + additional_dependencies: ["tox>=4.6.4"] - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v3.0.0-alpha.9-for-vscode" + rev: "v3.0.1" hooks: - id: prettier args: ["--print-width=120", "--prose-wrap=always"] - repo: https://github.com/asottile/blacken-docs - rev: 1.14.0 + rev: 1.15.0 hooks: - id: blacken-docs - additional_dependencies: [black==23.3] + additional_dependencies: [black==23.7] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.0.275" + rev: "v0.0.282" hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/docs/changelog.rst b/docs/changelog.rst index d92140a78..b6ba24f08 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,26 @@ Release History .. towncrier release notes start +v4.7.0 (2023-08-08) +------------------- + +Features - 4.7.0 +~~~~~~~~~~~~~~~~ +- Make --hashseed default to PYTHONHASHSEED, if defined - by :user:`paravoid`. + The main motivation for this is to able to set the hash seed when building the + documentation with "tox -e docs", and thus avoid embedding a random value in + the tox documentation for --help. This caused documentation builds to fail to + build reproducibly. (:issue:`2942`) + +Bugfixes - 4.7.0 +~~~~~~~~~~~~~~~~ +- Update a regular expression in tests to match the exception message in both Python 3.12 and older. (:issue:`3065`) + +Improved Documentation - 4.7.0 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- Fix broken links - by :user:`gaborbernat`. (:issue:`3072`) + + v4.6.4 (2023-07-06) ------------------- diff --git a/docs/config.rst b/docs/config.rst index b80de22d3..50e6aa242 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -15,8 +15,8 @@ Out of box tox supports three configuration locations prioritized in the followi 2. ``pyproject.toml``, 3. ``setup.cfg``. -With regards to the configuration format, at the moment we only support *ini-style*. ``tox.ini`` and ``setup.cfg`` are by -nature such files, while in ``pyproject.toml`` currently you can only inline the *ini-style* config. +With regards to the configuration format, at the moment we only support *ini-style*. ``tox.ini`` and ``setup.cfg`` are +by nature such files, while in ``pyproject.toml`` currently you can only inline the *ini-style* config. Note that ``setup.cfg`` requires the content to be under the ``tox:tox`` and ``testenv`` sections and is otherwise ignored. ``pyproject.toml`` on the other hand is in TOML format. However, one can inline the *ini-style* format under @@ -616,8 +616,9 @@ Python run :keys: package :version_added: 4.0 - When option can be one of ``wheel``, ``sdist``, ``editable``, ``editable-legacy``, ``skip``, or ``external``. If :ref:`use_develop` is - set this becomes a constant of ``editable``. If :ref:`skip_install` is set this becomes a constant of ``skip``. + When option can be one of ``wheel``, ``sdist``, ``editable``, ``editable-legacy``, ``skip``, or ``external``. If + :ref:`use_develop` is set this becomes a constant of ``editable``. If :ref:`skip_install` is set this becomes a + constant of ``skip``. .. conf:: @@ -767,8 +768,8 @@ Pip installer :version_added: 2.4 The ``list_dependencies_command`` setting is used for listing the packages installed into the virtual environment. - This command will be executed only if executing on Contionous Integrations is detected (for example set environment variable ``CI=1``) - or if journal is active. + This command will be executed only if executing on Continuous Integrations is detected (for example set environment + variable ``CI=1``) or if journal is active. .. conf:: @@ -782,11 +783,11 @@ Pip installer .. conf:: :keys: constrain_package_deps - :default: true + :default: false :version_added: 4.4.0 If ``constrain_package_deps`` is true, then tox will create and use ``{env_dir}{/}constraints.txt`` when installing - package dependnecies during ``install_package_deps`` stage. When this value is set to false, any conflicting package + package dependencies during ``install_package_deps`` stage. When this value is set to false, any conflicting package dependencies will override explicit dependencies and constraints passed to ``deps``. .. conf:: @@ -795,17 +796,16 @@ Pip installer :version_added: 4.4.0 When ``use_frozen_constraints`` is true, then tox will use the ``list_dependencies_command`` to enumerate package - versions in order to create ``{env_dir}{/}constraints.txt``. Otherwise the package specifications explicitly listed under - ``deps`` (or in requirements / constraints files referenced in ``deps``) will be used as the constraints. If + versions in order to create ``{env_dir}{/}constraints.txt``. Otherwise the package specifications explicitly listed + under ``deps`` (or in requirements / constraints files referenced in ``deps``) will be used as the constraints. If ``constrain_package_deps`` is false, then this setting has no effect. User configuration ------------------ -tox allows creation of user level config-file to modify default values of the CLI commands. -It is located in the OS-specific user config directory under ``tox/config.ini`` path, see ``tox --help`` output for exact location. -It can be changed via ``TOX_USER_CONFIG_FILE`` environment variable. -Example configuration: +tox allows creation of user level config-file to modify default values of the CLI commands. It is located in the +OS-specific user config directory under ``tox/config.ini`` path, see ``tox --help`` output for exact location. It can be +changed via ``TOX_USER_CONFIG_FILE`` environment variable. Example configuration: .. code-block:: ini @@ -820,7 +820,7 @@ through the ``{...}`` string-substitution pattern. The string inside the curly braces may reference a global or per-environment config key as described above. -In substitutions, the backslash character ``\`` will act as an escape when preceeding +In substitutions, the backslash character ``\`` will act as an escape when preceding ``{``, ``}``, ``:``, ``[``, or ``]``, otherwise the backslash will be reproduced literally:: diff --git a/docs/development.rst b/docs/development.rst index 3c0fb580d..b95d542de 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -5,10 +5,10 @@ Getting started --------------- -``tox`` is a volunteer maintained open source project and we welcome contributions of all forms. The sections -below will help you get started with development, testing, and documentation. We’re pleased that you are interested in -working on tox. This document is meant to get you setup to work on tox and to act as a guide and reference -to the development setup. If you face any issues during this process, please +``tox`` is a volunteer maintained open source project and we welcome contributions of all forms. The sections below will +help you get started with development, testing, and documentation. We’re pleased that you are interested in working on +tox. This document is meant to get you setup to work on tox and to act as a guide and reference to the development +setup. If you face any issues during this process, please :issue:`new?title=Trouble+with+development+environment` about it on the issue tracker. Setup @@ -44,8 +44,8 @@ The easiest way to do this is to generate the development tox environment, and t Running tests ~~~~~~~~~~~~~ -tox's tests are written using the :pypi:`pytest` test framework. :pypi:`tox` is used to automate the setup -and execution of tox's tests. +tox's tests are written using the :pypi:`pytest` test framework. :pypi:`tox` is used to automate the setup and execution +of tox's tests. To run tests locally execute: @@ -56,10 +56,9 @@ To run tests locally execute: This will run the test suite for the same Python version as under which ``tox`` is installed. Alternatively you can specify a specific version of Python by using the ``pyNN`` format, such as: ``py38``, ``pypy3``, etc. -``tox`` has been configured to forward any additional arguments it is given to ``pytest``. -This enables the use of pytest's -`rich CLI `_. As an example, you can -select tests using the various ways that pytest provides: +``tox`` has been configured to forward any additional arguments it is given to ``pytest``. This enables the use of +pytest's `rich CLI `_. As an example, +you can select tests using the various ways that pytest provides: .. code-block:: shell @@ -69,8 +68,8 @@ select tests using the various ways that pytest provides: tox -e py -- -k "test_extra" Some tests require additional dependencies to be run, such is the various shell activators (``bash``, ``fish``, -``powershell``, etc). The tests will be skipped automatically if the dependencies are not present. Please note however that in CI -all tests are run; so even if all tests succeed locally for you, they may still fail in the CI. +``powershell``, etc). The tests will be skipped automatically if the dependencies are not present. Please note however +that in CI all tests are run; so even if all tests succeed locally for you, they may still fail in the CI. Running linters ~~~~~~~~~~~~~~~ @@ -128,17 +127,15 @@ Contributing Submitting pull requests ~~~~~~~~~~~~~~~~~~~~~~~~ -Submit pull requests (PRs) against the ``main`` branch, providing a good description of what you're doing and why. You must -have legal permission to distribute any code you contribute to tox and it must be available under the MIT -License. Provide tests that cover your changes and run the tests locally first. tox -:ref:`supports ` multiple Python versions and operating systems. Any pull request must -consider and work on all these platforms. +Submit pull requests (PRs) against the ``main`` branch, providing a good description of what you're doing and why. You +must have legal permission to distribute any code you contribute to tox and it must be available under the MIT License. +Provide tests that cover your changes and run the tests locally first. tox :ref:`supports ` +multiple Python versions and operating systems. Any pull request must consider and work on all these platforms. -Pull requests should be small to facilitate review. Keep them self-contained, and limited in scope. `Studies have shown -`_ that review quality falls off as patch size -grows. Sometimes this will result in many small PRs to land a single large feature. In particular, pull requests must -not be treated as "feature branches", with ongoing development work happening within the PR. Instead, the feature should -be broken up into smaller, independent parts which can be reviewed and merged individually. +Pull requests should be small to facilitate review. Keep them self-contained, and limited in scope. Studies have shown +that review quality falls off as patch size grows. In particular, pull requests must not be treated as +"feature branches", with ongoing development work happening within the PR. Instead, the feature should be broken up into +smaller, independent parts which can be reviewed and merged individually. Additionally, avoid including "cosmetic" changes to code that is unrelated to your change, as these make reviewing the PR more difficult. Examples include re-flowing text in comments or documentation, or addition or removal of blank lines @@ -158,10 +155,10 @@ pull request. If needed, project maintainers can manually trigger a restart of a Changelog entries ~~~~~~~~~~~~~~~~~ -The ``changelog.rst`` file is managed using :pypi:`towncrier` and all changes must be accompanied by a -changelog entry. To add an entry to the changelog, first you need to have created an issue describing the -change you want to make. A pull request itself *may* function as such, but it is preferred to have a dedicated issue -(for example, in case the PR ends up rejected due to code quality reasons). +The ``changelog.rst`` file is managed using :pypi:`towncrier` and all changes must be accompanied by a changelog entry. +To add an entry to the changelog, first you need to have created an issue describing the change you want to make. A pull +request itself *may* function as such, but it is preferred to have a dedicated issue (for example, in case the PR ends +up rejected due to code quality reasons). There is no need to create an issue for trivial changes, e.g. for typo fixes. @@ -184,30 +181,30 @@ Contents of a changelog entry ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The content of this file is reStructuredText formatted text that will be used as the content of the changelog entry. -You do not need to reference the issue or PR numbers here as towncrier will automatically add a reference to all of -the affected issues when rendering the changelog. +You do not need to reference the issue or PR numbers here as towncrier will automatically add a reference to all of the +affected issues when rendering the changelog. In order to maintain a consistent style in the ``changelog.rst`` file, it is preferred to keep the entries to the point, in sentence case, shorter than 120 characters and in an imperative tone -- an entry should complete the sentence ``This change will …``. In rare cases, where one line is not enough, use a summary line in an imperative tone followed -by a blank line separating it from a description of the feature/change in one or more paragraphs, each wrapped -at 120 characters. Remember that a changelog entry is meant for end users and should only contain details relevant to an -end user. +by a blank line separating it from a description of the feature/change in one or more paragraphs, each wrapped at 120 +characters. Remember that a changelog entry is meant for end users and should only contain details relevant to an end +user. Becoming a maintainer ~~~~~~~~~~~~~~~~~~~~~ If you want to become an official maintainer, start by helping out. As a first step, we welcome you to triage issues on -tox's issue tracker. tox maintainers provide triage abilities to contributors once they have been around -for some time and contributed positively to the project. This is optional and highly recommended for becoming a -tox maintainer. Later, when you think you're ready, get in touch with one of the maintainers and they will -initiate a vote among the existing maintainers. +tox's issue tracker. tox maintainers provide triage abilities to contributors once they have been around for some time +and contributed positively to the project. This is optional and highly recommended for becoming a tox maintainer. Later, +when you think you're ready, get in touch with one of the maintainers and they will initiate a vote among the existing +maintainers. .. note:: - Upon becoming a maintainer, a person should be given access to various tox-related tooling across - multiple platforms. These are noted here for future reference by the maintainers: + Upon becoming a maintainer, a person should be given access to various tox-related tooling across multiple + platforms. These are noted here for future reference by the maintainers: - GitHub Push Access (provides also CI administration capabilities) - PyPI Publishing Access diff --git a/docs/faq.rst b/docs/faq.rst index 257c6a57d..ec5dd7030 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -78,8 +78,8 @@ directly. pip accepts environment variables as configuration flags, therefore th set_env = PIP_INDEX_URL = https://tox.wiki/pypi/simple -It's considered a best practice to allow the user to change the index server rather than hard code it, allowing them -to use for example a local cache when they are offline. Therefore, a better form of this would be: +It's considered a best practice to allow the user to change the index server rather than hard code it, allowing them to +use for example a local cache when they are offline. Therefore, a better form of this would be: .. code-block:: ini @@ -132,8 +132,8 @@ error to be raised when the package dependencies conflict with the test environm For stronger guarantees, set ``use_frozen_constraints = true`` in the test environment to generate the constraints file based on the exact versions enumerated by the ``list_dependencies_command`` (``pip freeze``). When using frozen constraints, if the package deps are incompatible with any previously installed dependency, an error will be raised. -To use constraints with url, path, or "extras" (``.[tests]``) deps, then you must ``use_frozen_constraints``, as -these types of deps are not valid constraints as specified (see pypa/pip#8210). +To use constraints with url, path, or "extras" (``.[tests]``) deps, then you must ``use_frozen_constraints``, as these +types of deps are not valid constraints as specified (see pypa/pip#8210). Note constraint files are a subset of requirement files. Therefore, it's valid to pass a constraint file wherever you can specify a requirement file. @@ -197,8 +197,8 @@ Customizing virtual environment creation ---------------------------------------- By default tox uses the :pypi:`virtualenv` to create Python virtual environments to run your tools in. To change how tox -creates virtual environments you can set environment variables to customize virtualenv. For example, to provision a given -pip version in the virtual environment you can set ``VIRTUALENV_PIP`` or to enable system site packages use the +creates virtual environments you can set environment variables to customize virtualenv. For example, to provision a +given pip version in the virtual environment you can set ``VIRTUALENV_PIP`` or to enable system site packages use the ``VIRTUALENV_SYSTEM_SITE_PACKAGES``: @@ -210,8 +210,8 @@ pip version in the virtual environment you can set ``VIRTUALENV_PIP`` or to enab VIRTUALENV_PIP==22.1 VIRTUALENV_SYSTEM_SITE_PACKAGES=true -Consult the :pypi:`virtualenv` project for supported values (any CLI flag for virtualenv, in all upper case, prefixed -by the ``VIRTUALENV_`` key). +Consult the :pypi:`virtualenv` project for supported values (any CLI flag for virtualenv, in all upper case, prefixed by +the ``VIRTUALENV_`` key). Building documentation with Sphinx ---------------------------------- @@ -224,8 +224,8 @@ We don't recommend using the Make and Batch file generated by Sphinx, as this ma platform specific. A better solution is to use tox to setup a documentation build environment and invoke sphinx inside it. This solution is cross platform. -For example if the sphinx file structure is under the ``docs`` folder the following configuration will generate -the documentation under ``.tox/docs_out/index.html`` and print out a link to the generated documentation: +For example if the sphinx file structure is under the ``docs`` folder the following configuration will generate the +documentation under ``.tox/docs_out/index.html`` and print out a link to the generated documentation: .. code-block:: ini @@ -270,8 +270,7 @@ substitution logic to avoid duplication: Understanding ``InvocationError`` exit codes -------------------------------------------- -When a command executed by tox fails, it always has a non-zero exit code and an ``InvocationError`` exception is -raised: +When a command executed by tox fails, it always has a non-zero exit code and an ``InvocationError`` exception is raised: .. code-block:: shell @@ -292,9 +291,9 @@ module, an additional hint is given: The signal numbers (e.g. 11 for a segmentation fault) can be found in the "Standard signals" section of the -`signal man page `_. -Their meaning is described in `POSIX signals `_. Beware -that programs may issue custom exit codes with any value, so their documentation should be consulted. +`signal man page `_. Their meaning is described in +`POSIX signals `_. Beware that programs may issue custom exit +codes with any value, so their documentation should be consulted. Sometimes, no exit code is given at all. An example may be found in @@ -304,8 +303,8 @@ Sometimes, no exit code is given at all. An example may be found in Access full logs ---------------- -If you want to access the full logs you need to write ``-q`` and ``-v`` as -individual tox arguments and avoid combining them into a single one. +If you want to access the full logs you need to write ``-q`` and ``-v`` as individual tox arguments and avoid combining +them into a single one. Running within a Docker container --------------------------------- @@ -354,12 +353,11 @@ Testing end-of-life Python versions ----------------------------------- ``tox`` uses ``virtualenv`` under its hood for managing virtual environments. +`Virtualenv 20.22.0 `_ dropped support for all +Python versions smaller or equal to Python 3.6. -`Virtualenv 20.22.0 `_ -dropped support for all Python versions smaller or equal to Python 3.6. - -If you need to test against e.g. Python 2.7, 3.5 or 3.6, you need to add the -following ``requires`` statement to your ``tox.ini`` configuration files. +If you need to test against e.g. Python 2.7, 3.5 or 3.6, you need to add the following ``requires`` statement to your +``tox.ini`` configuration files. .. code-block:: ini diff --git a/docs/installation.rst b/docs/installation.rst index 01c248abb..ac2f21946 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -40,7 +40,7 @@ sdist ~~~~~ When installing via a source distribution you need an installer that handles the :pep:`517` specification. In case of ``pip`` this is version ``18.0.0`` or later (released in July 2018). If you cannot upgrade your pip to support this you -need to ensure that the build requirements from :gh:`pyproject.toml ` are +need to ensure that the build requirements from :gh:`pyproject.toml ` are satisfied before triggering the installation. via ``setup.py`` diff --git a/pyproject.toml b/pyproject.toml index 5da0dcf15..5ce1823be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,12 +54,12 @@ dependencies = [ "filelock>=3.12.2", 'importlib-metadata>=6.7; python_version < "3.8"', "packaging>=23.1", - "platformdirs>=3.8", + "platformdirs>=3.9.1", "pluggy>=1.2", - "pyproject-api>=1.5.2", + "pyproject-api>=1.5.3", 'tomli>=2.0.1; python_version < "3.11"', - 'typing-extensions>=4.6.3; python_version < "3.8"', - "virtualenv>=20.23.1", + 'typing-extensions>=4.7.1; python_version < "3.8"', + "virtualenv>=20.24.1", ] optional-dependencies.docs = [ "furo>=2023.5.20", @@ -76,8 +76,8 @@ optional-dependencies.testing = [ "covdefaults>=2.3", "detect-test-pollution>=1.1.1", "devpi-process>=0.3.1", - "diff-cover>=7.6", - "distlib>=0.3.6", + "diff-cover>=7.7", + "distlib>=0.3.7", "flaky>=3.7", "hatch-vcs>=0.3", "hatchling>=1.17.1", diff --git a/src/tox/config/cli/parser.py b/src/tox/config/cli/parser.py index 25990f843..b06b10ae8 100644 --- a/src/tox/config/cli/parser.py +++ b/src/tox/config/cli/parser.py @@ -64,7 +64,7 @@ def get_type(action: Action) -> type[Any]: loc = locals() loc["Literal"] = Literal as_literal = f"Literal[{', '.join(repr(i) for i in action.choices)}]" - of_type = eval(as_literal, globals(), loc) # noqa: PGH001 + of_type = eval(as_literal, globals(), loc) # noqa: PGH001, S307 elif action.default is not None: of_type = type(action.default) elif isinstance(action, argparse._StoreConstAction) and action.const is not None: # noqa: SLF001 diff --git a/src/tox/config/loader/ini/replace.py b/src/tox/config/loader/ini/replace.py index 9d9373989..f72db8f16 100644 --- a/src/tox/config/loader/ini/replace.py +++ b/src/tox/config/loader/ini/replace.py @@ -255,7 +255,7 @@ def replace_reference( # noqa: PLR0912, C901 if isinstance(src, SectionProxy): return loader.process_raw(conf, conf_args.env_name, src[key]) value = src.load(key, conf_args.chain) - except KeyError as exc: # if fails, keep trying maybe another source can satisfy + except KeyError as exc: # if fails, keep trying maybe another source can satisfy # noqa: PERF203 exception = exc else: as_str, _ = stringify(value) diff --git a/src/tox/config/main.py b/src/tox/config/main.py index a073911bb..50a026b66 100644 --- a/src/tox/config/main.py +++ b/src/tox/config/main.py @@ -140,7 +140,7 @@ def get_section_config( # noqa: PLR0913 if for_env is not None: conf_set.loaders.extend(self.memory_seed_loaders.get(for_env, [])) for loader in self._src.get_loaders(section, base, self._overrides, conf_set): - conf_set.loaders.append(loader) + conf_set.loaders.append(loader) # noqa: PERF402 if loaders is not None: conf_set.loaders.extend(loaders) return conf_set diff --git a/src/tox/config/source/discover.py b/src/tox/config/source/discover.py index 42bcba81a..7fcac23cd 100644 --- a/src/tox/config/source/discover.py +++ b/src/tox/config/source/discover.py @@ -64,7 +64,7 @@ def _load_exact_source(config_file: Path) -> Source: for src_type in (exact_match,) if exact_match is not None else SOURCE_TYPES: # pragma: no branch try: return src_type(config_file) - except ValueError: + except ValueError: # noqa: PERF203 pass msg = f"could not recognize config file {config_file}" raise HandledError(msg) diff --git a/src/tox/execute/local_sub_process/__init__.py b/src/tox/execute/local_sub_process/__init__.py index 2f9c7c8df..efd9c9c78 100644 --- a/src/tox/execute/local_sub_process/__init__.py +++ b/src/tox/execute/local_sub_process/__init__.py @@ -115,7 +115,7 @@ def write_stdin(self, content: str) -> None: result = ov.getresult(10) # wait up to 10ms to perform the operation if result != len(bytes_content): msg = f"failed to write to {stdin!r}" - raise RuntimeError(msg) # noqa: TRY301 + raise RuntimeError(msg) else: stdin.write(bytes_content) stdin.flush() diff --git a/src/tox/session/cmd/depends.py b/src/tox/session/cmd/depends.py index 5bb42c4b1..09f519752 100644 --- a/src/tox/session/cmd/depends.py +++ b/src/tox/session/cmd/depends.py @@ -38,7 +38,7 @@ def _handle(at: int, env: str) -> None: packager_list: list[str] = [] try: for pkg_env in run_env.package_envs: - packager_list.append(pkg_env.name) + packager_list.append(pkg_env.name) # noqa: PERF401 except Exception as exception: # noqa: BLE001 packager_list.append(f"... ({exception})") names = " | ".join(packager_list) diff --git a/src/tox/session/cmd/devenv.py b/src/tox/session/cmd/devenv.py index dbed3aeda..c4754e546 100644 --- a/src/tox/session/cmd/devenv.py +++ b/src/tox/session/cmd/devenv.py @@ -38,7 +38,7 @@ def devenv(state: State) -> int: usedevelop=True, # dev environments must be of type dev env_dir=opt.devenv_path, # move it in source ) - state.conf.memory_seed_loaders[list(opt.env)[0]].append(loader) + state.conf.memory_seed_loaders[next(iter(opt.env))].append(loader) state.envs.ensure_only_run_env_is_active() envs = list(state.envs.iter()) diff --git a/src/tox/session/cmd/run/common.py b/src/tox/session/cmd/run/common.py index d943fc860..25894ed58 100644 --- a/src/tox/session/cmd/run/common.py +++ b/src/tox/session/cmd/run/common.py @@ -135,6 +135,11 @@ def __call__( raise ArgumentError(self, str(exc)) from exc setattr(namespace, self.dest, result) + if os.environ.get("PYTHONHASHSEED", "random") != "random": + hashseed_default = int(os.environ["PYTHONHASHSEED"]) + else: + hashseed_default = random.randint(1, 1024 if sys.platform == "win32" else 4294967295) # noqa: S311 + parser.add_argument( "--hashseed", metavar="SEED", @@ -142,7 +147,7 @@ def __call__( "[1, 4294967295] ([1, 1024] on Windows). Passing 'noset' suppresses this behavior.", action=SeedAction, of_type=Optional[int], - default=random.randint(1, 1024 if sys.platform == "win32" else 4294967295), # noqa: S311 + default=hashseed_default, dest="hash_seed", ) parser.add_argument( @@ -263,10 +268,8 @@ def execute(state: State, max_workers: int | None, has_spinner: bool, live: bool thread._stop() # type: ignore[attr-defined] # pragma: no cover # noqa: SLF001 thread.join() finally: - ordered_results: list[ToxEnvRunResult] = [] name_to_run = {r.name: r for r in results} - for env in to_run_list: - ordered_results.append(name_to_run[env]) + ordered_results: list[ToxEnvRunResult] = [name_to_run[env] for env in to_run_list] # write the journal write_journal(getattr(state.conf.options, "result_json", None), state._journal) # noqa: SLF001 # report the outcome diff --git a/src/tox/tox_env/python/pip/req/file.py b/src/tox/tox_env/python/pip/req/file.py index e307729fb..5a6b17849 100644 --- a/src/tox/tox_env/python/pip/req/file.py +++ b/src/tox/tox_env/python/pip/req/file.py @@ -320,7 +320,7 @@ def _merge_option_line( # noqa: C901, PLR0912, PLR0915 base_opt.features_enabled = [] for feature in opt.features_enabled: if feature not in base_opt.features_enabled: - base_opt.features_enabled.append(feature) + base_opt.features_enabled.append(feature) # noqa: PERF401 base_opt.features_enabled.sort() if opt.index_url: if getattr(base_opt, "index_url", []): diff --git a/src/tox/tox_env/python/pip/req_file.py b/src/tox/tox_env/python/pip/req_file.py index 55d9d36e4..a4266970e 100644 --- a/src/tox/tox_env/python/pip/req_file.py +++ b/src/tox/tox_env/python/pip/req_file.py @@ -66,10 +66,8 @@ def _normalize_raw(raw: str) -> str: # a line ending in an unescaped \ is treated as a line continuation and the newline following it is effectively # ignored raw = "".join(raw.replace("\r", "").split("\\\n")) - lines: list[str] = [] - for line in raw.splitlines(): - # for tox<4 supporting requirement/constraint files via -rreq.txt/-creq.txt - lines.append(PythonDeps._normalize_line(line)) + # for tox<4 supporting requirement/constraint files via -rreq.txt/-creq.txt + lines: list[str] = [PythonDeps._normalize_line(line) for line in raw.splitlines()] adjusted = "\n".join(lines) return f"{adjusted}\n" if raw.endswith("\\\n") else adjusted # preserve trailing newline if input has it diff --git a/src/tox/tox_env/python/virtual_env/package/cmd_builder.py b/src/tox/tox_env/python/virtual_env/package/cmd_builder.py index 428ed5b37..ac0875a05 100644 --- a/src/tox/tox_env/python/virtual_env/package/cmd_builder.py +++ b/src/tox/tox_env/python/virtual_env/package/cmd_builder.py @@ -93,7 +93,7 @@ def perform_packaging(self, for_env: EnvConfigSet) -> list[Package]: msg = "stopping as failed to build package" raise Fail(msg) package_glob = self.conf["package_glob"] - found = glob.glob(package_glob) + found = glob.glob(package_glob) # noqa: PTH207 if not found: msg = f"no package found in {package_glob}" raise Fail(msg) diff --git a/src/tox/util/spinner.py b/src/tox/util/spinner.py index fb0cedde3..4fb6320a8 100644 --- a/src/tox/util/spinner.py +++ b/src/tox/util/spinner.py @@ -25,11 +25,11 @@ class _CursorInfo(ctypes.Structure): def _file_support_encoding(chars: Sequence[str], file: IO[str]) -> bool: encoding = getattr(file, "encoding", None) if encoding is not None: # pragma: no branch # this should be always set, unless someone passes in something bad - for char in chars: - try: + try: + for char in chars: char.encode(encoding) - except UnicodeEncodeError: - break + except UnicodeEncodeError: + pass else: return True return False diff --git a/tests/config/loader/ini/replace/test_replace_env_var.py b/tests/config/loader/ini/replace/test_replace_env_var.py index d54b6b599..dc0db3b54 100644 --- a/tests/config/loader/ini/replace/test_replace_env_var.py +++ b/tests/config/loader/ini/replace/test_replace_env_var.py @@ -34,8 +34,8 @@ def test_replace_env_set_triple_bs(replace_one: ReplaceOne, monkeypatch: MonkeyP def test_replace_env_set_quad_bs(replace_one: ReplaceOne, monkeypatch: MonkeyPatch) -> None: """Quad backslash should remain but not affect surrounding replacements.""" monkeypatch.setenv("MAGIC", "something good") - result = replace_one(r"\\{env:MAGIC}\\\\{env:MAGIC}" + "\\") # noqa: ISC003 - assert result == r"\\something good\\\\something good" + "\\" # noqa: ISC003 + result = replace_one(r"\\{env:MAGIC}\\\\{env:MAGIC}" + "\\") + assert result == r"\\something good\\\\something good" + "\\" def test_replace_env_when_value_is_backslash(replace_one: ReplaceOne, monkeypatch: MonkeyPatch) -> None: diff --git a/tests/config/loader/test_memory_loader.py b/tests/config/loader/test_memory_loader.py index 6f645540e..8ab7f8f62 100644 --- a/tests/config/loader/test_memory_loader.py +++ b/tests/config/loader/test_memory_loader.py @@ -66,7 +66,7 @@ def test_memory_loader(value: Any, of_type: type[Any], outcome: Any) -> None: (["m"], List[int], ValueError, "invalid literal for int"), ({"m": 1}, Dict[int, int], ValueError, "invalid literal for int"), ({1: "m"}, Dict[int, int], ValueError, "invalid literal for int"), - (object, Path, TypeError, "expected str, bytes or os.PathLike object"), + (object, Path, TypeError, r"str(, bytes)? or (an )?os\.PathLike object"), (1, Command, TypeError, "1"), (1, EnvList, TypeError, "1"), ], diff --git a/tests/demo_pkg_inline/build.py b/tests/demo_pkg_inline/build.py index eedf6d797..a56db5b80 100644 --- a/tests/demo_pkg_inline/build.py +++ b/tests/demo_pkg_inline/build.py @@ -42,11 +42,9 @@ def tox_register_tox_env(register: ToxEnvRegister) -> None: """, } metadata_files = { - entry_points: """ + entry_points: f""" [tox] - example = {}.example_plugin""".format( - name, - ), + example = {name}.example_plugin""", metadata: """ Metadata-Version: 2.1 Name: {} @@ -65,16 +63,12 @@ def tox_register_tox_env(register: ToxEnvRegister) -> None: version, "\n ".join(os.environ.get("METADATA_EXTRA", "").split("\n")), ), - wheel: """ + wheel: f""" Wheel-Version: 1.0 - Generator: {}-{} + Generator: {name}-{version} Root-Is-Purelib: true - Tag: py{}-none-any - """.format( - name, - version, - sys.version_info[0], - ), + Tag: py{sys.version_info[0]}-none-any + """, f"{dist_info}/top_level.txt": name, record: """ {0}/__init__.py,, diff --git a/tests/plugin/test_plugin.py b/tests/plugin/test_plugin.py index 227e85abb..6c03ff5ca 100644 --- a/tests/plugin/test_plugin.py +++ b/tests/plugin/test_plugin.py @@ -240,8 +240,8 @@ def _cannot_extend_config(config_set: ConfigSet) -> None: ): try: _conf(config_set) # type: ignore[no-untyped-call] # call to not typed function - raise NotImplementedError # noqa: TRY301 - except RuntimeError as exc: + raise NotImplementedError + except RuntimeError as exc: # noqa: PERF203 assert str(exc) == "config set has been marked final and cannot be extended" # noqa: PT017 @impl diff --git a/tests/tox_env/python/test_python_api.py b/tests/tox_env/python/test_python_api.py index a3a619276..159d9d772 100644 --- a/tests/tox_env/python/test_python_api.py +++ b/tests/tox_env/python/test_python_api.py @@ -2,6 +2,7 @@ import sys from typing import TYPE_CHECKING, Callable +from unittest.mock import patch import pytest @@ -220,6 +221,38 @@ def test_python_set_hash_seed_incorrect(tox_project: ToxProjectCreator) -> None: assert "tox run: error: argument --hashseed: invalid literal for int() with base 10: 'ok'" in result.err +def test_python_use_hash_seed_from_env(tox_project: ToxProjectCreator) -> None: + ini = "[testenv]\npackage=skip" + with patch.dict("os.environ", {"PYTHONHASHSEED": "13"}): + result = tox_project({"tox.ini": ini}).run("c", "-e", "py", "-k", "setenv") + result.assert_success() + assert "PYTHONHASHSEED=13" in result.out + + +def test_python_hash_seed_from_env_random(tox_project: ToxProjectCreator) -> None: + ini = "[testenv]\npackage=skip" + with patch.dict("os.environ", {"PYTHONHASHSEED": "random"}): + result = tox_project({"tox.ini": ini}).run("c", "-e", "py", "-k", "setenv") + result.assert_success() + assert "PYTHONHASHSEED=" in result.out + + +def test_python_hash_seed_from_env_and_override(tox_project: ToxProjectCreator) -> None: + ini = "[testenv]\npackage=skip\ncommands=python -c 'import os; print(os.environ.get(\"PYTHONHASHSEED\"))'" + with patch.dict("os.environ", {"PYTHONHASHSEED": "14"}): + result = tox_project({"tox.ini": ini}).run("r", "-e", "py", "--hashseed", "15") + result.assert_success() + assert result.out.splitlines()[1] == "15" + + +def test_python_hash_seed_from_env_and_disable(tox_project: ToxProjectCreator) -> None: + ini = "[testenv]\npackage=skip\ncommands=python -c 'import os; print(os.environ.get(\"PYTHONHASHSEED\"))'" + with patch.dict("os.environ", {"PYTHONHASHSEED": "16"}): + result = tox_project({"tox.ini": ini}).run("r", "-e", "py", "--hashseed", "notset") + result.assert_success() + assert result.out.splitlines()[1] == "None" + + @pytest.mark.parametrize("in_ci", [True, False]) def test_list_installed_deps(in_ci: bool, tox_project: ToxProjectCreator, mocker: MockerFixture) -> None: mocker.patch("tox.session.cmd.run.common.is_ci", return_value=in_ci) diff --git a/tox.ini b/tox.ini index 0e28277f2..9f6557e61 100644 --- a/tox.ini +++ b/tox.ini @@ -84,7 +84,7 @@ commands = description = do a release, required posarg of the version number skip_install = true deps = - gitpython>=3.1.31 + gitpython>=3.1.32 packaging>=23.1 towncrier>=23.6 commands =