diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e0a2bcb65..cacfe5517 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,12 @@ repos: additional_dependencies: - prettier@3.3.3 - "@prettier/plugin-xml@3.4.1" + - repo: https://github.com/LilSpazJoekp/docstrfmt + rev: v2.0.2 + hooks: + - id: docstrfmt + args: ["-l", "120"] + additional_dependencies: ["sphinx>=9.1"] - repo: meta hooks: - id: check-hooks-apply diff --git a/README.md b/README.md index 87f45e077..680a08bc7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# virtualenv +virtualenv.config.cli.parser.VirtualEnvOptions# virtualenv [![PyPI](https://img.shields.io/pypi/v/virtualenv?style=flat-square)](https://pypi.org/project/virtualenv) [![PyPI - Implementation](https://img.shields.io/pypi/implementation/virtualenv?style=flat-square)](https://pypi.org/project/virtualenv) diff --git a/docs/_static/rtd-search.js b/docs/_static/rtd-search.js new file mode 100644 index 000000000..133e1e31c --- /dev/null +++ b/docs/_static/rtd-search.js @@ -0,0 +1,11 @@ +document.addEventListener("DOMContentLoaded", () => { + const searchInput = document.querySelector( + ".sidebar-search input[type='search']", + ); + if (searchInput) { + searchInput.addEventListener("focus", () => { + document.dispatchEvent(new CustomEvent("readthedocs-search-show")); + searchInput.blur(); + }); + } +}); diff --git a/docs/_static/virtualenv.png b/docs/_static/virtualenv.png new file mode 100644 index 000000000..1f729837d Binary files /dev/null and b/docs/_static/virtualenv.png differ diff --git a/docs/_static/virtualenv.svg b/docs/_static/virtualenv.svg new file mode 100644 index 000000000..090396002 --- /dev/null +++ b/docs/_static/virtualenv.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + virtualenv + + + + + + + + diff --git a/docs/changelog.rst b/docs/changelog.rst index d2d3f109b..36dd7081c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,1077 +1,1314 @@ -Release History -=============== +################# + Release History +################# -.. include:: _draft.rst +.. towncrier-draft-entries:: [UNRELEASED DRAFT] .. towncrier release notes start -v20.36.1 (2026-01-09) ---------------------- +*********************** + v20.36.1 (2026-01-09) +*********************** Bugfixes - 20.36.1 -~~~~~~~~~~~~~~~~~~ -- Fix TOCTOU vulnerabilities in app_data and lock directory creation that could be exploited via symlink attacks - reported by :user:`tsigouris007`, fixed by :user:`gaborbernat`. (:issue:`3013`) +================== -v20.36.0 (2026-01-07) ---------------------- +- Fix TOCTOU vulnerabilities in app_data and lock directory creation that could be exploited via symlink attacks - + reported by :user:`tsigouris007`, fixed by :user:`gaborbernat`. (:issue:`3013`) + +*********************** + v20.36.0 (2026-01-07) +*********************** Features - 20.36.0 -~~~~~~~~~~~~~~~~~~ -- Add support for PEP 440 version specifiers in the ``--python`` flag. Users can now specify Python versions using operators like ``>=``, ``<=``, ``~=``, etc. For example: ``virtualenv --python=">=3.12" myenv`` `. (:issue:`2994`) +================== + +- Add support for PEP 440 version specifiers in the ``--python`` flag. Users can now specify Python versions using + operators like ``>=``, ``<=``, ``~=``, etc. For example: ``virtualenv --python=">=3.12" myenv`` `. (:issue:`2994`) -v20.35.4 (2025-10-28) ---------------------- +*********************** + v20.35.4 (2025-10-28) +*********************** Bugfixes - 20.35.4 -~~~~~~~~~~~~~~~~~~ -- Fix race condition in ``_virtualenv.py`` when file is overwritten during import, preventing ``NameError`` when ``_DISTUTILS_PATCH`` is accessed - by :user:`gracetyy`. (:issue:`2969`) +================== + +- Fix race condition in ``_virtualenv.py`` when file is overwritten during import, preventing ``NameError`` when + ``_DISTUTILS_PATCH`` is accessed - by :user:`gracetyy`. (:issue:`2969`) - Upgrade embedded wheels: - * pip to ``25.3`` from ``25.2`` (:issue:`2989`) + - pip to ``25.3`` from ``25.2`` (:issue:`2989`) -v20.35.3 (2025-10-10) ---------------------- +*********************** + v20.35.3 (2025-10-10) +*********************** Bugfixes - 20.35.3 -~~~~~~~~~~~~~~~~~~ +================== + - Accept RuntimeError in `test_too_many_open_files`, by :user:`esafak` (:issue:`2935`) -v20.35.2 (2025-10-10) ---------------------- +*********************** + v20.35.2 (2025-10-10) +*********************** Bugfixes - 20.35.2 -~~~~~~~~~~~~~~~~~~ +================== + - Revert out changes related to the extraction of the discovery module - by :user:`gaborbernat`. (:issue:`2978`) -v20.35.1 (2025-10-09) ---------------------- +*********************** + v20.35.1 (2025-10-09) +*********************** Bugfixes - 20.35.1 -~~~~~~~~~~~~~~~~~~ +================== + - Patch get_interpreter to handle missing cache and app_data - by :user:`esafak` (:issue:`2972`) - Fix backwards incompatible changes to ``PythonInfo`` - by :user:`gaborbernat`. (:issue:`2975`) -v20.35.0 (2025-10-08) ---------------------- +*********************** + v20.35.0 (2025-10-08) +*********************** Features - 20.35.0 -~~~~~~~~~~~~~~~~~~ +================== + - Add AppData and Cache protocols to discovery for decoupling - by :user:`esafak`. (:issue:`2074`) - Ensure python3.exe and python3 on Windows for Python 3 - by :user:`esafak`. (:issue:`2774`) Bugfixes - 20.35.0 -~~~~~~~~~~~~~~~~~~ +================== + - Replaced direct references to tcl/tk library paths with getattr - by :user:`esafak` (:issue:`2944`) - Restore absolute import of fs_is_case_sensitive - by :user:`esafak`. (:issue:`2955`) -v20.34.0 (2025-08-13) ---------------------- +*********************** + v20.34.0 (2025-08-13) +*********************** Features - 20.34.0 -~~~~~~~~~~~~~~~~~~ -- Abstract out caching in discovery - by :user:`esafak`. - Decouple `FileCache` from `py_info` (discovery) - by :user:`esafak`. - Remove references to py_info in FileCache - by :user:`esafak`. - Decouple discovery from creator plugins - by :user:`esafak`. - Decouple discovery by duplicating info utils - by :user:`esafak`. (:issue:`2074`) +================== + +- Abstract out caching in discovery - by :user:`esafak`. Decouple `FileCache` from `py_info` (discovery) - by + :user:`esafak`. Remove references to py_info in FileCache - by :user:`esafak`. Decouple discovery from creator plugins + - by :user:`esafak`. Decouple discovery by duplicating info utils - by :user:`esafak`. (:issue:`2074`) - Add PyPy 3.11 support. Contributed by :user:`esafak`. (:issue:`2932`) Bugfixes - 20.34.0 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheel pip to ``25.2`` from ``25.1.1`` - by :user:`gaborbernat`. (:issue:`2333`) - Accept RuntimeError in `test_too_many_open_files`, by :user:`esafak` (:issue:`2935`) - Python in PATH takes precedence over uv-managed python. Contributed by :user:`edgarrmondragon`. (:issue:`2952`) -v20.33.1 (2025-08-05) ---------------------- +*********************** + v20.33.1 (2025-08-05) +*********************** Bugfixes - 20.33.1 -~~~~~~~~~~~~~~~~~~ -- Correctly unpack _get_tcl_tk_libs() response in PythonInfo. - Contributed by :user:`esafak`. (:issue:`2930`) -- Restore `py_info.py` timestamp in `test_py_info_cache_invalidation_on_py_info_change` - Contributed by :user:`esafak`. (:issue:`2933`) +================== + +- Correctly unpack _get_tcl_tk_libs() response in PythonInfo. Contributed by :user:`esafak`. (:issue:`2930`) +- Restore `py_info.py` timestamp in `test_py_info_cache_invalidation_on_py_info_change` Contributed by :user:`esafak`. + (:issue:`2933`) -v20.33.0 (2025-08-03) ---------------------- +*********************** + v20.33.0 (2025-08-03) +*********************** Features - 20.33.0 -~~~~~~~~~~~~~~~~~~ -- Added support for Tcl and Tkinter. You're welcome. - Contributed by :user:`esafak`. (:issue:`425`) +================== + +- Added support for Tcl and Tkinter. You're welcome. Contributed by :user:`esafak`. (:issue:`425`) Bugfixes - 20.33.0 -~~~~~~~~~~~~~~~~~~ -- Prevent logging setup when --help is passed, fixing a flaky test. - Contributed by :user:`esafak`. (:issue:`u`) -- Fix cache invalidation for PythonInfo by hashing `py_info.py`. - Contributed by :user:`esafak`. (:issue:`2467`) -- When no discovery plugins are found, the application would crash with a StopIteration. - This change catches the StopIteration and raises a RuntimeError with a more informative message. - Contributed by :user:`esafak`. (:issue:`2493`) -- Stop `--try-first-with` overriding absolute `--python` paths. - Contributed by :user:`esafak`. (:issue:`2659`) -- Force UTF-8 encoding for pip download - Contributed by :user:`esafak`. (:issue:`2780`) -- Creating a virtual environment on a filesystem without symlink-support would fail even with `--copies` - Make `fs_supports_symlink` perform a real symlink creation check on all platforms. - Contributed by :user:`esafak`. (:issue:`2786`) +================== + +- Prevent logging setup when --help is passed, fixing a flaky test. Contributed by :user:`esafak`. (:issue:`u`) +- Fix cache invalidation for PythonInfo by hashing `py_info.py`. Contributed by :user:`esafak`. (:issue:`2467`) +- When no discovery plugins are found, the application would crash with a StopIteration. This change catches the + StopIteration and raises a RuntimeError with a more informative message. Contributed by :user:`esafak`. + (:issue:`2493`) +- Stop `--try-first-with` overriding absolute `--python` paths. Contributed by :user:`esafak`. (:issue:`2659`) +- Force UTF-8 encoding for pip download Contributed by :user:`esafak`. (:issue:`2780`) +- Creating a virtual environment on a filesystem without symlink-support would fail even with `--copies` Make + `fs_supports_symlink` perform a real symlink creation check on all platforms. Contributed by :user:`esafak`. + (:issue:`2786`) - Add a note to the user guide recommending the use of a specific Python version when creating virtual environments. Contributed by :user:`esafak`. (:issue:`2808`) -- Fix 'Too many open files' error due to a file descriptor leak in virtualenv's locking mechanism. - Contributed by :user:`esafak`. (:issue:`2834`) -- Support renamed Windows venv redirector (`venvlauncher.exe` and `venvwlauncher.exe`) on Python 3.13 - Contributed by :user:`esafak`. (:issue:`2851`) +- Fix 'Too many open files' error due to a file descriptor leak in virtualenv's locking mechanism. Contributed by + :user:`esafak`. (:issue:`2834`) +- Support renamed Windows venv redirector (`venvlauncher.exe` and `venvwlauncher.exe`) on Python 3.13 Contributed by + :user:`esafak`. (:issue:`2851`) - Resolve Nushell activation script deprecation warnings by dynamically selecting the ``--optional`` flag for Nushell - ``get`` command on version 0.106.0 and newer, while retaining the deprecated ``-i`` flag for older versions to maintain - compatibility. Contributed by :user:`gaborbernat`. (:issue:`2910`) + ``get`` command on version 0.106.0 and newer, while retaining the deprecated ``-i`` flag for older versions to + maintain compatibility. Contributed by :user:`gaborbernat`. (:issue:`2910`) -v20.32.0 (2025-07-20) ---------------------- +*********************** + v20.32.0 (2025-07-20) +*********************** Features - 20.32.0 -~~~~~~~~~~~~~~~~~~ +================== + - Warn on incorrect invocation of Nushell activation script - by :user:`esafak`. (:issue:`nushell_activation`) - Discover uv-managed Python installations (:issue:`2901`) Bugfixes - 20.32.0 -~~~~~~~~~~~~~~~~~~ +================== + - Ignore missing absolute paths for python discovery - by :user:`esafak` (:issue:`2870`) - Upgrade embedded setuptools to ``80.9.0`` from ``80.3.1`` - by :user:`gaborbernat`. (:issue:`2900`) -v20.31.2 (2025-05-08) ---------------------- +*********************** + v20.31.2 (2025-05-08) +*********************** No significant changes. - -v20.31.1 (2025-05-05) ---------------------- +*********************** + v20.31.1 (2025-05-05) +*********************** Bugfixes - 20.31.1 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: - * pip to ``25.1.1`` from ``25.1`` - * setuptools to ``80.3.1`` from ``78.1.0`` (:issue:`2880`) + - pip to ``25.1.1`` from ``25.1`` + - setuptools to ``80.3.1`` from ``78.1.0`` (:issue:`2880`) -v20.31.0 (2025-05-05) ---------------------- +*********************** + v20.31.0 (2025-05-05) +*********************** Features - 20.31.0 -~~~~~~~~~~~~~~~~~~ -- No longer bundle ``wheel`` wheels (except on Python 3.8), ``setuptools`` includes native ``bdist_wheel`` support. Update ``pip`` to ``25.1``. (:issue:`2868`) +================== + +- No longer bundle ``wheel`` wheels (except on Python 3.8), ``setuptools`` includes native ``bdist_wheel`` support. + Update ``pip`` to ``25.1``. (:issue:`2868`) Bugfixes - 20.31.0 -~~~~~~~~~~~~~~~~~~ -- ``get_embed_wheel()`` no longer fails with a :exc:`TypeError` when it is - called with an unknown *distribution*. (:issue:`2877`) +================== + +- ``get_embed_wheel()`` no longer fails with a :exc:`TypeError` when it is called with an unknown *distribution*. + (:issue:`2877`) - Fix ``HelpFormatter`` error with Python 3.14.0b1. (:issue:`2878`) -v20.30.0 (2025-03-31) ---------------------- +*********************** + v20.30.0 (2025-03-31) +*********************** Features - 20.30.0 -~~~~~~~~~~~~~~~~~~ +================== + - Add support for `GraalPy `_. (:issue:`2832`) Bugfixes - 20.30.0 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: - * setuptools to ``78.1.0`` from ``75.3.2`` (:issue:`2863`) + - setuptools to ``78.1.0`` from ``75.3.2`` (:issue:`2863`) -v20.29.3 (2025-03-06) ---------------------- +*********************** + v20.29.3 (2025-03-06) +*********************** Bugfixes - 20.29.3 -~~~~~~~~~~~~~~~~~~ +================== + - Ignore unreadable directories in ``PATH``. (:issue:`2794`) -v20.29.2 (2025-02-10) ---------------------- +*********************** + v20.29.2 (2025-02-10) +*********************** Bugfixes - 20.29.2 -~~~~~~~~~~~~~~~~~~ +================== + - Remove old virtualenv wheel from the source distribution - by :user:`gaborbernat`. (:issue:`2841`) - Upgrade embedded wheel pip to ``25.0.1`` from ``24.3.1`` - by :user:`gaborbernat`. (:issue:`2843`) -v20.29.1 (2025-01-17) ---------------------- +*********************** + v20.29.1 (2025-01-17) +*********************** Bugfixes - 20.29.1 -~~~~~~~~~~~~~~~~~~ +================== + - Fix PyInfo cache incompatibility warnings - by :user:`robsdedude`. (:issue:`2827`) -v20.29.0 (2025-01-15) ---------------------- +*********************** + v20.29.0 (2025-01-15) +*********************** Features - 20.29.0 -~~~~~~~~~~~~~~~~~~ +================== + - Add support for selecting free-threaded Python interpreters, e.g., `python3.13t`. (:issue:`2809`) Bugfixes - 20.29.0 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: - * setuptools to ``75.8.0`` from ``75.6.0`` (:issue:`2823`) + - setuptools to ``75.8.0`` from ``75.6.0`` (:issue:`2823`) -v20.28.1 (2025-01-02) ---------------------- +*********************** + v20.28.1 (2025-01-02) +*********************** Bugfixes - 20.28.1 -~~~~~~~~~~~~~~~~~~ +================== + - Skip tcsh tests on broken tcsh versions - by :user:`gaborbernat`. (:issue:`2814`) -v20.28.0 (2024-11-25) ---------------------- +*********************** + v20.28.0 (2024-11-25) +*********************** Features - 20.28.0 -~~~~~~~~~~~~~~~~~~ +================== + - Write CACHEDIR.TAG file on creation - by "user:`neilramsay`. (:issue:`2803`) -v20.27.2 (2024-11-25) ---------------------- +*********************** + v20.27.2 (2024-11-25) +*********************** Bugfixes - 20.27.2 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: - * setuptools to ``75.3.0`` from ``75.2.0`` (:issue:`2798`) + - setuptools to ``75.3.0`` from ``75.2.0`` (:issue:`2798`) + - Upgrade embedded wheels: - * wheel to ``0.45.0`` from ``0.44.0`` - * setuptools to ``75.5.0`` (:issue:`2800`) + - wheel to ``0.45.0`` from ``0.44.0`` + - setuptools to ``75.5.0`` (:issue:`2800`) + - no longer forcibly echo off during windows batch activation (:issue:`2801`) - Upgrade embedded wheels: - * setuptools to ``75.6.0`` from ``75.5.0`` - * wheel to ``0.45.1`` from ``0.45.0`` (:issue:`2804`) + - setuptools to ``75.6.0`` from ``75.5.0`` + - wheel to ``0.45.1`` from ``0.45.0`` (:issue:`2804`) -v20.27.1 (2024-10-28) ---------------------- +*********************** + v20.27.1 (2024-10-28) +*********************** Bugfixes - 20.27.1 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: - * pip to ``24.3.1`` from ``24.2`` (:issue:`2789`) + - pip to ``24.3.1`` from ``24.2`` (:issue:`2789`) -v20.27.0 (2024-10-17) ---------------------- +*********************** + v20.27.0 (2024-10-17) +*********************** Features - 20.27.0 -~~~~~~~~~~~~~~~~~~ +================== + - Drop 3.7 support as the CI environments no longer allow it running - by :user:`gaborbernat`. (:issue:`2758`) Bugfixes - 20.27.0 -~~~~~~~~~~~~~~~~~~ -- When a ``$PATH`` entry cannot be checked for existence, skip it instead of terminating - by :user:`hroncok`. (:issue:`2782`) -- Upgrade embedded wheels: +================== - * setuptools to ``75.2.0`` from ``75.1.0`` - * Removed pip of ``24.0`` - * Removed setuptools of ``68.0.0`` - * Removed wheel of ``0.42.0`` +- When a ``$PATH`` entry cannot be checked for existence, skip it instead of terminating - by :user:`hroncok`. + (:issue:`2782`) +- Upgrade embedded wheels: + - setuptools to ``75.2.0`` from ``75.1.0`` + - Removed pip of ``24.0`` + - Removed setuptools of ``68.0.0`` + - Removed wheel of ``0.42.0`` - by :user:`gaborbernat`. (:issue:`2783`) + - Fix zipapp is broken on Windows post distlib ``0.3.9`` - by :user:`gaborbernat`. (:issue:`2784`) -v20.26.6 (2024-09-27) ---------------------- +*********************** + v20.26.6 (2024-09-27) +*********************** Bugfixes - 20.26.6 -~~~~~~~~~~~~~~~~~~ -- Properly quote string placeholders in activation script templates to mitigate - potential command injection - by :user:`y5c4l3`. (:issue:`2768`) +================== + +- Properly quote string placeholders in activation script templates to mitigate potential command injection - by + :user:`y5c4l3`. (:issue:`2768`) -v20.26.5 (2024-09-17) ---------------------- +*********************** + v20.26.5 (2024-09-17) +*********************** Bugfixes - 20.26.5 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: setuptools to ``75.1.0`` from ``74.1.2`` - by :user:`gaborbernat`. (:issue:`2765`) -v20.26.4 (2024-09-07) ---------------------- +*********************** + v20.26.4 (2024-09-07) +*********************** Bugfixes - 20.26.4 -~~~~~~~~~~~~~~~~~~ +================== + - no longer create `()` output in console during activation of a virtualenv by .bat file. (:issue:`2728`) - Upgrade embedded wheels: - * wheel to ``0.44.0`` from ``0.43.0`` - * pip to ``24.2`` from ``24.1`` - * setuptools to ``74.1.2`` from ``70.1.0`` (:issue:`2760`) + - wheel to ``0.44.0`` from ``0.43.0`` + - pip to ``24.2`` from ``24.1`` + - setuptools to ``74.1.2`` from ``70.1.0`` (:issue:`2760`) -v20.26.3 (2024-06-21) ---------------------- +*********************** + v20.26.3 (2024-06-21) +*********************** Bugfixes - 20.26.3 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: - * setuptools to ``70.1.0`` from ``69.5.1`` - * pip to ``24.1`` from ``24.0`` (:issue:`2741`) + - setuptools to ``70.1.0`` from ``69.5.1`` + - pip to ``24.1`` from ``24.0`` (:issue:`2741`) -v20.26.2 (2024-05-13) ---------------------- +*********************** + v20.26.2 (2024-05-13) +*********************** Bugfixes - 20.26.2 -~~~~~~~~~~~~~~~~~~ -- ``virtualenv.pyz`` no longer fails when zipapp path contains a symlink - by :user:`HandSonic` and :user:`petamas`. (:issue:`1949`) +================== + +- ``virtualenv.pyz`` no longer fails when zipapp path contains a symlink - by :user:`HandSonic` and :user:`petamas`. + (:issue:`1949`) - Fix bad return code from activate.sh if hashing is disabled - by :user:'fenkes-ibm'. (:issue:`2717`) -v20.26.1 (2024-04-29) ---------------------- +*********************** + v20.26.1 (2024-04-29) +*********************** Bugfixes - 20.26.1 -~~~~~~~~~~~~~~~~~~ +================== + - fix PATH-based Python discovery on Windows - by :user:`ofek`. (:issue:`2712`) -v20.26.0 (2024-04-23) ---------------------- +*********************** + v20.26.0 (2024-04-23) +*********************** Bugfixes - 20.26.0 -~~~~~~~~~~~~~~~~~~ -- allow builtin discovery to discover specific interpreters (e.g. ``python3.12``) given an unspecific spec (e.g. ``python3``) - by :user:`flying-sheep`. (:issue:`2709`) +================== -v20.25.3 (2024-04-17) ---------------------- +- allow builtin discovery to discover specific interpreters (e.g. ``python3.12``) given an unspecific spec (e.g. + ``python3``) - by :user:`flying-sheep`. (:issue:`2709`) + +*********************** + v20.25.3 (2024-04-17) +*********************** Bugfixes - 20.25.3 -~~~~~~~~~~~~~~~~~~ +================== + - Python 3.13.0a6 renamed pathmod to parser. (:issue:`2702`) -v20.25.2 (2024-04-16) ---------------------- +*********************** + v20.25.2 (2024-04-16) +*********************** Bugfixes - 20.25.2 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: - setuptools of ``69.1.0`` to ``69.5.1`` - wheel of ``0.42.0`` to ``0.43.0`` (:issue:`2699`) -v20.25.1 (2024-02-21) ---------------------- +*********************** + v20.25.1 (2024-02-21) +*********************** Bugfixes - 20.25.1 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: - * setuptools to ``69.0.3`` from ``69.0.2`` - * pip to ``23.3.2`` from ``23.3.1`` (:issue:`2681`) + - setuptools to ``69.0.3`` from ``69.0.2`` + - pip to ``23.3.2`` from ``23.3.1`` (:issue:`2681`) + - Upgrade embedded wheels: - pip ``23.3.2`` to ``24.0``, - setuptools ``69.0.3`` to ``69.1.0``. (:issue:`2691`) Misc - 20.25.1 -~~~~~~~~~~~~~~ +============== + - :issue:`2688` -v20.25.0 (2023-12-01) ---------------------- +*********************** + v20.25.0 (2023-12-01) +*********************** Features - 20.25.0 -~~~~~~~~~~~~~~~~~~ +================== + - The tests now pass on the CI with Python 3.13.0a2 - by :user:`hroncok`. (:issue:`2673`) Bugfixes - 20.25.0 -~~~~~~~~~~~~~~~~~~ +================== + - Upgrade embedded wheels: - * wheel to ``0.41.3`` from ``0.41.2`` (:issue:`2665`) + - wheel to ``0.41.3`` from ``0.41.2`` (:issue:`2665`) + - Upgrade embedded wheels: - * wheel to ``0.42.0`` from ``0.41.3`` - * setuptools to ``69.0.2`` from ``68.2.2`` (:issue:`2669`) + - wheel to ``0.42.0`` from ``0.41.3`` + - setuptools to ``69.0.2`` from ``68.2.2`` (:issue:`2669`) -v20.24.6 (2023-10-23) ---------------------- +*********************** + v20.24.6 (2023-10-23) +*********************** Bugfixes - 20.24.6 -~~~~~~~~~~~~~~~~~~ +================== + - Use get_hookimpls method instead of the private attribute in tests. (:issue:`2649`) - Upgrade embedded wheels: - * setuptools to ``68.2.2`` from ``68.2.0`` - * pip to ``23.3.1`` from ``23.2.1`` (:issue:`2656`) - + - setuptools to ``68.2.2`` from ``68.2.0`` + - pip to ``23.3.1`` from ``23.2.1`` (:issue:`2656`) -v20.24.5 (2023-09-08) ---------------------- +*********************** + v20.24.5 (2023-09-08) +*********************** Bugfixes - 20.24.5 -~~~~~~~~~~~~~~~~~~ +================== + - Declare PyPy 3.10 support - by :user:`cclauss`. (:issue:`2638`) - Brew on macOS no longer allows copy builds - disallow choosing this by :user:`gaborbernat`. (:issue:`2640`) - Upgrade embedded wheels: - * setuptools to ``68.2.0`` from ``68.1.2`` (:issue:`2642`) - + - setuptools to ``68.2.0`` from ``68.1.2`` (:issue:`2642`) -v20.24.4 (2023-08-30) ---------------------- +*********************** + v20.24.4 (2023-08-30) +*********************** Bugfixes - 20.24.4 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded wheels: +================== - * setuptools to ``68.1.2`` from ``68.1.0`` on ``3.8+`` - * wheel to ``0.41.2`` from ``0.41.1`` on ``3.7+`` (:issue:`2628`) +- Upgrade embedded wheels: + - setuptools to ``68.1.2`` from ``68.1.0`` on ``3.8+`` + - wheel to ``0.41.2`` from ``0.41.1`` on ``3.7+`` (:issue:`2628`) -v20.24.3 (2023-08-11) ---------------------- +*********************** + v20.24.3 (2023-08-11) +*********************** Bugfixes - 20.24.3 -~~~~~~~~~~~~~~~~~~ +================== + - Fixed ResourceWarning on exit caused by periodic update subprocess (:issue:`2472`) - Upgrade embedded wheels: - * wheel to ``0.41.1`` from ``0.41.0`` (:issue:`2622`) + - wheel to ``0.41.1`` from ``0.41.0`` (:issue:`2622`) Misc - 20.24.3 -~~~~~~~~~~~~~~ -- :issue:`2610` +============== +- :issue:`2610` -v20.24.2 (2023-07-24) ---------------------- +*********************** + v20.24.2 (2023-07-24) +*********************** Bugfixes - 20.24.2 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded wheels: +================== - * pip to ``23.2.1`` from ``23.2`` - * wheel to ``0.41.0`` from ``0.40.0`` (:issue:`2614`) +- Upgrade embedded wheels: + - pip to ``23.2.1`` from ``23.2`` + - wheel to ``0.41.0`` from ``0.40.0`` (:issue:`2614`) -v20.24.1 (2023-07-19) ---------------------- +*********************** + v20.24.1 (2023-07-19) +*********************** Bugfixes - 20.24.1 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded wheels: +================== - * pip to ``23.2`` from ``23.1.2`` - by :user:`arielkirkwood` (:issue:`2611`) +- Upgrade embedded wheels: + - pip to ``23.2`` from ``23.1.2`` - by :user:`arielkirkwood` (:issue:`2611`) -v20.24.0 (2023-07-14) ---------------------- +*********************** + v20.24.0 (2023-07-14) +*********************** Features - 20.24.0 -~~~~~~~~~~~~~~~~~~ -- Export the prompt prefix as ``VIRTUAL_ENV_PROMPT`` when activating a virtual - environment - by :user:`jimporter`. (:issue:`2194`) +================== + +- Export the prompt prefix as ``VIRTUAL_ENV_PROMPT`` when activating a virtual environment - by :user:`jimporter`. + (:issue:`2194`) Bugfixes - 20.24.0 -~~~~~~~~~~~~~~~~~~ +================== + - Fix test suite - by :user:`gaborbernat`. (:issue:`2592`) - Upgrade embedded wheels: - * setuptools to ``68.0.0`` from ``67.8.0`` (:issue:`2607`) - + - setuptools to ``68.0.0`` from ``67.8.0`` (:issue:`2607`) -v20.23.1 (2023-06-16) ---------------------- +*********************** + v20.23.1 (2023-06-16) +*********************** Bugfixes - 20.23.1 -~~~~~~~~~~~~~~~~~~ -- update and simplify nushell activation script, fixes an issue on Windows resulting in consecutive command not found - by :user:`melMass`. (:issue:`2572`) -- Upgrade embedded wheels: +================== - * setuptools to ``67.8.0`` from ``67.7.2`` (:issue:`2588`) +- update and simplify nushell activation script, fixes an issue on Windows resulting in consecutive command not found - + by :user:`melMass`. (:issue:`2572`) +- Upgrade embedded wheels: + - setuptools to ``67.8.0`` from ``67.7.2`` (:issue:`2588`) -v20.23.0 (2023-04-27) ---------------------- +*********************** + v20.23.0 (2023-04-27) +*********************** Features - 20.23.0 -~~~~~~~~~~~~~~~~~~ +================== + - Do not install ``wheel`` and ``setuptools`` seed packages for Python 3.12+. To restore the old behavior use: - for ``wheel`` use ``VIRTUALENV_WHEEL=bundle`` environment variable or ``--wheel=bundle`` CLI flag, - for ``setuptools`` use ``VIRTUALENV_SETUPTOOLS=bundle`` environment variable or ``--setuptools=bundle`` CLI flag. By :user:`chrysle`. (:issue:`2487`) + - 3.12 support - by :user:`gaborbernat`. (:issue:`2558`) Bugfixes - 20.23.0 -~~~~~~~~~~~~~~~~~~ -- Prevent ``PermissionError`` when using venv creator on systems that deliver files without user write - permission - by :user:`kulikjak`. (:issue:`2543`) -- Upgrade setuptools to ``67.7.2`` from ``67.6.1`` and pip to ``23.1.2`` from ``23.1`` - by :user:`szleb`. (:issue:`2560`) +================== +- Prevent ``PermissionError`` when using venv creator on systems that deliver files without user write permission - by + :user:`kulikjak`. (:issue:`2543`) +- Upgrade setuptools to ``67.7.2`` from ``67.6.1`` and pip to ``23.1.2`` from ``23.1`` - by :user:`szleb`. + (:issue:`2560`) -v20.22.0 (2023-04-19) ---------------------- +*********************** + v20.22.0 (2023-04-19) +*********************** Features - 20.22.0 -~~~~~~~~~~~~~~~~~~ +================== + - Drop support for creating Python <=3.6 (including 2) interpreters. Removed pip of ``20.3.4``, ``21.3.1``; wheel of ``0.37.1``; setuptools of ``59.6.0``, ``44.1.1``, ``50.3.2``- by :user:`gaborbernat`. (:issue:`2548`) - -v20.21.1 (2023-04-19) ---------------------- +*********************** + v20.21.1 (2023-04-19) +*********************** Bugfixes - 20.21.1 -~~~~~~~~~~~~~~~~~~ +================== + - Add ``tox.ini`` to sdist - by :user:`mtelka`. (:issue:`2511`) - Move the use of 'let' in nushell to ensure compatibility with future releases of nushell, where 'let' no longer assumes that its initializer is a full expressions. (:issue:`2527`) -- The nushell command 'str collect' has been superseded by the 'str join' command. The activate.nu script has - been updated to reflect this change. (:issue:`2532`) +- The nushell command 'str collect' has been superseded by the 'str join' command. The activate.nu script has been + updated to reflect this change. (:issue:`2532`) - Upgrade embedded wheels: - * wheel to ``0.40.0`` from ``0.38.4`` - * setuptools to ``67.6.1`` from ``67.4.0`` - * pip to ``23.1`` from ``23.0.1`` (:issue:`2546`) + - wheel to ``0.40.0`` from ``0.38.4`` + - setuptools to ``67.6.1`` from ``67.4.0`` + - pip to ``23.1`` from ``23.0.1`` (:issue:`2546`) - -v20.21.0 (2023-03-12) ---------------------- +*********************** + v20.21.0 (2023-03-12) +*********************** Features - 20.21.0 -~~~~~~~~~~~~~~~~~~ +================== + - Make closure syntax explicitly starts with {||. (:issue:`2512`) Bugfixes - 20.21.0 -~~~~~~~~~~~~~~~~~~ -- Add ``print`` command to nushell print_prompt to ensure compatibility with future release of nushell, - where intermediate commands no longer print their result to stdout. (:issue:`2514`) -- Do not assume the default encoding. (:issue:`2515`) -- Make ``ReentrantFileLock`` thread-safe and, - thereby, fix race condition in ``virtualenv.cli_run`` - by :user:`radoering`. (:issue:`2516`) +================== +- Add ``print`` command to nushell print_prompt to ensure compatibility with future release of nushell, where + intermediate commands no longer print their result to stdout. (:issue:`2514`) +- Do not assume the default encoding. (:issue:`2515`) +- Make ``ReentrantFileLock`` thread-safe and, thereby, fix race condition in ``virtualenv.cli_run`` - by + :user:`radoering`. (:issue:`2516`) -v20.20.0 (2023-02-28) ---------------------- +*********************** + v20.20.0 (2023-02-28) +*********************** Features - 20.20.0 -~~~~~~~~~~~~~~~~~~ -- Change environment variable existence check in Nushell activation script to not use deprecated command. (:issue:`2506`) +================== + +- Change environment variable existence check in Nushell activation script to not use deprecated command. + (:issue:`2506`) Bugfixes - 20.20.0 -~~~~~~~~~~~~~~~~~~ -- Discover CPython implementations distributed on Windows by any organization - by :user:`faph`. (:issue:`2504`) -- Upgrade embedded setuptools to ``67.4.0`` from ``67.1.0`` and pip to ``23.0.1`` from ``23.0`` - by :user:`gaborbernat`. (:issue:`2510`) +================== +- Discover CPython implementations distributed on Windows by any organization - by :user:`faph`. (:issue:`2504`) +- Upgrade embedded setuptools to ``67.4.0`` from ``67.1.0`` and pip to ``23.0.1`` from ``23.0`` - by + :user:`gaborbernat`. (:issue:`2510`) -v20.19.0 (2023-02-07) ---------------------- +*********************** + v20.19.0 (2023-02-07) +*********************** Features - 20.19.0 -~~~~~~~~~~~~~~~~~~ -- Allow platformdirs version ``3`` - by :user:`cdce8p`. (:issue:`2499`) +================== +- Allow platformdirs version ``3`` - by :user:`cdce8p`. (:issue:`2499`) -v20.18.0 (2023-02-06) ---------------------- +*********************** + v20.18.0 (2023-02-06) +*********************** Features - 20.18.0 -~~~~~~~~~~~~~~~~~~ +================== + - Drop ``3.6`` runtime support (can still create ``2.7+``) - by :user:`gaborbernat`. (:issue:`2489`) Bugfixes - 20.18.0 -~~~~~~~~~~~~~~~~~~ +================== + - Fix broken prompt in Nushell when activating virtual environment - by :user:`kubouc`. (:issue:`2481`) - Bump embedded pip to ``23.0`` and setuptools to ``67.1`` - by :user:`gaborbernat`. (:issue:`2489`) - -v20.17.1 (2022-12-05) ---------------------- +*********************** + v20.17.1 (2022-12-05) +*********************** Bugfixes - 20.17.1 -~~~~~~~~~~~~~~~~~~ -- A ``py`` or ``python`` spec means any Python rather than ``CPython`` - by :user:`gaborbernat`. (`#2460 `_) -- Make ``activate.nu`` respect ``VIRTUAL_ENV_DISABLE_PROMPT`` and not set the prompt if requested - by :user:`m-lima`. (`#2461 `_) +================== +- A ``py`` or ``python`` spec means any Python rather than ``CPython`` - by :user:`gaborbernat`. (`#2460 + `_) +- Make ``activate.nu`` respect ``VIRTUAL_ENV_DISABLE_PROMPT`` and not set the prompt if requested - by :user:`m-lima`. + (`#2461 `_) -v20.17.0 (2022-11-27) ---------------------- +*********************** + v20.17.0 (2022-11-27) +*********************** Features - 20.17.0 -~~~~~~~~~~~~~~~~~~ -- Change Nushell activation script to be a module meant to be activated as an overlay. (`#2422 `_) -- Update operator used in Nushell activation script to be compatible with future versions. (`#2450 `_) +================== + +- Change Nushell activation script to be a module meant to be activated as an overlay. (`#2422 + `_) +- Update operator used in Nushell activation script to be compatible with future versions. (`#2450 + `_) Bugfixes - 20.17.0 -~~~~~~~~~~~~~~~~~~ -- Do not use deprecated API from ``importlib.resources`` on Python 3.10 or later - by :user:`gaborbernat`. (`#2448 `_) -- Upgrade embedded setuptools to ``65.6.3`` from ``65.5.1`` - by :user:`gaborbernat`. (`#2451 `_) +================== +- Do not use deprecated API from ``importlib.resources`` on Python 3.10 or later - by :user:`gaborbernat`. (`#2448 + `_) +- Upgrade embedded setuptools to ``65.6.3`` from ``65.5.1`` - by :user:`gaborbernat`. (`#2451 + `_) -v20.16.7 (2022-11-12) ---------------------- +*********************** + v20.16.7 (2022-11-12) +*********************** Bugfixes - 20.16.7 -~~~~~~~~~~~~~~~~~~ -- Use parent directory of python executable for pyvenv.cfg "home" value per PEP 405 - by :user:`vfazio`. (`#2440 `_) -- In POSIX virtual environments, try alternate binary names if ``sys._base_executable`` does not exist - by :user:`vfazio`. (`#2442 `_) -- Upgrade embedded wheel to ``0.38.4`` and pip to ``22.3.1`` from ``22.3`` and setuptools to ``65.5.1`` from - ``65.5.0`` - by :user:`gaborbernat`. (`#2443 `_) +================== +- Use parent directory of python executable for pyvenv.cfg "home" value per PEP 405 - by :user:`vfazio`. (`#2440 + `_) +- In POSIX virtual environments, try alternate binary names if ``sys._base_executable`` does not exist - by + :user:`vfazio`. (`#2442 `_) +- Upgrade embedded wheel to ``0.38.4`` and pip to ``22.3.1`` from ``22.3`` and setuptools to ``65.5.1`` from ``65.5.0`` + - by :user:`gaborbernat`. (`#2443 `_) -v20.16.6 (2022-10-25) ---------------------- +*********************** + v20.16.6 (2022-10-25) +*********************** Features - 20.16.6 -~~~~~~~~~~~~~~~~~~ +================== + - Drop unneeded shims for PyPy3 directory structure (`#2426 `_) Bugfixes - 20.16.6 -~~~~~~~~~~~~~~~~~~ -- Fix selected scheme on debian derivatives for python 3.10 when ``python3-distutils`` is not installed or the ``venv`` scheme is not available - by :user:`asottile`. (`#2350 `_) -- Allow the test suite to pass even with the original C shell (rather than ``tcsh``) - by :user:`kulikjak`. (`#2418 `_) -- Fix fallback handling of downloading wheels for bundled packages - by :user:`schaap`. (`#2429 `_) -- Upgrade embedded setuptools to ``65.5.0`` from ``65.3.0`` and pip to ``22.3`` from ``22.2.2`` - by :user:`gaborbernat`. (`#2434 `_) +================== +- Fix selected scheme on debian derivatives for python 3.10 when ``python3-distutils`` is not installed or the ``venv`` + scheme is not available - by :user:`asottile`. (`#2350 `_) +- Allow the test suite to pass even with the original C shell (rather than ``tcsh``) - by :user:`kulikjak`. (`#2418 + `_) +- Fix fallback handling of downloading wheels for bundled packages - by :user:`schaap`. (`#2429 + `_) +- Upgrade embedded setuptools to ``65.5.0`` from ``65.3.0`` and pip to ``22.3`` from ``22.2.2`` - by + :user:`gaborbernat`. (`#2434 `_) -v20.16.5 (2022-09-07) ---------------------- +*********************** + v20.16.5 (2022-09-07) +*********************** Bugfixes - 20.16.5 -~~~~~~~~~~~~~~~~~~ -- Do not turn echo off for subsequent commands in batch activators - (``activate.bat`` and ``deactivate.bat``) - by :user:`pawelszramowski`. (`#2411 `_) +================== +- Do not turn echo off for subsequent commands in batch activators (``activate.bat`` and ``deactivate.bat``) - by + :user:`pawelszramowski`. (`#2411 `_) -v20.16.4 (2022-08-29) ---------------------- +*********************** + v20.16.4 (2022-08-29) +*********************** Bugfixes - 20.16.4 -~~~~~~~~~~~~~~~~~~ -- Bump embed setuptools to ``65.3`` - by :user:`gaborbernat`. (`#2405 `_) +================== +- Bump embed setuptools to ``65.3`` - by :user:`gaborbernat`. (`#2405 + `_) -v20.16.3 (2022-08-04) ---------------------- +*********************** + v20.16.3 (2022-08-04) +*********************** Bugfixes - 20.16.3 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded pip to ``22.2.2`` from ``22.2.1`` and setuptools to ``63.4.1`` from ``63.2.0`` - by :user:`gaborbernat`. (`#2395 `_) +================== +- Upgrade embedded pip to ``22.2.2`` from ``22.2.1`` and setuptools to ``63.4.1`` from ``63.2.0`` - by + :user:`gaborbernat`. (`#2395 `_) -v20.16.2 (2022-07-27) ---------------------- +*********************** + v20.16.2 (2022-07-27) +*********************** Bugfixes - 20.16.2 -~~~~~~~~~~~~~~~~~~ -- Bump embedded pip from ``22.2`` to ``22.2.1`` - by :user:`gaborbernat`. (`#2391 `_) +================== +- Bump embedded pip from ``22.2`` to ``22.2.1`` - by :user:`gaborbernat`. (`#2391 + `_) -v20.16.1 (2022-07-26) ---------------------- +*********************** + v20.16.1 (2022-07-26) +*********************** Features - 20.16.1 -~~~~~~~~~~~~~~~~~~ -- Update Nushell activation scripts to version 0.67 - by :user:`kubouch`. (`#2386 `_) +================== +- Update Nushell activation scripts to version 0.67 - by :user:`kubouch`. (`#2386 + `_) -v20.16.0 (2022-07-25) ---------------------- +*********************** + v20.16.0 (2022-07-25) +*********************** Features - 20.16.0 -~~~~~~~~~~~~~~~~~~ -- Drop support for running under Python 2 (still can generate Python 2 environments) - by :user:`gaborbernat`. (`#2382 `_) -- Upgrade embedded pip to ``22.2`` from ``22.1.2`` and setuptools to ``63.2.0`` from ``62.6.0`` - - by :user:`gaborbernat`. (`#2383 `_) +================== +- Drop support for running under Python 2 (still can generate Python 2 environments) - by :user:`gaborbernat`. (`#2382 + `_) +- Upgrade embedded pip to ``22.2`` from ``22.1.2`` and setuptools to ``63.2.0`` from ``62.6.0`` - by + :user:`gaborbernat`. (`#2383 `_) -v20.15.1 (2022-06-28) ---------------------- +*********************** + v20.15.1 (2022-06-28) +*********************** Bugfixes - 20.15.1 -~~~~~~~~~~~~~~~~~~ -- Fix the incorrect operation when ``setuptools`` plugins output something into ``stdout``. (`#2335 `_) -- CPython3Windows creator ignores missing ``DLLs`` dir. (`#2368 `_) +================== +- Fix the incorrect operation when ``setuptools`` plugins output something into ``stdout``. (`#2335 + `_) +- CPython3Windows creator ignores missing ``DLLs`` dir. (`#2368 `_) -v20.15.0 (2022-06-25) ---------------------- +*********************** + v20.15.0 (2022-06-25) +*********************** Features - 20.15.0 -~~~~~~~~~~~~~~~~~~ -- Support for Windows embeddable Python package: includes ``python.zip`` in the creator sources - - by :user:`reksarka`. (`#1774 `_) +================== + +- Support for Windows embeddable Python package: includes ``python.zip`` in the creator sources - by + :user:`reksarka`. (`#1774 `_) Bugfixes - 20.15.0 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded setuptools to ``62.3.3`` from ``62.6.0`` and pip to ``22.1.2`` from ``22.0.4`` - - by :user:`gaborbernat`. (`#2348 `_) -- Use ``shlex.quote`` instead of deprecated ``pipes.quote`` in Python 3 - by :user:`frenzymadness`. (`#2351 `_) -- Fix Windows PyPy 3.6 - by :user:`reksarka`. (`#2363 `_) +================== +- Upgrade embedded setuptools to ``62.3.3`` from ``62.6.0`` and pip to ``22.1.2`` from ``22.0.4`` - by + :user:`gaborbernat`. (`#2348 `_) +- Use ``shlex.quote`` instead of deprecated ``pipes.quote`` in Python 3 - by :user:`frenzymadness`. (`#2351 + `_) +- Fix Windows PyPy 3.6 - by :user:`reksarka`. (`#2363 `_) -v20.14.1 (2022-04-11) ---------------------- +*********************** + v20.14.1 (2022-04-11) +*********************** Features - 20.14.1 -~~~~~~~~~~~~~~~~~~ -- Support for creating a virtual environment from a Python 2.7 framework on macOS 12 - by :user:`nickhutchinson`. (`#2284 `_) +================== + +- Support for creating a virtual environment from a Python 2.7 framework on macOS 12 - by :user:`nickhutchinson`. + (`#2284 `_) Bugfixes - 20.14.1 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded setuptools to ``62.1.0`` from ``61.0.0`` - by :user:`gaborbernat`. (`#2327 `_) +================== +- Upgrade embedded setuptools to ``62.1.0`` from ``61.0.0`` - by :user:`gaborbernat`. (`#2327 + `_) -v20.14.0 (2022-03-25) ---------------------- +*********************** + v20.14.0 (2022-03-25) +*********************** Features - 20.14.0 -~~~~~~~~~~~~~~~~~~ -- Support Nushell activation scripts with nu version ``0.60`` - by :user:`kubouch`. (`#2321 `_) +================== + +- Support Nushell activation scripts with nu version ``0.60`` - by :user:`kubouch`. (`#2321 + `_) Bugfixes - 20.14.0 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded setuptools to ``61.0.0`` from ``60.10.0`` - by :user:`gaborbernat`. (`#2322 `_) +================== -v20.13.4 (2022-03-18) ---------------------- +- Upgrade embedded setuptools to ``61.0.0`` from ``60.10.0`` - by :user:`gaborbernat`. (`#2322 + `_) + +*********************** + v20.13.4 (2022-03-18) +*********************** Bugfixes - 20.13.4 -~~~~~~~~~~~~~~~~~~ -- Improve performance of python startup inside created virtualenvs - by :user:`asottile`. (`#2317 `_) -- Upgrade embedded setuptools to ``60.10.0`` from ``60.9.3`` - by :user:`gaborbernat`. (`#2320 `_) +================== + +- Improve performance of python startup inside created virtualenvs - by :user:`asottile`. (`#2317 + `_) +- Upgrade embedded setuptools to ``60.10.0`` from ``60.9.3`` - by :user:`gaborbernat`. (`#2320 + `_) -v20.13.3 (2022-03-07) ---------------------- +*********************** + v20.13.3 (2022-03-07) +*********************** Bugfixes - 20.13.3 -~~~~~~~~~~~~~~~~~~ -- Avoid symlinking the contents of ``/usr`` into PyPy3.8+ virtualenvs - by :user:`stefanor`. (`#2310 `_) -- Bump embed pip from ``22.0.3`` to ``22.0.4`` - by :user:`gaborbernat`. (`#2311 `_) +================== +- Avoid symlinking the contents of ``/usr`` into PyPy3.8+ virtualenvs - by :user:`stefanor`. (`#2310 + `_) +- Bump embed pip from ``22.0.3`` to ``22.0.4`` - by :user:`gaborbernat`. (`#2311 + `_) -v20.13.2 (2022-02-24) ---------------------- +*********************** + v20.13.2 (2022-02-24) +*********************** Bugfixes - 20.13.2 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded setuptools to ``60.9.3`` from ``60.6.0`` - by :user:`gaborbernat`. (`#2306 `_) +================== +- Upgrade embedded setuptools to ``60.9.3`` from ``60.6.0`` - by :user:`gaborbernat`. (`#2306 + `_) -v20.13.1 (2022-02-05) ---------------------- +*********************** + v20.13.1 (2022-02-05) +*********************** Bugfixes - 20.13.1 -~~~~~~~~~~~~~~~~~~ -- fix "execv() arg 2 must contain only strings" error on M1 MacOS (`#2282 `_) -- Upgrade embedded setuptools to ``60.5.0`` from ``60.2.0`` - by :user:`asottile`. (`#2289 `_) -- Upgrade embedded pip to ``22.0.3`` and setuptools to ``60.6.0`` - by :user:`gaborbernat` and :user:`asottile`. (`#2294 `_) +================== +- fix "execv() arg 2 must contain only strings" error on M1 MacOS (`#2282 + `_) +- Upgrade embedded setuptools to ``60.5.0`` from ``60.2.0`` - by :user:`asottile`. (`#2289 + `_) +- Upgrade embedded pip to ``22.0.3`` and setuptools to ``60.6.0`` - by :user:`gaborbernat` and :user:`asottile`. (`#2294 + `_) -v20.13.0 (2022-01-02) ---------------------- +*********************** + v20.13.0 (2022-01-02) +*********************** Features - 20.13.0 -~~~~~~~~~~~~~~~~~~ -- Add downloaded wheel information in the relevant JSON embed file to - prevent additional downloads of the same wheel. - by :user:`mayeut`. (`#2268 `_) +================== + +- Add downloaded wheel information in the relevant JSON embed file to prevent additional downloads of the same wheel. - + by :user:`mayeut`. (`#2268 `_) Bugfixes - 20.13.0 -~~~~~~~~~~~~~~~~~~ -- Fix ``AttributeError: 'bool' object has no attribute 'error'`` when creating a - Python 2.x virtualenv on macOS - by ``moreati``. (`#2269 `_) -- Fix ``PermissionError: [Errno 1] Operation not permitted`` when creating a - Python 2.x virtualenv on macOS/arm64 - by ``moreati``. (`#2271 `_) +================== +- Fix ``AttributeError: 'bool' object has no attribute 'error'`` when creating a Python 2.x virtualenv on macOS - by + ``moreati``. (`#2269 `_) +- Fix ``PermissionError: [Errno 1] Operation not permitted`` when creating a Python 2.x virtualenv on macOS/arm64 - by + ``moreati``. (`#2271 `_) -v20.12.1 (2022-01-01) ---------------------- +*********************** + v20.12.1 (2022-01-01) +*********************** Bugfixes - 20.12.1 -~~~~~~~~~~~~~~~~~~ -- Try using previous updates of ``pip``, ``setuptools`` & ``wheel`` - when inside an update grace period rather than always falling back - to embedded wheels - by :user:`mayeut`. (`#2265 `_) -- New patch versions of ``pip``, ``setuptools`` & ``wheel`` are now - returned in the expected timeframe. - by :user:`mayeut`. (`#2266 `_) -- Manual upgrades of ``pip``, ``setuptools`` & ``wheel`` are - not discarded by a periodic update - by :user:`mayeut`. (`#2267 `_) +================== +- Try using previous updates of ``pip``, ``setuptools`` & ``wheel`` when inside an update grace period rather than + always falling back to embedded wheels - by :user:`mayeut`. (`#2265 + `_) +- New patch versions of ``pip``, ``setuptools`` & ``wheel`` are now returned in the expected timeframe. - by + :user:`mayeut`. (`#2266 `_) +- Manual upgrades of ``pip``, ``setuptools`` & ``wheel`` are not discarded by a periodic update - by :user:`mayeut`. + (`#2267 `_) -v20.12.0 (2021-12-31) ---------------------- +*********************** + v20.12.0 (2021-12-31) +*********************** Features - 20.12.0 -~~~~~~~~~~~~~~~~~~ -- Sign the python2 exe on Darwin arm64 - by :user:`tmspicer`. (`#2233 `_) +================== + +- Sign the python2 exe on Darwin arm64 - by :user:`tmspicer`. (`#2233 + `_) Bugfixes - 20.12.0 -~~~~~~~~~~~~~~~~~~ -- Fix ``--download`` option - by :user:`mayeut`. (`#2120 `_) -- Upgrade embedded setuptools to ``60.2.0`` from ``60.1.1`` - by :user:`gaborbernat`. (`#2263 `_) +================== +- Fix ``--download`` option - by :user:`mayeut`. (`#2120 `_) +- Upgrade embedded setuptools to ``60.2.0`` from ``60.1.1`` - by :user:`gaborbernat`. (`#2263 + `_) -v20.11.2 (2021-12-29) ---------------------- +*********************** + v20.11.2 (2021-12-29) +*********************** Bugfixes - 20.11.2 -~~~~~~~~~~~~~~~~~~ -- Fix installation of pinned versions of ``pip``, ``setuptools`` & ``wheel`` - by :user:`mayeut`. (`#2203 `_) +================== +- Fix installation of pinned versions of ``pip``, ``setuptools`` & ``wheel`` - by :user:`mayeut`. (`#2203 + `_) -v20.11.1 (2021-12-29) ---------------------- +*********************** + v20.11.1 (2021-12-29) +*********************** Bugfixes - 20.11.1 -~~~~~~~~~~~~~~~~~~ -- Bump embed setuptools to ``60.1.1`` from ``60.1.0`` - by :user:`gaborbernat`. (`#2258 `_) +================== +- Bump embed setuptools to ``60.1.1`` from ``60.1.0`` - by :user:`gaborbernat`. (`#2258 + `_) -v20.11.0 (2021-12-28) ---------------------- +*********************** + v20.11.0 (2021-12-28) +*********************** Features - 20.11.0 -~~~~~~~~~~~~~~~~~~ -- Avoid deprecation warning from py-filelock argument - by :user:`ofek`. (`#2237 `_) -- Upgrade embedded setuptools to ``61.1.0`` from ``58.3.0`` - by :user:`gaborbernat`. (`#2240 `_) -- Drop the runtime dependency of ``backports.entry-points-selectable`` - by :user:`hroncok`. (`#2246 `_) -- Fish: PATH variables should not be quoted when being set - by :user:`d3dave`. (`#2248 `_) +================== +- Avoid deprecation warning from py-filelock argument - by :user:`ofek`. (`#2237 + `_) +- Upgrade embedded setuptools to ``61.1.0`` from ``58.3.0`` - by :user:`gaborbernat`. (`#2240 + `_) +- Drop the runtime dependency of ``backports.entry-points-selectable`` - by :user:`hroncok`. (`#2246 + `_) +- Fish: PATH variables should not be quoted when being set - by :user:`d3dave`. (`#2248 + `_) -v20.10.0 (2021-11-01) ---------------------- +*********************** + v20.10.0 (2021-11-01) +*********************** Features - 20.10.0 -~~~~~~~~~~~~~~~~~~ +================== + - If a ``"venv"`` install scheme exists in ``sysconfig``, virtualenv now uses it to create new virtual environments. - This allows Python distributors, such as Fedora, to patch/replace the default install scheme without affecting - the paths in new virtual environments. - A similar technique `was proposed to Python, for the venv module `_ - by ``hroncok`` (`#2208 `_) -- The activated virtualenv prompt is now always wrapped in parentheses. This - affects venvs created with the ``--prompt`` attribute, and matches virtualenv's - behavior on par with venv. (`#2224 `_) + This allows Python distributors, such as Fedora, to patch/replace the default install scheme without affecting the + paths in new virtual environments. A similar technique `was proposed to Python, for the venv module + `_ - by ``hroncok`` (`#2208 `_) +- The activated virtualenv prompt is now always wrapped in parentheses. This affects venvs created with the ``--prompt`` + attribute, and matches virtualenv's behavior on par with venv. (`#2224 + `_) Bugfixes - 20.10.0 -~~~~~~~~~~~~~~~~~~ -- Fix broken prompt set up by activate.bat - by :user:`SiggyBar`. (`#2225 `_) +================== +- Fix broken prompt set up by activate.bat - by :user:`SiggyBar`. (`#2225 + `_) -v20.9.0 (2021-10-23) --------------------- +********************** + v20.9.0 (2021-10-23) +********************** Features - 20.9.0 -~~~~~~~~~~~~~~~~~ -- Special-case ``--prompt .`` to the name of the current directory - by :user:`rkm`. (`#2220 `_) +================= + +- Special-case ``--prompt .`` to the name of the current directory - by :user:`rkm`. (`#2220 + `_) - Add libffi-8.dll to pypy windows `#2218 `_ - by :user:`mattip` Bugfixes - 20.9.0 -~~~~~~~~~~~~~~~~~ -- Fixed path collision that could lead to a PermissionError or writing to system - directories when using PyPy3.8 - by :user:`mgorny`. (`#2182 `_) +================= + +- Fixed path collision that could lead to a PermissionError or writing to system directories when using PyPy3.8 - by + :user:`mgorny`. (`#2182 `_) - Upgrade embedded setuptools to ``58.3.0`` from ``58.1.0`` and pip to ``21.3.1`` from ``21.2.4`` - by :user:`gaborbernat`. (`#2205 `_) -- Remove stray closing parenthesis in activate.bat - by :user:`SiggyBar`. (`#2221 `_) - +- Remove stray closing parenthesis in activate.bat - by :user:`SiggyBar`. (`#2221 + `_) -v20.8.1 (2021-09-24) --------------------- +********************** + v20.8.1 (2021-09-24) +********************** Bugfixes - 20.8.1 -~~~~~~~~~~~~~~~~~ -- Fixed a bug where while creating a venv on top of an existing one, without cleaning, when seeded - wheel version mismatch occurred, multiple ``.dist-info`` directories may be present, confounding entrypoint - discovery - by :user:`arcivanov` (`#2185 `_) -- Bump embed setuptools from ``58.0.4`` to ``58.1.0`` - by :user:`gaborbernat`. (`#2195 `_) +================= + +- Fixed a bug where while creating a venv on top of an existing one, without cleaning, when seeded wheel version + mismatch occurred, multiple ``.dist-info`` directories may be present, confounding entrypoint discovery - by + :user:`arcivanov` (`#2185 `_) +- Bump embed setuptools from ``58.0.4`` to ``58.1.0`` - by :user:`gaborbernat`. (`#2195 + `_) Misc - 20.8.1 -~~~~~~~~~~~~~ -- `#2189 `_ +============= +- `#2189 `_ -v20.8.0 (2021-09-16) --------------------- +********************** + v20.8.0 (2021-09-16) +********************** -* upgrade embedded setuptools to ``58.0.4`` from ``57.4.0`` and pip to ``21.2.4`` from ``21.2.3`` -* Add nushell activation script +- upgrade embedded setuptools to ``58.0.4`` from ``57.4.0`` and pip to ``21.2.4`` from ``21.2.3`` +- Add nushell activation script -v20.7.2 (2021-08-10) --------------------- +********************** + v20.7.2 (2021-08-10) +********************** Bugfixes - 20.7.2 -~~~~~~~~~~~~~~~~~ -- Upgrade embedded pip to ``21.2.3`` from ``21.2.2`` and wheel to ``0.37.0`` from ``0.36.2`` - by :user:`gaborbernat`. (`#2168 `_) +================= +- Upgrade embedded pip to ``21.2.3`` from ``21.2.2`` and wheel to ``0.37.0`` from ``0.36.2`` - by :user:`gaborbernat`. + (`#2168 `_) -v20.7.1 (2021-08-09) --------------------- +********************** + v20.7.1 (2021-08-09) +********************** Bugfixes - 20.7.1 -~~~~~~~~~~~~~~~~~ -- Fix unpacking dictionary items in PythonInfo.install_path (`#2165 `_) +================= +- Fix unpacking dictionary items in PythonInfo.install_path (`#2165 `_) -v20.7.0 (2021-07-31) --------------------- +********************** + v20.7.0 (2021-07-31) +********************** Bugfixes - 20.7.0 -~~~~~~~~~~~~~~~~~ -- upgrade embedded pip to ``21.2.2`` from ``21.1.3`` and setuptools to ``57.4.0`` from ``57.1.0`` - by :user:`gaborbernat` (`#2159 `_) +================= + +- upgrade embedded pip to ``21.2.2`` from ``21.1.3`` and setuptools to ``57.4.0`` from ``57.1.0`` - by + :user:`gaborbernat` (`#2159 `_) Deprecations and Removals - 20.7.0 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Removed ``xonsh`` activator due to this breaking fairly often the CI and lack of support from those packages - maintainers, upstream is encouraged to continue supporting the project as a - `plugin `_ - by :user:`gaborbernat`. (`#2160 `_) +================================== +- Removed ``xonsh`` activator due to this breaking fairly often the CI and lack of support from those packages + maintainers, upstream is encouraged to continue supporting the project as a `plugin + `_ - by :user:`gaborbernat`. (`#2160 + `_) -v20.6.0 (2021-07-14) --------------------- +********************** + v20.6.0 (2021-07-14) +********************** Features - 20.6.0 -~~~~~~~~~~~~~~~~~ -- Support Python interpreters without ``distutils`` (fallback to ``syconfig`` in these cases) - by :user:`gaborbernat`. (`#1910 `_) +================= +- Support Python interpreters without ``distutils`` (fallback to ``syconfig`` in these cases) - by :user:`gaborbernat`. + (`#1910 `_) -v20.5.0 (2021-07-13) --------------------- +********************** + v20.5.0 (2021-07-13) +********************** Features - 20.5.0 -~~~~~~~~~~~~~~~~~ -- Plugins now use 'selectable' entry points - by :user:`jaraco`. (`#2093 `_) +================= + +- Plugins now use 'selectable' entry points - by :user:`jaraco`. (`#2093 + `_) - add libffi-7.dll to the hard-coded list of dlls for PyPy (`#2141 `_) -- Use the better maintained ``platformdirs`` instead of ``appdirs`` - by :user:`gaborbernat`. (`#2142 `_) +- Use the better maintained ``platformdirs`` instead of ``appdirs`` - by :user:`gaborbernat`. (`#2142 + `_) Bugfixes - 20.5.0 -~~~~~~~~~~~~~~~~~ -- Bump pip the embedded pip ``21.1.3`` and setuptools to ``57.1.0`` - by :user:`gaborbernat`. (`#2135 `_) +================= + +- Bump pip the embedded pip ``21.1.3`` and setuptools to ``57.1.0`` - by :user:`gaborbernat`. (`#2135 + `_) Deprecations and Removals - 20.5.0 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Drop python ``3.4`` support as it has been over 2 years since EOL - by :user:`gaborbernat`. (`#2141 `_) +================================== +- Drop python ``3.4`` support as it has been over 2 years since EOL - by :user:`gaborbernat`. (`#2141 + `_) -v20.4.7 (2021-05-24) --------------------- +********************** + v20.4.7 (2021-05-24) +********************** Bugfixes - 20.4.7 -~~~~~~~~~~~~~~~~~ -- Upgrade embedded pip to ``21.1.2`` and setuptools to ``57.0.0`` - by :user:`gaborbernat`. (`#2123 `_) +================= +- Upgrade embedded pip to ``21.1.2`` and setuptools to ``57.0.0`` - by :user:`gaborbernat`. (`#2123 + `_) -v20.4.6 (2021-05-05) --------------------- +********************** + v20.4.6 (2021-05-05) +********************** Bugfixes - 20.4.6 -~~~~~~~~~~~~~~~~~ -- Fix ``site.getsitepackages()`` broken on python2 on debian - by :user:`freundTech`. (`#2105 `_) +================= +- Fix ``site.getsitepackages()`` broken on python2 on debian - by :user:`freundTech`. (`#2105 + `_) -v20.4.5 (2021-05-05) --------------------- +********************** + v20.4.5 (2021-05-05) +********************** Bugfixes - 20.4.5 -~~~~~~~~~~~~~~~~~ -- Bump pip to ``21.1.1`` from ``21.0.1`` - by :user:`gaborbernat`. (`#2104 `_) -- Fix ``site.getsitepackages()`` ignoring ``--system-site-packages`` on python2 - by :user:`freundTech`. (`#2106 `_) +================= +- Bump pip to ``21.1.1`` from ``21.0.1`` - by :user:`gaborbernat`. (`#2104 + `_) +- Fix ``site.getsitepackages()`` ignoring ``--system-site-packages`` on python2 - by :user:`freundTech`. (`#2106 + `_) -v20.4.4 (2021-04-20) --------------------- +********************** + v20.4.4 (2021-04-20) +********************** Bugfixes - 20.4.4 -~~~~~~~~~~~~~~~~~ -- Built in discovery class is always preferred over plugin supplied classes. (`#2087 `_) -- Upgrade embedded setuptools to ``56.0.0`` by :user:`gaborbernat`. (`#2094 `_) +================= +- Built in discovery class is always preferred over plugin supplied classes. (`#2087 + `_) +- Upgrade embedded setuptools to ``56.0.0`` by :user:`gaborbernat`. (`#2094 + `_) -v20.4.3 (2021-03-16) --------------------- +********************** + v20.4.3 (2021-03-16) +********************** Bugfixes - 20.4.3 -~~~~~~~~~~~~~~~~~ -- Bump embedded setuptools from ``52.0.0`` to ``54.1.2`` - by :user:`gaborbernat` (`#2069 `_) -- Fix PyPy3 stdlib on Windows is incorrect - by :user:`gaborbernat`. (`#2071 `_) +================= +- Bump embedded setuptools from ``52.0.0`` to ``54.1.2`` - by :user:`gaborbernat` (`#2069 + `_) +- Fix PyPy3 stdlib on Windows is incorrect - by :user:`gaborbernat`. (`#2071 + `_) -v20.4.2 (2021-02-01) --------------------- +********************** + v20.4.2 (2021-02-01) +********************** Bugfixes - 20.4.2 -~~~~~~~~~~~~~~~~~ -- Running virtualenv ``--upgrade-embed-wheels`` crashes - by :user:`gaborbernat`. (`#2058 `_) +================= +- Running virtualenv ``--upgrade-embed-wheels`` crashes - by :user:`gaborbernat`. (`#2058 + `_) -v20.4.1 (2021-01-31) --------------------- +********************** + v20.4.1 (2021-01-31) +********************** Bugfixes - 20.4.1 -~~~~~~~~~~~~~~~~~ -- Bump embedded pip and setuptools packages to latest upstream supported (``21.0.1`` and ``52.0.0``) - by :user:`gaborbernat`. (`#2060 `_) +================= +- Bump embedded pip and setuptools packages to latest upstream supported (``21.0.1`` and ``52.0.0``) - by + :user:`gaborbernat`. (`#2060 `_) -v20.4.0 (2021-01-19) --------------------- +********************** + v20.4.0 (2021-01-19) +********************** Features - 20.4.0 -~~~~~~~~~~~~~~~~~ +================= + - On the programmatic API allow passing in the environment variable dictionary to use, defaults to ``os.environ`` if not specified - by :user:`gaborbernat`. (`#2054 `_) Bugfixes - 20.4.0 -~~~~~~~~~~~~~~~~~ -- Upgrade embedded setuptools to ``51.3.3`` from ``51.1.2`` - by :user:`gaborbernat`. (`#2055 `_) +================= +- Upgrade embedded setuptools to ``51.3.3`` from ``51.1.2`` - by :user:`gaborbernat`. (`#2055 + `_) -v20.3.1 (2021-01-13) --------------------- +********************** + v20.3.1 (2021-01-13) +********************** Bugfixes - 20.3.1 -~~~~~~~~~~~~~~~~~ -- Bump embed pip to ``20.3.3``, setuptools to ``51.1.1`` and wheel to ``0.36.2`` - by :user:`gaborbernat`. (`#2036 `_) -- Allow unfunctioning of pydoc to fail freely so that virtualenvs can be - activated under Zsh with set -e (since otherwise ``unset -f`` and - ``unfunction`` exit with 1 if the function does not exist in Zsh) - by - :user:`d125q`. (`#2049 `_) -- Drop cached python information if the system executable is no longer present (for example when the executable is a - shim and the mapped executable is replaced - such is the case with pyenv) - by :user:`gaborbernat`. (`#2050 `_) +================= +- Bump embed pip to ``20.3.3``, setuptools to ``51.1.1`` and wheel to ``0.36.2`` - by :user:`gaborbernat`. (`#2036 + `_) +- Allow unfunctioning of pydoc to fail freely so that virtualenvs can be activated under Zsh with set -e (since + otherwise ``unset -f`` and ``unfunction`` exit with 1 if the function does not exist in Zsh) - by :user:`d125q`. + (`#2049 `_) +- Drop cached python information if the system executable is no longer present (for example when the executable is a + shim and the mapped executable is replaced - such is the case with pyenv) - by :user:`gaborbernat`. (`#2050 + `_) -v20.3.0 (2021-01-10) --------------------- +********************** + v20.3.0 (2021-01-10) +********************** Features - 20.3.0 -~~~~~~~~~~~~~~~~~ +================= + - The builtin discovery takes now a ``--try-first-with`` argument and is first attempted as valid interpreters. One can - use this to force discovery of a given python executable when the discovery order/mechanism raises errors - - by :user:`gaborbernat`. (`#2046 `_) + use this to force discovery of a given python executable when the discovery order/mechanism raises errors - by + :user:`gaborbernat`. (`#2046 `_) Bugfixes - 20.3.0 -~~~~~~~~~~~~~~~~~ -- On Windows python ``3.7+`` distributions where the exe shim is missing fallback to the old ways - by :user:`gaborbernat`. (`#1986 `_) -- When discovering interpreters on Windows, via the PEP-514, prefer ``PythonCore`` releases over other ones. virtualenv - is used via pip mostly by this distribution, so prefer it over other such as conda - by :user:`gaborbernat`. (`#2046 `_) +================= +- On Windows python ``3.7+`` distributions where the exe shim is missing fallback to the old ways - by + :user:`gaborbernat`. (`#1986 `_) +- When discovering interpreters on Windows, via the PEP-514, prefer ``PythonCore`` releases over other ones. virtualenv + is used via pip mostly by this distribution, so prefer it over other such as conda - by :user:`gaborbernat`. (`#2046 + `_) -v20.2.2 (2020-12-07) --------------------- +********************** + v20.2.2 (2020-12-07) +********************** Bugfixes - 20.2.2 -~~~~~~~~~~~~~~~~~ -- Bump pip to ``20.3.1``, setuptools to ``51.0.0`` and wheel to ``0.36.1`` - by :user:`gaborbernat`. (`#2029 `_) +================= +- Bump pip to ``20.3.1``, setuptools to ``51.0.0`` and wheel to ``0.36.1`` - by :user:`gaborbernat`. (`#2029 + `_) -v20.2.1 (2020-11-23) --------------------- +********************** + v20.2.1 (2020-11-23) +********************** No significant changes. - -v20.2.0 (2020-11-21) --------------------- +********************** + v20.2.0 (2020-11-21) +********************** Features - 20.2.0 -~~~~~~~~~~~~~~~~~ -- Optionally skip VCS ignore directive for entire virtualenv directory, using option :option:`no-vcs-ignore`, by default ``False``. (`#2003 `_) -- Add ``--read-only-app-data`` option to allow for creation based on an existing - app data cache which is non-writable. This may be useful (for example) to - produce a docker image where the app-data is pre-populated. +================= + +- Optionally skip VCS ignore directive for entire virtualenv directory, using option :option:`no-vcs-ignore`, by default + ``False``. (`#2003 `_) +- Add ``--read-only-app-data`` option to allow for creation based on an existing app data cache which is non-writable. + This may be useful (for example) to produce a docker image where the app-data is pre-populated. .. code-block:: dockerfile @@ -1087,114 +1324,147 @@ Features - 20.2.0 Patch by :user:`asottile`. (`#2009 `_) Bugfixes - 20.2.0 -~~~~~~~~~~~~~~~~~ -- Fix processing of the ``VIRTUALENV_PYTHON`` environment variable and make it - multi-value as well (separated by comma) - by :user:`pneff`. (`#1998 `_) +================= +- Fix processing of the ``VIRTUALENV_PYTHON`` environment variable and make it multi-value as well (separated by comma) + - by :user:`pneff`. (`#1998 `_) -v20.1.0 (2020-10-25) --------------------- +********************** + v20.1.0 (2020-10-25) +********************** Features - 20.1.0 -~~~~~~~~~~~~~~~~~ +================= + - The python specification can now take one or more values, first found is used to create the virtual environment - by :user:`gaborbernat`. (`#1995 `_) - -v20.0.35 (2020-10-15) ---------------------- +*********************** + v20.0.35 (2020-10-15) +*********************** Bugfixes - 20.0.35 -~~~~~~~~~~~~~~~~~~ -- Bump embedded setuptools from ``50.3.0`` to ``50.3.1`` - by :user:`gaborbernat`. (`#1982 `_) -- After importing virtualenv passing cwd to a subprocess calls breaks with ``invalid directory`` - by :user:`gaborbernat`. (`#1983 `_) +================== +- Bump embedded setuptools from ``50.3.0`` to ``50.3.1`` - by :user:`gaborbernat`. (`#1982 + `_) +- After importing virtualenv passing cwd to a subprocess calls breaks with ``invalid directory`` - by + :user:`gaborbernat`. (`#1983 `_) -v20.0.34 (2020-10-12) ---------------------- +*********************** + v20.0.34 (2020-10-12) +*********************** Bugfixes - 20.0.34 -~~~~~~~~~~~~~~~~~~ -- Align with venv module when creating virtual environments with builtin creator on Windows 3.7 and later - - by :user:`gaborbernat`. (`#1782 `_) -- Handle Cygwin path conversion in the activation script - by :user:`davidcoghlan`. (`#1969 `_) +================== +- Align with venv module when creating virtual environments with builtin creator on Windows 3.7 and later - by + :user:`gaborbernat`. (`#1782 `_) +- Handle Cygwin path conversion in the activation script - by :user:`davidcoghlan`. (`#1969 + `_) -v20.0.33 (2020-10-04) ---------------------- +*********************** + v20.0.33 (2020-10-04) +*********************** Bugfixes - 20.0.33 -~~~~~~~~~~~~~~~~~~ -- Fix ``None`` type error in cygwin if POSIX path in dest - by :user:`danyeaw`. (`#1962 `_) -- Fix Python 3.4 incompatibilities (added back to the CI) - by :user:`gaborbernat`. (`#1963 `_) +================== +- Fix ``None`` type error in cygwin if POSIX path in dest - by :user:`danyeaw`. (`#1962 + `_) +- Fix Python 3.4 incompatibilities (added back to the CI) - by :user:`gaborbernat`. (`#1963 + `_) -v20.0.32 (2020-10-01) ---------------------- +*********************** + v20.0.32 (2020-10-01) +*********************** Bugfixes - 20.0.32 -~~~~~~~~~~~~~~~~~~ -- For activation scripts always use UNIX line endings (unless it's BATCH shell related) - by :user:`saytosid`. (`#1818 `_) -- Upgrade embedded pip to ``20.2.1`` and setuptools to ``49.4.0`` - by :user:`gaborbernat`. (`#1918 `_) -- Avoid spawning new windows when doing seed package upgrades in the background on Windows - by :user:`gaborbernat`. (`#1928 `_) -- Fix a bug that reading and writing on the same file may cause race on multiple processes. (`#1938 `_) -- Upgrade embedded setuptools to ``50.2.0`` and pip to ``20.2.3`` - by :user:`gaborbernat`. (`#1939 `_) -- Provide correct path for bash activator in cygwin or msys2 - by :user:`danyeaw`. (`#1940 `_) -- Relax importlib requirement to allow version<3 - by :user:`usamasadiq` (`#1953 `_) -- pth files were not processed on CPython2 if $PYTHONPATH was pointing to site-packages/ - by :user:`navytux`. (`#1959 `_) (`#1960 `_) - - -v20.0.31 (2020-08-17) ---------------------- +================== + +- For activation scripts always use UNIX line endings (unless it's BATCH shell related) - by :user:`saytosid`. (`#1818 + `_) +- Upgrade embedded pip to ``20.2.1`` and setuptools to ``49.4.0`` - by :user:`gaborbernat`. (`#1918 + `_) +- Avoid spawning new windows when doing seed package upgrades in the background on Windows - by :user:`gaborbernat`. + (`#1928 `_) +- Fix a bug that reading and writing on the same file may cause race on multiple processes. (`#1938 + `_) +- Upgrade embedded setuptools to ``50.2.0`` and pip to ``20.2.3`` - by :user:`gaborbernat`. (`#1939 + `_) +- Provide correct path for bash activator in cygwin or msys2 - by :user:`danyeaw`. (`#1940 + `_) +- Relax importlib requirement to allow version<3 - by :user:`usamasadiq` (`#1953 + `_) +- pth files were not processed on CPython2 if $PYTHONPATH was pointing to site-packages/ - by :user:`navytux`. (`#1959 + `_) (`#1960 `_) + +*********************** + v20.0.31 (2020-08-17) +*********************** Bugfixes - 20.0.31 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded pip to ``20.2.1``, setuptools to ``49.6.0`` and wheel to ``0.35.1`` - by :user:`gaborbernat`. (`#1918 `_) +================== +- Upgrade embedded pip to ``20.2.1``, setuptools to ``49.6.0`` and wheel to ``0.35.1`` - by :user:`gaborbernat`. (`#1918 + `_) -v20.0.30 (2020-08-04) ---------------------- +*********************** + v20.0.30 (2020-08-04) +*********************** Bugfixes - 20.0.30 -~~~~~~~~~~~~~~~~~~ -- Upgrade pip to ``20.2.1`` and setuptools to ``49.2.1`` - by :user:`gaborbernat`. (`#1915 `_) +================== +- Upgrade pip to ``20.2.1`` and setuptools to ``49.2.1`` - by :user:`gaborbernat`. (`#1915 + `_) -v20.0.29 (2020-07-31) ---------------------- +*********************** + v20.0.29 (2020-07-31) +*********************** Bugfixes - 20.0.29 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded pip from version ``20.1.2`` to ``20.2`` - by :user:`gaborbernat`. (`#1909 `_) +================== +- Upgrade embedded pip from version ``20.1.2`` to ``20.2`` - by :user:`gaborbernat`. (`#1909 + `_) -v20.0.28 (2020-07-24) ---------------------- +*********************** + v20.0.28 (2020-07-24) +*********************** Bugfixes - 20.0.28 -~~~~~~~~~~~~~~~~~~ -- Fix test suite failing if run from system Python - by :user:`gaborbernat`. (`#1882 `_) +================== + +- Fix test suite failing if run from system Python - by :user:`gaborbernat`. (`#1882 + `_) - Provide ``setup_logging`` flag to python API so that users can bypass logging handling if their application already performs this - by :user:`gaborbernat`. (`#1896 `_) - Use ``\n`` instead if ``\r\n`` as line separator for report (because Python already performs this transformation - automatically upon write to the logging pipe) - by :user:`gaborbernat`. (`#1905 `_) + automatically upon write to the logging pipe) - by :user:`gaborbernat`. (`#1905 + `_) - -v20.0.27 (2020-07-15) ---------------------- +*********************** + v20.0.27 (2020-07-15) +*********************** Bugfixes - 20.0.27 -~~~~~~~~~~~~~~~~~~ -- No longer preimport threading to fix support for `gpython `_ and `gevent `_ - by :user:`navytux`. (`#1897 `_) -- Upgrade setuptools from ``49.2.0`` on ``Python 3.5+`` - by :user:`gaborbernat`. (`#1898 `_) +================== +- No longer preimport threading to fix support for `gpython `_ and `gevent + `_ - by :user:`navytux`. (`#1897 `_) +- Upgrade setuptools from ``49.2.0`` on ``Python 3.5+`` - by :user:`gaborbernat`. (`#1898 + `_) -v20.0.26 (2020-07-07) ---------------------- +*********************** + v20.0.26 (2020-07-07) +*********************** Bugfixes - 20.0.26 -~~~~~~~~~~~~~~~~~~ -- Bump dependency ``distutils >= 0.3.1`` - by :user:`gaborbernat`. (`#1880 `_) +================== + +- Bump dependency ``distutils >= 0.3.1`` - by :user:`gaborbernat`. (`#1880 + `_) - Improve periodic update handling: - better logging output while running and enable logging on background process call ( @@ -1203,36 +1473,45 @@ Bugfixes - 20.0.26 - stop downloading wheels once we reach the embedded version, by :user:`gaborbernat`. (`#1883 `_) -- Do not print error message if the application exists with ``SystemExit(0)`` - by :user:`gaborbernat`. (`#1885 `_) -- Upgrade embedded setuptools from ``47.3.1`` to ``49.1.0`` for Python ``3.5+`` - by :user:`gaborbernat`. (`#1887 `_) +- Do not print error message if the application exists with ``SystemExit(0)`` - by :user:`gaborbernat`. (`#1885 + `_) +- Upgrade embedded setuptools from ``47.3.1`` to ``49.1.0`` for Python ``3.5+`` - by :user:`gaborbernat`. (`#1887 + `_) -v20.0.25 (2020-06-23) ---------------------- +*********************** + v20.0.25 (2020-06-23) +*********************** Bugfixes - 20.0.25 -~~~~~~~~~~~~~~~~~~ -- Fix that when the ``app-data`` seeders image creation fails the exception is silently ignored. Avoid two virtual environment creations to step on each others toes by using a lock while creating the base images. By :user:`gaborbernat`. (`#1869 `_) +================== +- Fix that when the ``app-data`` seeders image creation fails the exception is silently ignored. Avoid two virtual + environment creations to step on each others toes by using a lock while creating the base images. By + :user:`gaborbernat`. (`#1869 `_) -v20.0.24 (2020-06-22) ---------------------- +*********************** + v20.0.24 (2020-06-22) +*********************** Features - 20.0.24 -~~~~~~~~~~~~~~~~~~ +================== + - Ensure that the seeded packages do not get too much out of date: - add a CLI flag that triggers upgrade of embedded wheels under :option:`upgrade-embed-wheels` - - periodically (once every 14 days) upgrade the embedded wheels in a background process, and use them if they have been - released for more than 28 days (can be disabled via :option:`no-periodic-update`) + - periodically (once every 14 days) upgrade the embedded wheels in a background process, and use them if they have + been released for more than 28 days (can be disabled via :option:`no-periodic-update`) More details under :ref:`wheels` - by :user:`gaborbernat`. (`#1821 `_) + - Upgrade embed wheel content: - ship wheels for Python ``3.9`` and ``3.10`` - upgrade setuptools for Python ``3.5+`` from ``47.1.1`` to ``47.3.1`` by :user:`gaborbernat`. (`#1841 `_) + - Display the installed seed package versions in the final summary output, for example: .. code-block:: console @@ -1245,249 +1524,317 @@ Features - 20.0.24 by :user:`gaborbernat`. (`#1864 `_) Bugfixes - 20.0.24 -~~~~~~~~~~~~~~~~~~ -- Do not generate/overwrite ``.gitignore`` if it already exists at destination path - by :user:`gaborbernat`. (`#1862 `_) -- Improve error message for no ``.dist-info`` inside the ``app-data`` copy seeder - by :user:`gaborbernat`. (`#1867 `_) +================== + +- Do not generate/overwrite ``.gitignore`` if it already exists at destination path - by :user:`gaborbernat`. (`#1862 + `_) +- Improve error message for no ``.dist-info`` inside the ``app-data`` copy seeder - by :user:`gaborbernat`. (`#1867 + `_) Improved Documentation - 20.0.24 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- How seeding mechanisms discover (and automatically keep it up to date) wheels at :ref:`wheels` - by :user:`gaborbernat`. (`#1821 `_) -- How distributions should handle shipping their own embedded wheels at :ref:`distribution_wheels` - by :user:`gaborbernat`. (`#1840 `_) +================================ +- How seeding mechanisms discover (and automatically keep it up to date) wheels at :ref:`wheels` - by + :user:`gaborbernat`. (`#1821 `_) +- How distributions should handle shipping their own embedded wheels at :ref:`distribution_wheels` - by + :user:`gaborbernat`. (`#1840 `_) -v20.0.23 (2020-06-12) ---------------------- +*********************** + v20.0.23 (2020-06-12) +*********************** Bugfixes - 20.0.23 -~~~~~~~~~~~~~~~~~~ -- Fix typo in ``setup.cfg`` - by :user:`RowdyHowell`. (`#1857 `_) +================== +- Fix typo in ``setup.cfg`` - by :user:`RowdyHowell`. (`#1857 `_) -v20.0.22 (2020-06-12) ---------------------- +*********************** + v20.0.22 (2020-06-12) +*********************** Bugfixes - 20.0.22 -~~~~~~~~~~~~~~~~~~ -- Relax ``importlib.resources`` requirement to also allow version 2 - by :user:`asottile`. (`#1846 `_) -- Upgrade embedded setuptools to ``44.1.1`` for python 2 and ``47.1.1`` for python3.5+ - by :user:`gaborbernat`. (`#1855 `_) +================== +- Relax ``importlib.resources`` requirement to also allow version 2 - by :user:`asottile`. (`#1846 + `_) +- Upgrade embedded setuptools to ``44.1.1`` for python 2 and ``47.1.1`` for python3.5+ - by :user:`gaborbernat`. (`#1855 + `_) -v20.0.21 (2020-05-20) ---------------------- +*********************** + v20.0.21 (2020-05-20) +*********************** Features - 20.0.21 -~~~~~~~~~~~~~~~~~~ +================== + - Generate ignore file for version control systems to avoid tracking virtual environments by default. Users should - remove these files if still want to track. For now we support only **git** by :user:`gaborbernat`. (`#1806 `_) + remove these files if still want to track. For now we support only **git** by :user:`gaborbernat`. (`#1806 + `_) Bugfixes - 20.0.21 -~~~~~~~~~~~~~~~~~~ +================== + - Fix virtualenv fails sometimes when run concurrently, ``--clear-app-data`` conflicts with :option:`clear` flag when abbreviation is turned on. To bypass this while allowing abbreviated flags on the command line we had to move it to :option:`reset-app-data` - by :user:`gaborbernat`. (`#1824 `_) -- Upgrade embedded ``setuptools`` to ``46.4.0`` from ``46.1.3`` on Python ``3.5+``, and ``pip`` from ``20.1`` to ``20.1.1`` - by :user:`gaborbernat`. (`#1827 `_) -- Seeder pip now correctly handles ``--extra-search-dir`` - by :user:`frenzymadness`. (`#1834 `_) - +- Upgrade embedded ``setuptools`` to ``46.4.0`` from ``46.1.3`` on Python ``3.5+``, and ``pip`` from ``20.1`` to + ``20.1.1`` - by :user:`gaborbernat`. (`#1827 `_) +- Seeder pip now correctly handles ``--extra-search-dir`` - by :user:`frenzymadness`. (`#1834 + `_) -v20.0.20 (2020-05-04) ---------------------- +*********************** + v20.0.20 (2020-05-04) +*********************** Bugfixes - 20.0.20 -~~~~~~~~~~~~~~~~~~ -- Fix download fails with python 3.4 - by :user:`gaborbernat`. (`#1809 `_) -- Fixes older CPython2 versions use ``_get_makefile_filename`` instead of ``get_makefile_filename`` on ``sysconfig`` - by :user:`ianw`. (`#1810 `_) -- Fix download is ``True`` by default - by :user:`gaborbernat`. (`#1813 `_) -- Fail ``app-data`` seed operation when wheel download fails and better error message - by :user:`gaborbernat`. (`#1814 `_) +================== +- Fix download fails with python 3.4 - by :user:`gaborbernat`. (`#1809 + `_) +- Fixes older CPython2 versions use ``_get_makefile_filename`` instead of ``get_makefile_filename`` on ``sysconfig`` - + by :user:`ianw`. (`#1810 `_) +- Fix download is ``True`` by default - by :user:`gaborbernat`. (`#1813 + `_) +- Fail ``app-data`` seed operation when wheel download fails and better error message - by :user:`gaborbernat`. (`#1814 + `_) -v20.0.19 (2020-05-03) ---------------------- +*********************** + v20.0.19 (2020-05-03) +*********************** Bugfixes - 20.0.19 -~~~~~~~~~~~~~~~~~~ -- Fix generating a Python 2 environment from Python 3 creates invalid python activator - by :user:`gaborbernat`. (`#1776 `_) -- Fix pinning seed packages via ``app-data`` seeder raised ``Invalid Requirement`` - by :user:`gaborbernat`. (`#1779 `_) -- Do not stop interpreter discovery if we fail to find the system interpreter for a executable during discovery - - by :user:`gaborbernat`. (`#1781 `_) -- On CPython2 POSIX platforms ensure ``syconfig.get_makefile_filename`` exists within the virtual environment (this is used by some c-extension based libraries - e.g. numpy - for building) - by :user:`gaborbernat`. (`#1783 `_) -- Better handling of options :option:`copies` and :option:`symlinks`. Introduce priority of where the option is set - to follow the order: CLI, env var, file, hardcoded. If both set at same level prefers copy over symlink. - by +================== + +- Fix generating a Python 2 environment from Python 3 creates invalid python activator - by :user:`gaborbernat`. (`#1776 + `_) +- Fix pinning seed packages via ``app-data`` seeder raised ``Invalid Requirement`` - by :user:`gaborbernat`. (`#1779 + `_) +- Do not stop interpreter discovery if we fail to find the system interpreter for a executable during discovery - by + :user:`gaborbernat`. (`#1781 `_) +- On CPython2 POSIX platforms ensure ``syconfig.get_makefile_filename`` exists within the virtual environment (this is + used by some c-extension based libraries - e.g. numpy - for building) - by :user:`gaborbernat`. (`#1783 + `_) +- Better handling of options :option:`copies` and :option:`symlinks`. Introduce priority of where the option is set to + follow the order: CLI, env var, file, hardcoded. If both set at same level prefers copy over symlink. - by :user:`gaborbernat`. (`#1784 `_) -- Upgrade pip for Python ``2.7`` and ``3.5+`` from ``20.0.2`` to ``20.1`` - by :user:`gaborbernat`. (`#1793 `_) -- Fix CPython is not discovered from Windows registry, and discover pythons from Windows registry in decreasing order - by version - by :user:`gaborbernat`. (`#1796 `_) +- Upgrade pip for Python ``2.7`` and ``3.5+`` from ``20.0.2`` to ``20.1`` - by :user:`gaborbernat`. (`#1793 + `_) +- Fix CPython is not discovered from Windows registry, and discover pythons from Windows registry in decreasing order by + version - by :user:`gaborbernat`. (`#1796 `_) - Fix symlink detection for creators - by :user:`asottile` (`#1803 `_) - -v20.0.18 (2020-04-16) ---------------------- +*********************** + v20.0.18 (2020-04-16) +*********************** Bugfixes - 20.0.18 -~~~~~~~~~~~~~~~~~~ +================== + - Importing setuptools before cli_run could cause our python information query to fail due to setuptools patching ``distutils.dist.Distribution`` - by :user:`gaborbernat`. (`#1771 `_) - -v20.0.17 (2020-04-09) ---------------------- +*********************** + v20.0.17 (2020-04-09) +*********************** Features - 20.0.17 -~~~~~~~~~~~~~~~~~~ -- Extend environment variables checked for configuration to also check aliases (e.g. setting either - ``VIRTUALENV_COPIES`` or ``VIRTUALENV_ALWAYS_COPY`` will work) - by :user:`gaborbernat`. (`#1763 `_) +================== +- Extend environment variables checked for configuration to also check aliases (e.g. setting either + ``VIRTUALENV_COPIES`` or ``VIRTUALENV_ALWAYS_COPY`` will work) - by :user:`gaborbernat`. (`#1763 + `_) -v20.0.16 (2020-04-04) ---------------------- +*********************** + v20.0.16 (2020-04-04) +*********************** Bugfixes - 20.0.16 -~~~~~~~~~~~~~~~~~~ -- Allow seed wheel files inside the :option:`extra-search-dir` folders that do not have ``Requires-Python`` - metadata specified, these are considered compatible with all python versions - by :user:`gaborbernat`. (`#1757 `_) +================== +- Allow seed wheel files inside the :option:`extra-search-dir` folders that do not have ``Requires-Python`` metadata + specified, these are considered compatible with all python versions - by :user:`gaborbernat`. (`#1757 + `_) -v20.0.15 (2020-03-27) ---------------------- +*********************** + v20.0.15 (2020-03-27) +*********************** Features - 20.0.15 -~~~~~~~~~~~~~~~~~~ -- Upgrade embedded setuptools to ``46.1.3`` from ``46.1.1`` - by :user:`gaborbernat`. (`#1752 `_) +================== +- Upgrade embedded setuptools to ``46.1.3`` from ``46.1.1`` - by :user:`gaborbernat`. (`#1752 + `_) -v20.0.14 (2020-03-25) ---------------------- +*********************** + v20.0.14 (2020-03-25) +*********************** Features - 20.0.14 -~~~~~~~~~~~~~~~~~~ +================== + - Remove ``__PYVENV_LAUNCHER__`` on macOs for Python ``3.7.(<8)`` and ``3.8.(<3)`` on interpreter startup via ``pth`` - file, this pulls in the `upstream patch `_ - by :user:`gaborbernat`. (`#1704 `_) -- Upgrade embedded setuptools for Python ``3.5+`` to ``46.1.1``, for Python ``2.7`` to ``44.1.0`` - by :user:`gaborbernat`. (`#1745 `_) + file, this pulls in the `upstream patch `_ - by :user:`gaborbernat`. + (`#1704 `_) +- Upgrade embedded setuptools for Python ``3.5+`` to ``46.1.1``, for Python ``2.7`` to ``44.1.0`` - by + :user:`gaborbernat`. (`#1745 `_) Bugfixes - 20.0.14 -~~~~~~~~~~~~~~~~~~ -- Fix discovery of interpreter by name from ``PATH`` that does not match a spec format - by :user:`gaborbernat`. (`#1746 `_) +================== +- Fix discovery of interpreter by name from ``PATH`` that does not match a spec format - by :user:`gaborbernat`. (`#1746 + `_) -v20.0.13 (2020-03-19) ---------------------- +*********************** + v20.0.13 (2020-03-19) +*********************** Bugfixes - 20.0.13 -~~~~~~~~~~~~~~~~~~ -- Do not fail when the pyc files is missing for the host Python 2 - by :user:`gaborbernat`. (`#1738 `_) -- Support broken Packaging pythons that put the include headers under distutils pattern rather than sysconfig one - - by :user:`gaborbernat`. (`#1739 `_) +================== +- Do not fail when the pyc files is missing for the host Python 2 - by :user:`gaborbernat`. (`#1738 + `_) +- Support broken Packaging pythons that put the include headers under distutils pattern rather than sysconfig one - by + :user:`gaborbernat`. (`#1739 `_) -v20.0.12 (2020-03-19) ---------------------- +*********************** + v20.0.12 (2020-03-19) +*********************** Bugfixes - 20.0.12 -~~~~~~~~~~~~~~~~~~ -- Fix relative path discovery of interpreters - by :user:`gaborbernat`. (`#1734 `_) +================== +- Fix relative path discovery of interpreters - by :user:`gaborbernat`. (`#1734 + `_) -v20.0.11 (2020-03-18) ---------------------- +*********************** + v20.0.11 (2020-03-18) +*********************** Features - 20.0.11 -~~~~~~~~~~~~~~~~~~ +================== + - Improve error message when the host python does not satisfy invariants needed to create virtual environments (now we - print which host files are incompatible/missing and for which creators when no supported creator can be matched, however - we found creators that can describe the given Python interpreter - will still print no supported creator for Jython, - however print exactly what host files do not allow creation of virtual environments in case of CPython/PyPy) - - by :user:`gaborbernat`. (`#1716 `_) + print which host files are incompatible/missing and for which creators when no supported creator can be matched, + however we found creators that can describe the given Python interpreter - will still print no supported creator for + Jython, however print exactly what host files do not allow creation of virtual environments in case of CPython/PyPy) - + by :user:`gaborbernat`. (`#1716 `_) Bugfixes - 20.0.11 -~~~~~~~~~~~~~~~~~~ -- Support Python 3 Framework distributed via XCode in macOs Catalina and before - by :user:`gaborbernat`. (`#1663 `_) -- Fix Windows Store Python support, do not allow creation via symlink as that's not going to work by design - - by :user:`gaborbernat`. (`#1709 `_) +================== + +- Support Python 3 Framework distributed via XCode in macOs Catalina and before - by :user:`gaborbernat`. (`#1663 + `_) +- Fix Windows Store Python support, do not allow creation via symlink as that's not going to work by design - by + :user:`gaborbernat`. (`#1709 `_) - Fix ``activate_this.py`` throws ``AttributeError`` on Windows when virtual environment was created via cross python mechanism - by :user:`gaborbernat`. (`#1710 `_) -- Fix ``--no-pip``, ``--no-setuptools``, ``--no-wheel`` not being respected - by :user:`gaborbernat`. (`#1712 `_) -- Allow missing ``.py`` files if a compiled ``.pyc`` version is available - by :user:`tucked`. (`#1714 `_) -- Do not fail if the distutils/setuptools patch happens on a C-extension loader (such as ``zipimporter`` on Python 3.7 or - earlier) - by :user:`gaborbernat`. (`#1715 `_) +- Fix ``--no-pip``, ``--no-setuptools``, ``--no-wheel`` not being respected - by :user:`gaborbernat`. (`#1712 + `_) +- Allow missing ``.py`` files if a compiled ``.pyc`` version is available - by :user:`tucked`. (`#1714 + `_) +- Do not fail if the distutils/setuptools patch happens on a C-extension loader (such as ``zipimporter`` on Python 3.7 + or earlier) - by :user:`gaborbernat`. (`#1715 `_) - Support Python 2 implementations that require the landmark files and ``site.py`` to be in platform standard library - instead of the standard library path of the virtual environment (notably some RHEL ones, such as the Docker - image ``amazonlinux:1``) - by :user:`gaborbernat`. (`#1719 `_) + instead of the standard library path of the virtual environment (notably some RHEL ones, such as the Docker image + ``amazonlinux:1``) - by :user:`gaborbernat`. (`#1719 `_) - Allow the test suite to pass even when called with the system Python - to help repackaging of the tool for Linux - distributions - by :user:`gaborbernat`. (`#1721 `_) -- Also generate ``pipx.y`` console script beside ``pip-x.y`` to be compatible with how pip installs itself - - by :user:`gaborbernat`. (`#1723 `_) -- Automatically create the application data folder if it does not exists - by :user:`gaborbernat`. (`#1728 `_) + distributions - by :user:`gaborbernat`. (`#1721 `_) +- Also generate ``pipx.y`` console script beside ``pip-x.y`` to be compatible with how pip installs itself - by + :user:`gaborbernat`. (`#1723 `_) +- Automatically create the application data folder if it does not exists - by :user:`gaborbernat`. (`#1728 + `_) Improved Documentation - 20.0.11 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- :ref:`supports ` details now explicitly what Python installations we support - - by :user:`gaborbernat`. (`#1714 `_) +================================ +- :ref:`supports ` details now explicitly what Python installations we support - by + :user:`gaborbernat`. (`#1714 `_) -v20.0.10 (2020-03-10) ---------------------- +*********************** + v20.0.10 (2020-03-10) +*********************** Bugfixes - 20.0.10 -~~~~~~~~~~~~~~~~~~ +================== + - Fix acquiring python information might be altered by distutils configuration files generating incorrect layout virtual environments - by :user:`gaborbernat`. (`#1663 `_) -- Upgrade embedded setuptools to ``46.0.0`` from ``45.3.0`` on Python ``3.5+`` - by :user:`gaborbernat`. (`#1702 `_) +- Upgrade embedded setuptools to ``46.0.0`` from ``45.3.0`` on Python ``3.5+`` - by :user:`gaborbernat`. (`#1702 + `_) Improved Documentation - 20.0.10 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +================================ + - Document requirements (pip + index server) when installing via pip under the installation section - by :user:`gaborbernat`. (`#1618 `_) -- Document installing from non PEP-518 systems - :user:`gaborbernat`. (`#1619 `_) -- Document installing latest unreleased version from Github - :user:`gaborbernat`. (`#1620 `_) +- Document installing from non PEP-518 systems - :user:`gaborbernat`. (`#1619 + `_) +- Document installing latest unreleased version from Github - :user:`gaborbernat`. (`#1620 + `_) - -v20.0.9 (2020-03-08) --------------------- +********************** + v20.0.9 (2020-03-08) +********************** Bugfixes - 20.0.9 -~~~~~~~~~~~~~~~~~ -- ``pythonw.exe`` works as ``python.exe`` on Windows - by :user:`gaborbernat`. (`#1686 `_) -- Handle legacy loaders for virtualenv import hooks used to patch distutils configuration load - by :user:`gaborbernat`. (`#1690 `_) +================= + +- ``pythonw.exe`` works as ``python.exe`` on Windows - by :user:`gaborbernat`. (`#1686 + `_) +- Handle legacy loaders for virtualenv import hooks used to patch distutils configuration load - by :user:`gaborbernat`. + (`#1690 `_) - Support for python 2 platforms that store landmark files in ``platstdlib`` over ``stdlib`` (e.g. RHEL) - by :user:`gaborbernat`. (`#1694 `_) -- Upgrade embedded setuptools to ``45.3.0`` from ``45.2.0`` for Python ``3.5+`` - by :user:`gaborbernat`. (`#1699 `_) - +- Upgrade embedded setuptools to ``45.3.0`` from ``45.2.0`` for Python ``3.5+`` - by :user:`gaborbernat`. (`#1699 + `_) -v20.0.8 (2020-03-04) --------------------- +********************** + v20.0.8 (2020-03-04) +********************** Bugfixes - 20.0.8 -~~~~~~~~~~~~~~~~~ -- Having `distutils configuration `_ - files that set ``prefix`` and ``install_scripts`` cause installation of packages in the wrong location - - by :user:`gaborbernat`. (`#1663 `_) -- Fix ``PYTHONPATH`` being overridden on Python 2 — by :user:`jd`. (`#1673 `_) -- Fix list configuration value parsing from config file or environment variable - by :user:`gaborbernat`. (`#1674 `_) -- Fix Batch activation script shell prompt to display environment name by default - by :user:`spetafree`. (`#1679 `_) -- Fix startup on Python 2 is slower for virtualenv - this was due to setuptools calculating it's working set distribution - - by :user:`gaborbernat`. (`#1682 `_) +================= + +- Having `distutils configuration `_ files + that set ``prefix`` and ``install_scripts`` cause installation of packages in the wrong location - by + :user:`gaborbernat`. (`#1663 `_) +- Fix ``PYTHONPATH`` being overridden on Python 2 — by :user:`jd`. (`#1673 + `_) +- Fix list configuration value parsing from config file or environment variable - by :user:`gaborbernat`. (`#1674 + `_) +- Fix Batch activation script shell prompt to display environment name by default - by :user:`spetafree`. (`#1679 + `_) +- Fix startup on Python 2 is slower for virtualenv - this was due to setuptools calculating it's working set + distribution - by :user:`gaborbernat`. (`#1682 `_) - Fix entry points are not populated for editable installs on Python 2 due to setuptools working set being calculated before ``easy_install.pth`` runs - by :user:`gaborbernat`. (`#1684 `_) -- Fix ``attr:`` import fails for setuptools - by :user:`gaborbernat`. (`#1685 `_) +- Fix ``attr:`` import fails for setuptools - by :user:`gaborbernat`. (`#1685 + `_) - -v20.0.7 (2020-02-26) --------------------- +********************** + v20.0.7 (2020-02-26) +********************** Bugfixes - 20.0.7 -~~~~~~~~~~~~~~~~~ +================= + - Disable distutils fixup for python 3 until `pypa/pip #7778 `_ is fixed and released - by :user:`gaborbernat`. (`#1669 `_) - -v20.0.6 (2020-02-26) --------------------- +********************** + v20.0.6 (2020-02-26) +********************** Bugfixes - 20.0.6 -~~~~~~~~~~~~~~~~~ -- Fix global site package always being added with bundled macOs python framework builds - by :user:`gaborbernat`. (`#1561 `_) -- Fix generated scripts use host version info rather than target - by :user:`gaborbernat`. (`#1600 `_) -- Fix circular prefix reference with single elements (accept these as if they were system executables, print a info about - them referencing themselves) - by :user:`gaborbernat`. (`#1632 `_) +================= + +- Fix global site package always being added with bundled macOs python framework builds - by :user:`gaborbernat`. + (`#1561 `_) +- Fix generated scripts use host version info rather than target - by :user:`gaborbernat`. (`#1600 + `_) +- Fix circular prefix reference with single elements (accept these as if they were system executables, print a info + about them referencing themselves) - by :user:`gaborbernat`. (`#1632 + `_) - Handle the case when the application data folder is read-only: - the application data folder is now controllable via :option:`app-data`, @@ -1497,75 +1844,103 @@ Bugfixes - 20.0.6 - :option:`symlink-app-data` is always ``False`` when the application data is temporary by :user:`gaborbernat`. (`#1640 `_) -- Fix PyPy 2 builtin modules are imported from standard library, rather than from builtin - by :user:`gaborbernat`. (`#1652 `_) -- Fix creation of entry points when path contains spaces - by :user:`nsoranzo`. (`#1660 `_) -- Fix relative paths for the zipapp (for python ``3.7+``) - by :user:`gaborbernat`. (`#1666 `_) -v20.0.5 (2020-02-21) --------------------- +- Fix PyPy 2 builtin modules are imported from standard library, rather than from builtin - by :user:`gaborbernat`. + (`#1652 `_) +- Fix creation of entry points when path contains spaces - by :user:`nsoranzo`. (`#1660 + `_) +- Fix relative paths for the zipapp (for python ``3.7+``) - by :user:`gaborbernat`. (`#1666 + `_) + +********************** + v20.0.5 (2020-02-21) +********************** Features - 20.0.5 -~~~~~~~~~~~~~~~~~ -- Also create ``pythonX.X`` executables when creating pypy virtualenvs - by :user:`asottile` (`#1612 `_) -- Fail with better error message if trying to install source with unsupported ``setuptools``, allow ``setuptools-scm >= 2`` - and move to legacy ``setuptools-scm`` format to support better older platforms (``CentOS 7`` and such) - by :user:`gaborbernat`. (`#1621 `_) -- Report of the created virtual environment is now split across four short lines rather than one long - by :user:`gaborbernat` (`#1641 `_) +================= + +- Also create ``pythonX.X`` executables when creating pypy virtualenvs - by :user:`asottile` (`#1612 + `_) +- Fail with better error message if trying to install source with unsupported ``setuptools``, allow ``setuptools-scm >= + 2`` and move to legacy ``setuptools-scm`` format to support better older platforms (``CentOS 7`` and such) - by + :user:`gaborbernat`. (`#1621 `_) +- Report of the created virtual environment is now split across four short lines rather than one long - by + :user:`gaborbernat` (`#1641 `_) Bugfixes - 20.0.5 -~~~~~~~~~~~~~~~~~ -- Add macOs Python 2 Framework support (now we test it with the CI via brew) - by :user:`gaborbernat` (`#1561 `_) -- Fix losing of libpypy-c.so when the pypy executable is a symlink - by :user:`asottile` (`#1614 `_) -- Discover python interpreter in a case insensitive manner - by :user:`PrajwalM2212` (`#1624 `_) -- Fix cross interpreter support when the host python sets ``sys.base_executable`` based on ``__PYVENV_LAUNCHER__`` - - by :user:`cjolowicz` (`#1643 `_) +================= +- Add macOs Python 2 Framework support (now we test it with the CI via brew) - by :user:`gaborbernat` (`#1561 + `_) +- Fix losing of libpypy-c.so when the pypy executable is a symlink - by :user:`asottile` (`#1614 + `_) +- Discover python interpreter in a case insensitive manner - by :user:`PrajwalM2212` (`#1624 + `_) +- Fix cross interpreter support when the host python sets ``sys.base_executable`` based on ``__PYVENV_LAUNCHER__`` - by + :user:`cjolowicz` (`#1643 `_) -v20.0.4 (2020-02-14) --------------------- +********************** + v20.0.4 (2020-02-14) +********************** Features - 20.0.4 -~~~~~~~~~~~~~~~~~ -- When aliasing interpreters, use relative symlinks - by :user:`asottile`. (`#1596 `_) +================= + +- When aliasing interpreters, use relative symlinks - by :user:`asottile`. (`#1596 + `_) Bugfixes - 20.0.4 -~~~~~~~~~~~~~~~~~ -- Allow the use of ``/`` as pathname component separator on Windows - by ``vphilippon`` (`#1582 `_) -- Lower minimal version of six required to 1.9 - by ``ssbarnea`` (`#1606 `_) +================= +- Allow the use of ``/`` as pathname component separator on Windows - by ``vphilippon`` (`#1582 + `_) +- Lower minimal version of six required to 1.9 - by ``ssbarnea`` (`#1606 + `_) -v20.0.3 (2020-02-12) --------------------- +********************** + v20.0.3 (2020-02-12) +********************** Bugfixes - 20.0.3 -~~~~~~~~~~~~~~~~~ +================= + - On Python 2 with Apple Framework builds the global site package is no longer added when the - :option:`system-site-packages` is not specified - by :user:`gaborbernat`. (`#1561 `_) + :option:`system-site-packages` is not specified - by :user:`gaborbernat`. (`#1561 + `_) - Fix system python discovery mechanism when prefixes contain relative parts (e.g. ``..``) by resolving paths within the python information query - by :user:`gaborbernat`. (`#1583 `_) -- Expose a programmatic API as ``from virtualenv import cli_run`` - by :user:`gaborbernat`. (`#1585 `_) +- Expose a programmatic API as ``from virtualenv import cli_run`` - by :user:`gaborbernat`. (`#1585 + `_) - Fix ``app-data`` :option:`seeder` injects a extra ``.dist-info.virtualenv`` path that breaks ``importlib.metadata``, - now we inject an extra ``.virtualenv`` - by :user:`gaborbernat`. (`#1589 `_) + now we inject an extra ``.virtualenv`` - by :user:`gaborbernat`. (`#1589 + `_) Improved Documentation - 20.0.3 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Document a programmatic API as ``from virtualenv import cli_run`` under :ref:`programmatic_api` - - by :user:`gaborbernat`. (`#1585 `_) +=============================== +- Document a programmatic API as ``from virtualenv import cli_run`` under :ref:`programmatic_api` - by + :user:`gaborbernat`. (`#1585 `_) -v20.0.2 (2020-02-11) --------------------- +********************** + v20.0.2 (2020-02-11) +********************** Features - 20.0.2 -~~~~~~~~~~~~~~~~~ +================= + - Print out a one line message about the created virtual environment when no :option:`verbose` is set, this can now be - silenced to get back the original behavior via the :option:`quiet` flag - by :user:`pradyunsg`. (`#1557 `_) -- Allow virtualenv's app data cache to be overridden by ``VIRTUALENV_OVERRIDE_APP_DATA`` - by :user:`asottile`. (`#1559 `_) -- Passing in the virtual environment name/path is now required (no longer defaults to ``venv``) - by :user:`gaborbernat`. (`#1568 `_) + silenced to get back the original behavior via the :option:`quiet` flag - by :user:`pradyunsg`. (`#1557 + `_) +- Allow virtualenv's app data cache to be overridden by ``VIRTUALENV_OVERRIDE_APP_DATA`` - by :user:`asottile`. (`#1559 + `_) +- Passing in the virtual environment name/path is now required (no longer defaults to ``venv``) - by + :user:`gaborbernat`. (`#1568 `_) - Add a CLI flag :option:`with-traceback` that allows displaying the stacktrace of the virtualenv when a failure occurs - by :user:`gaborbernat`. (`#1572 `_) Bugfixes - 20.0.2 -~~~~~~~~~~~~~~~~~ +================= + - Support long path names for generated virtual environment console entry points (such as ``pip``) when using the ``app-data`` :option:`seeder` - by :user:`gaborbernat`. (`#997 `_) - Improve python discovery mechanism: @@ -1574,71 +1949,88 @@ Bugfixes - 20.0.2 - beside the prefix folder also try with the platform dependent binary folder within that, by :user:`gaborbernat`. (`#1545 `_) + - When copying (either files or trees) do not copy the permission bits, last access time, last modification time, and - flags as access to these might be forbidden (for example in case of the macOs Framework Python) and these are not needed - for the user to use the virtual environment - by :user:`gaborbernat`. (`#1561 `_) + flags as access to these might be forbidden (for example in case of the macOs Framework Python) and these are not + needed for the user to use the virtual environment - by :user:`gaborbernat`. (`#1561 + `_) - While discovering a python executables interpreters that cannot be queried are now displayed with info level rather than warning, so now they're no longer shown by default (these can be just executables to which we don't have access - or that are broken, don't warn if it's not the target Python we want) - by :user:`gaborbernat`. (`#1574 `_) + or that are broken, don't warn if it's not the target Python we want) - by :user:`gaborbernat`. (`#1574 + `_) - The ``app-data`` :option:`seeder` no longer symlinks the packages on UNIX and copies on Windows. Instead by default - always copies, however now has the :option:`symlink-app-data` flag allowing users to request this less robust but faster - method - by :user:`gaborbernat`. (`#1575 `_) + always copies, however now has the :option:`symlink-app-data` flag allowing users to request this less robust but + faster method - by :user:`gaborbernat`. (`#1575 `_) Improved Documentation - 20.0.2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Add link to the `legacy documentation `_ for the changelog by :user:`jezdez`. (`#1547 `_) -- Fine tune the documentation layout: default width of theme, allow tables to wrap around, soft corners for code snippets - - by :user:`pradyunsg`. (`#1548 `_) +=============================== +- Add link to the `legacy documentation `_ for the changelog by :user:`jezdez`. + (`#1547 `_) +- Fine tune the documentation layout: default width of theme, allow tables to wrap around, soft corners for code + snippets - by :user:`pradyunsg`. (`#1548 `_) -v20.0.1 (2020-02-10) --------------------- +********************** + v20.0.1 (2020-02-10) +********************** Features - 20.0.1 -~~~~~~~~~~~~~~~~~ -- upgrade embedded setuptools to ``45.2.0`` from ``45.1.0`` for Python ``3.4+`` - by :user:`gaborbernat`. (`#1554 `_) +================= + +- upgrade embedded setuptools to ``45.2.0`` from ``45.1.0`` for Python ``3.4+`` - by :user:`gaborbernat`. (`#1554 + `_) Bugfixes - 20.0.1 -~~~~~~~~~~~~~~~~~ -- Virtual environments created via relative path on Windows creates bad console executables - by :user:`gaborbernat`. (`#1552 `_) -- Seems sometimes venvs created set their base executable to themselves; we accept these without question, so we handle - virtual environments as system pythons causing issues - by :user:`gaborbernat`. (`#1553 `_) +================= +- Virtual environments created via relative path on Windows creates bad console executables - by :user:`gaborbernat`. + (`#1552 `_) +- Seems sometimes venvs created set their base executable to themselves; we accept these without question, so we handle + virtual environments as system pythons causing issues - by :user:`gaborbernat`. (`#1553 + `_) -v20.0.0. (2020-02-10) ---------------------- +*********************** + v20.0.0. (2020-02-10) +*********************** Improved Documentation - 20.0.0. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +================================ + - Fixes typos, repeated words and inconsistent heading spacing. Rephrase parts of the development documentation and CLI - documentation. Expands shorthands like ``env var`` and ``config`` to their full forms. Uses descriptions from respective - documentation, for projects listed in ``related links`` - by :user:`pradyunsg`. (`#1540 `_) + documentation. Expands shorthands like ``env var`` and ``config`` to their full forms. Uses descriptions from + respective documentation, for projects listed in ``related links`` - by :user:`pradyunsg`. (`#1540 + `_) -v20.0.0b2 (2020-02-04) ----------------------- +************************ + v20.0.0b2 (2020-02-04) +************************ Features - 20.0.0b2 -~~~~~~~~~~~~~~~~~~~ +=================== + - Improve base executable discovery mechanism: - print at debug level why we refuse some candidates, - when no candidates match exactly, instead of hard failing fallback to the closest match where the priority of - matching attributes is: python implementation, major version, minor version, architecture, patch version, - release level and serial (this is to facilitate things to still work when the OS upgrade replace/upgrades the system - python with a never version, than what the virtualenv host python was created with), + matching attributes is: python implementation, major version, minor version, architecture, patch version, release + level and serial (this is to facilitate things to still work when the OS upgrade replace/upgrades the system python + with a never version, than what the virtualenv host python was created with), - always resolve system_executable information during the interpreter discovery, and the discovered environment is the - system interpreter instead of the venv/virtualenv (this happened before lazily the first time we accessed, and caused - reporting that the created virtual environment is of type of the virtualenv host python version, instead of the - system pythons version - these two can differ if the OS upgraded the system python underneath and the virtualenv + system interpreter instead of the venv/virtualenv (this happened before lazily the first time we accessed, and + caused reporting that the created virtual environment is of type of the virtualenv host python version, instead of + the system pythons version - these two can differ if the OS upgraded the system python underneath and the virtualenv host was created via copy), by :user:`gaborbernat`. (`#1515 `_) -- Generate ``bash`` and ``fish`` activators on Windows too (as these can be available with git bash, cygwin or mysys2) - - by :user:`gaborbernat`. (`#1527 `_) -- Upgrade the bundled ``wheel`` package from ``0.34.0`` to ``0.34.2`` - by :user:`gaborbernat`. (`#1531 `_) + +- Generate ``bash`` and ``fish`` activators on Windows too (as these can be available with git bash, cygwin or mysys2) - + by :user:`gaborbernat`. (`#1527 `_) +- Upgrade the bundled ``wheel`` package from ``0.34.0`` to ``0.34.2`` - by :user:`gaborbernat`. (`#1531 + `_) Bugfixes - 20.0.0b2 -~~~~~~~~~~~~~~~~~~~ +=================== + - Bash activation script should have no extensions instead of ``.sh`` (this fixes the :pypi:`virtualenvwrapper` integration) - by :user:`gaborbernat`. (`#1508 `_) - Show less information when we run with a single verbosity (``-v``): @@ -1648,32 +2040,42 @@ Bugfixes - 20.0.0b2 - for the ``app-data`` seeder do not show the type of lock, only the path to the app data directory, By :user:`gaborbernat`. (`#1510 `_) + - Fixed cannot discover a python interpreter that has already been discovered under a different path (such is the case - when we have multiple symlinks to the same interpreter) - by :user:`gaborbernat`. (`#1512 `_) -- Support relative paths for ``-p`` - by :user:`gaborbernat`. (`#1514 `_) -- Creating virtual environments in parallel fail with cannot acquire lock within app data - by :user:`gaborbernat`. (`#1516 `_) -- pth files were not processed under Debian CPython2 interpreters - by :user:`gaborbernat`. (`#1517 `_) + when we have multiple symlinks to the same interpreter) - by :user:`gaborbernat`. (`#1512 + `_) +- Support relative paths for ``-p`` - by :user:`gaborbernat`. (`#1514 + `_) +- Creating virtual environments in parallel fail with cannot acquire lock within app data - by :user:`gaborbernat`. + (`#1516 `_) +- pth files were not processed under Debian CPython2 interpreters - by :user:`gaborbernat`. (`#1517 + `_) - Fix prompt not displayed correctly with upcoming fish 3.10 due to us not preserving ``$pipestatus`` - by :user:`krobelus`. (`#1530 `_) - Stable order within ``pyenv.cfg`` and add ``include-system-site-packages`` only for creators that reference a global Python - by user:`gaborbernat`. (`#1535 `_) Improved Documentation - 20.0.0b2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Create the first iteration of the new documentation - by :user:`gaborbernat`. (`#1465 `_) -- Project readme is now of type MarkDown instead of reStructuredText - by :user:`gaborbernat`. (`#1531 `_) +================================= +- Create the first iteration of the new documentation - by :user:`gaborbernat`. (`#1465 + `_) +- Project readme is now of type MarkDown instead of reStructuredText - by :user:`gaborbernat`. (`#1531 + `_) -v20.0.0b1 (2020-01-28) ----------------------- +************************ + v20.0.0b1 (2020-01-28) +************************ -* First public release of the rewrite. Everything is brand new and just added. -* ``--download`` defaults to ``False`` -* No longer replaces builtin ``site`` module with `custom version baked within virtualenv code itself `_. A simple shim module is used to fix up things on Python 2 only. +- First public release of the rewrite. Everything is brand new and just added. +- ``--download`` defaults to ``False`` +- No longer replaces builtin ``site`` module with `custom version baked within virtualenv code itself + `_. A simple shim module is used to fix up + things on Python 2 only. .. warning:: - The current virtualenv is the second iteration of implementation. From version ``0.8`` all the way to ``16.7.9`` - we numbered the first iteration. Version ``20.0.0b1`` is a complete rewrite of the package, and as such this release - history starts from there. The old changelog is still available in the - `legacy branch documentation `_. + The current virtualenv is the second iteration of implementation. From version ``0.8`` all the way to ``16.7.9`` we + numbered the first iteration. Version ``20.0.0b1`` is a complete rewrite of the package, and as such this release + history starts from there. The old changelog is still available in the `legacy branch documentation + `_. diff --git a/docs/changelog/1884.feature.rst b/docs/changelog/1884.feature.rst index 7a512ee94..f5d01a178 100644 --- a/docs/changelog/1884.feature.rst +++ b/docs/changelog/1884.feature.rst @@ -1 +1,5 @@ -Store app data (pip/setuptools/wheel caches) under the OS cache directory (``platformdirs.user_cache_dir``) instead of the data directory (``platformdirs.user_data_dir``). Existing app data at the old location is automatically migrated on first use. This ensures cached files that can be redownloaded are placed in the standard cache location (e.g. ``~/.cache`` on Linux, ``~/Library/Caches`` on macOS) where they are excluded from backups and can be cleaned by system tools - by :user:`rahuldevikar`. (:issue:`1884`) +Store app data (pip/setuptools/wheel caches) under the OS cache directory (``platformdirs.user_cache_dir``) instead of +the data directory (``platformdirs.user_data_dir``). Existing app data at the old location is automatically migrated on +first use. This ensures cached files that can be redownloaded are placed in the standard cache location (e.g. +``~/.cache`` on Linux, ``~/Library/Caches`` on macOS) where they are excluded from backups and can be cleaned by system +tools - by :user:`rahuldevikar`. (:issue:`1884`) diff --git a/docs/changelog/2637.feature.rst b/docs/changelog/2637.feature.rst index f4d0a35ee..79887c0f7 100644 --- a/docs/changelog/2637.feature.rst +++ b/docs/changelog/2637.feature.rst @@ -1 +1,4 @@ -Add ``PKG_CONFIG_PATH`` environment variable support to all activation scripts (Bash, Batch, PowerShell, Fish, C Shell, Nushell, and Python). The virtualenv's ``lib/pkgconfig`` directory is now automatically prepended to ``PKG_CONFIG_PATH`` on activation and restored on deactivation, enabling packages that use ``pkg-config`` during build/install to find their configuration files - by :user:`rahuldevikar`. +Add ``PKG_CONFIG_PATH`` environment variable support to all activation scripts (Bash, Batch, PowerShell, Fish, C Shell, +Nushell, and Python). The virtualenv's ``lib/pkgconfig`` directory is now automatically prepended to ``PKG_CONFIG_PATH`` +on activation and restored on deactivation, enabling packages that use ``pkg-config`` during build/install to find their +configuration files - by :user:`rahuldevikar`. diff --git a/docs/changelog/2662.bugfix.rst b/docs/changelog/2662.bugfix.rst index c9e072cdd..bcefc0d27 100644 --- a/docs/changelog/2662.bugfix.rst +++ b/docs/changelog/2662.bugfix.rst @@ -1,3 +1,3 @@ Exclude pywin32 DLLs (``pywintypes*.dll``, ``pythoncom*.dll``) from being copied to the Scripts directory during -virtualenv creation on Windows. This fixes compatibility issues with pywin32, which expects its DLLs to be installed -in ``site-packages/pywin32_system32`` by its own post-install script - by :user:`rahuldevikar`. +virtualenv creation on Windows. This fixes compatibility issues with pywin32, which expects its DLLs to be installed in +``site-packages/pywin32_system32`` by its own post-install script - by :user:`rahuldevikar`. diff --git a/docs/changelog/2770.bugfix.rst b/docs/changelog/2770.bugfix.rst index b67738777..80c8757c8 100644 --- a/docs/changelog/2770.bugfix.rst +++ b/docs/changelog/2770.bugfix.rst @@ -1 +1,3 @@ -Preserve symlinks in ``pyvenv.cfg`` paths to match ``venv`` behavior. Use ``os.path.abspath()`` instead of ``os.path.realpath()`` to normalize paths without resolving symlinks, fixing issues with Python installations accessed via symlinked directories (common in network-mounted filesystems) - by :user:`rahuldevikar`. Fixes :issue:`2770`. +Preserve symlinks in ``pyvenv.cfg`` paths to match ``venv`` behavior. Use ``os.path.abspath()`` instead of +``os.path.realpath()`` to normalize paths without resolving symlinks, fixing issues with Python installations accessed +via symlinked directories (common in network-mounted filesystems) - by :user:`rahuldevikar`. Fixes :issue:`2770`. diff --git a/docs/changelog/2985.bugfix.rst b/docs/changelog/2985.bugfix.rst index ae2fe2aca..5e157000d 100644 --- a/docs/changelog/2985.bugfix.rst +++ b/docs/changelog/2985.bugfix.rst @@ -1 +1,3 @@ -Fix Windows activation scripts to properly quote ``python.exe`` path, preventing failures when Python is installed in a path with spaces (e.g., ``C:\Program Files``) and a file named ``C:\Program`` exists on the filesystem - by :user:`rahuldevikar`. +Fix Windows activation scripts to properly quote ``python.exe`` path, preventing failures when Python is installed in a +path with spaces (e.g., ``C:\Program Files``) and a file named ``C:\Program`` exists on the filesystem - by +:user:`rahuldevikar`. diff --git a/docs/changelog/3027.feature.rst b/docs/changelog/3027.feature.rst index 9386812bc..4e662e945 100644 --- a/docs/changelog/3027.feature.rst +++ b/docs/changelog/3027.feature.rst @@ -1 +1,2 @@ -Upgrade embedded pip to ``26.0.1`` from ``25.3`` and setuptools to ``82.0.0``, ``75.3.4`` from ``75.3.2``, ``80.9.0`` - by :user:`rahuldevikar`. +Upgrade embedded pip to ``26.0.1`` from ``25.3`` and setuptools to ``82.0.0``, ``75.3.4`` from ``75.3.2``, ``80.9.0`` - +by :user:`rahuldevikar`. diff --git a/docs/changelog/3029.feature.rst b/docs/changelog/3029.feature.rst index cb77ec757..f96af7a58 100644 --- a/docs/changelog/3029.feature.rst +++ b/docs/changelog/3029.feature.rst @@ -1 +1,2 @@ -Replace ``ty: ignore`` comments with proper type narrowing using assertions and explicit None checks - by :user:`rahuldevikar`. +Replace ``ty: ignore`` comments with proper type narrowing using assertions and explicit None checks - by +:user:`rahuldevikar`. diff --git a/docs/changelog/examples.rst b/docs/changelog/examples.rst index eda79eb70..86257006a 100644 --- a/docs/changelog/examples.rst +++ b/docs/changelog/examples.rst @@ -1,15 +1,20 @@ .. examples for changelog entries adding to your Pull Requests -file ``544.doc.rst``:: +file ``544.doc.rst``: + +:: explain everything much better - by :user:`passionate_technicalwriter`. -file ``544.feature.rst``:: +file ``544.feature.rst``: + +:: ``tox --version`` now shows information about all registered plugins - by :user:`obestwalter`. +file ``571.bugfix.rst``: -file ``571.bugfix.rst``:: +:: ``skip_install`` overrides ``usedevelop`` (``usedevelop`` is an option to choose the installation type if the package is installed and ``skip_install`` determines if it should be diff --git a/docs/changelog/template.jinja2 b/docs/changelog/template.jinja2 index bb88fa2c2..9de49f34a 100644 --- a/docs/changelog/template.jinja2 +++ b/docs/changelog/template.jinja2 @@ -1,18 +1,18 @@ -{% set top_underline = underlines[0] %} {% if versiondata.name %} -v{{ versiondata.version }} ({{ versiondata.date }}) -{{ top_underline * ((versiondata.version + versiondata.date)|length + 4)}} +{% set version_title = "v" + versiondata.version + " (" + versiondata.date + ")" %} {% else %} -{{ versiondata.version }} ({{ versiondata.date }}) -{{ top_underline * ((versiondata.version + versiondata.date)|length + 3)}} +{% set version_title = versiondata.version + " (" + versiondata.date + ")" %} {% endif %} +{{ top_underline * (version_title|length + 2) }} + {{ version_title }} +{{ top_underline * (version_title|length + 2) }} {% for section, _ in sections.items() %} -{% set underline = underlines[1] %} {% if sections[section] %} {% for category, val in definitions.items() if category in sections[section]%} {{ definitions[category]['name'] }} - {{ versiondata.version }} -{{ underline * ((definitions[category]['name'] + versiondata.version)|length + 3)}} +{{ underlines[0] * ((definitions[category]['name'] + versiondata.version)|length + 3) }} + {% if definitions[category]['showcontent'] %} {% for text, values in sections[section][category].items() %} - {{ text }} ({{ values|join(', ') }}) diff --git a/docs/cli_interface.rst b/docs/cli_interface.rst deleted file mode 100644 index fd3543b35..000000000 --- a/docs/cli_interface.rst +++ /dev/null @@ -1,138 +0,0 @@ -CLI interface -============= - -.. _cli_flags: - -CLI flags -~~~~~~~~~ - -``virtualenv`` is primarily a command line application. - -It modifies the environment variables in a shell to create an isolated Python environment, so you'll need to have a -shell to run it. You can type in ``virtualenv`` (name of the application) followed by flags that control its -behavior. All options have sensible defaults, and there's one required argument: the name/path of the virtual -environment to create. The default values for the command line options can be overridden via the -:ref:`conf_file` or :ref:`env_vars`. Environment variables takes priority over the configuration file values -(``--help`` will show if a default comes from the environment variable as the help message will end in this case -with environment variables or the configuration file). - -The options that can be passed to virtualenv, along with their default values and a short description are listed below. - -:command:`virtualenv [OPTIONS]` - -.. table_cli:: - :module: virtualenv.run - :func: build_parser_only - -Discovery options -~~~~~~~~~~~~~~~~~ - -Understanding Interpreter Discovery: ``--python`` vs. ``--try-first-with`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can control which Python interpreter ``virtualenv`` selects using the ``--python`` and ``--try-first-with`` flags. -To avoid confusion, it's best to think of them as the "rule" and the "hint". - -**``--python ``: The Rule** - -This flag sets the mandatory requirements for the interpreter. The ```` can be: - -- **A version string** (e.g., ``python3.8``, ``pypy3``). ``virtualenv`` will search for any interpreter that matches this version. -- **A version specifier** using PEP 440 operators (e.g., ``>=3.12``, ``~=3.11.0``, ``python>=3.10``). ``virtualenv`` will search for any interpreter that satisfies the version constraint. You can also specify the implementation: ``cpython>=3.12``. -- **An absolute path** (e.g., ``/usr/bin/python3.8``). This is a *strict* requirement. Only the interpreter at this exact path will be used. If it does not exist or is not a valid interpreter, creation will fail. - -**``--try-first-with ``: The Hint** - -This flag provides a path to a Python executable to check *before* ``virtualenv`` performs its standard search. This can speed up discovery or help select a specific interpreter when multiple versions exist on your system. - -**How They Work Together** - -``virtualenv`` will only use an interpreter from ``--try-first-with`` if it **satisfies the rule** from the ``--python`` flag. The ``--python`` rule always wins. - -**Examples:** - -1. **Hint does not match the rule:** - - .. code-block:: bash - - virtualenv --python python3.8 --try-first-with /usr/bin/python3.10 my-env - - - **Result:** ``virtualenv`` first inspects ``/usr/bin/python3.10``. It sees this does not match the ``python3.8`` rule and **rejects it**. It then proceeds with its normal search to find a ``python3.8`` interpreter elsewhere. - -2. **Hint does not match a strict path rule:** - - .. code-block:: bash - - virtualenv --python /usr/bin/python3.8 --try-first-with /usr/bin/python3.10 my-env - - - **Result:** The rule is strictly ``/usr/bin/python3.8``. ``virtualenv`` checks the ``/usr/bin/python3.10`` hint, sees the path doesn't match, and **rejects it**. It then moves on to test ``/usr/bin/python3.8`` and successfully creates the environment. - -This approach ensures that the behavior is predictable and that ``--python`` remains the definitive source of truth for the user's intent. - - -Defaults -~~~~~~~~ - -.. _conf_file: - -Configuration file -^^^^^^^^^^^^^^^^^^ - -Unless ``VIRTUALENV_CONFIG_FILE`` is set, virtualenv looks for a standard ``virtualenv.ini`` configuration file. -The exact location depends on the operating system you're using, as determined by :pypi:`platformdirs` application -configuration definition. It can be overridden by setting the ``VIRTUALENV_CONFIG_FILE`` environment variable. -The configuration file location is printed as at the end of the output when ``--help`` is passed. - -The keys of the settings are derived from the command line option (left strip the ``-`` characters, and replace ``-`` -with ``_``). Where multiple flags are available first found wins (where order is as it shows up under the ``--help``). - -For example, :option:`--python ` would be specified as: - -.. code-block:: ini - - [virtualenv] - python = /opt/python-3.8/bin/python - -Options that take multiple values, like :option:`extra-search-dir` can be specified as: - -.. code-block:: ini - - [virtualenv] - extra_search_dir = - /path/to/dists - /path/to/other/dists - -.. _env_vars: - -Environment Variables -^^^^^^^^^^^^^^^^^^^^^ - -Default values may be also specified via environment variables. The keys of the settings are derived from the -command line option (left strip the ``-`` characters, and replace ``-`` with ``_``, finally capitalize the name). Where -multiple flags are available first found wins (where order is as it shows up under the ``--help``). - -For example, to use a custom Python binary, instead of the one virtualenv is run with, you can set the environment -variable ``VIRTUALENV_PYTHON`` like: - -.. code-block:: console - - env VIRTUALENV_PYTHON=/opt/python-3.8/bin/python virtualenv - -Where the option accepts multiple values, for example for :option:`python` or -:option:`extra-search-dir`, the values can be separated either by literal -newlines or commas. Newlines and commas can not be mixed and if both are -present only the newline is used for separating values. Examples for multiple -values: - - -.. code-block:: console - - env VIRTUALENV_PYTHON=/opt/python-3.8/bin/python,python3.8 virtualenv - env VIRTUALENV_EXTRA_SEARCH_DIR=/path/to/dists\n/path/to/other/dists virtualenv - -The equivalent CLI-flags based invocation for the above examples would be: - -.. code-block:: console - - virtualenv --python=/opt/python-3.8/bin/python --python=python3.8 - virtualenv --extra-search-dir=/path/to/dists --extra-search-dir=/path/to/other/dists diff --git a/docs/conf.py b/docs/conf.py index f02c8a7cb..d9a7cee4c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,5 @@ from __future__ import annotations -import subprocess import sys from datetime import datetime, timezone from pathlib import Path @@ -17,8 +16,22 @@ "sphinx.ext.autodoc", "sphinx.ext.autosectionlabel", "sphinx.ext.extlinks", + "sphinx.ext.intersphinx", + "sphinx_autodoc_typehints", + "sphinx_copybutton", + "sphinx_inline_tabs", + "sphinxcontrib.mermaid", + "sphinxcontrib.towncrier.ext", ] +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), +} + +towncrier_draft_autoversion_mode = "draft" +towncrier_draft_include_empty = True +towncrier_draft_working_directory = Path(__file__).parent.parent + templates_path = [] unused_docs = [] source_suffix = ".rst" @@ -33,7 +46,16 @@ html_theme = "furo" html_title, html_last_updated_fmt = project, datetime.now(tz=timezone.utc).isoformat() pygments_style, pygments_dark_style = "sphinx", "monokai" -html_static_path, html_css_files = ["_static"], ["custom.css"] +html_static_path = ["_static"] +html_css_files = ["custom.css"] +html_js_files = ["rtd-search.js"] +html_favicon = "_static/virtualenv.svg" +html_theme_options = { + "light_logo": "virtualenv.png", + "dark_logo": "virtualenv.png", + "sidebar_hide_name": True, +} +html_show_sourcelink = False autoclass_content = "both" # Include __init__ in class documentation autodoc_member_order = "bysource" @@ -48,24 +70,16 @@ def setup(app): - here = Path(__file__).parent - root, exe = here.parent, Path(sys.executable) - towncrier = exe.with_name(f"towncrier{exe.suffix}") - cmd = [str(towncrier), "build", "--draft", "--version", "NEXT"] - new = subprocess.check_output(cmd, cwd=root, text=True, stderr=subprocess.DEVNULL, encoding="UTF-8") - (root / "docs" / "_draft.rst").write_text("" if "No significant changes" in new else new, encoding="UTF-8") - - # the CLI arguments are dynamically generated doc_tree = Path(app.doctreedir) - cli_interface_doctree = doc_tree / "cli_interface.doctree" - if cli_interface_doctree.exists(): - cli_interface_doctree.unlink() + for name in ("cli_interface", "reference/cli"): + doctree = doc_tree / f"{name}.doctree" + if doctree.exists(): + doctree.unlink() here = Path(__file__).parent if str(here) not in sys.path: sys.path.append(str(here)) - # noinspection PyUnresolvedReferences from render_cli import CliTable, literal_data # noqa: PLC0415 app.add_css_file("custom.css") diff --git a/docs/development.rst b/docs/development.rst index 5d110f2ff..121a50743 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -1,38 +1,38 @@ -Development -=========== - -Getting started ---------------- +############# + Development +############# +***************** + Getting started +***************** ``virtualenv`` 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 virtualenv. This document is meant to get you setup to work on virtualenv and to act as a guide and reference -to the development setup. If you face any issues during this process, please -`open an issue `_ about it on -the issue tracker. +to the development setup. If you face any issues during this process, please `open an issue +`_ about it on the issue +tracker. Setup -~~~~~ +===== virtualenv is a command line application written in Python. To work on it, you'll need: - **Source code**: available on `GitHub `_. You can use ``git`` to clone the - repository: + repository: .. code-block:: console git clone https://github.com/pypa/virtualenv cd virtualenv -- **Python interpreter**: We recommend using ``CPython``. You can use - `this guide `_ to set it up. - +- **Python interpreter**: We recommend using ``CPython``. You can use `this guide + `_ to set it up. - :pypi:`tox`: to automatically get the projects development dependencies and run the test suite. We recommend installing it using `pipx `_. Running from source tree -~~~~~~~~~~~~~~~~~~~~~~~~ +======================== The easiest way to do this is to generate the development tox environment, and then invoke virtualenv from under the ``.tox/dev`` folder @@ -44,10 +44,10 @@ The easiest way to do this is to generate the development tox environment, and t .tox/dev/Scripts/virtualenv # on Windows Running tests -~~~~~~~~~~~~~ +============= -virtualenv's tests are written using the :pypi:`pytest` test framework. :pypi:`tox` is used to automate the setup -and execution of virtualenv's tests. +virtualenv's tests are written using the :pypi:`pytest` test framework. :pypi:`tox` is used to automate the setup and +execution of virtualenv's tests. To run tests locally execute: @@ -58,10 +58,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:: console @@ -71,11 +70,11 @@ 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). These tests will automatically be skipped if these are not present, 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). These tests will automatically be skipped if these are not present, 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 -~~~~~~~~~~~~~~~ +=============== virtualenv uses :pypi:`pre-commit` for managing linting of the codebase. ``pre-commit`` performs various checks on all files in virtualenv and uses tools that help follow a consistent code style within the codebase. To use linters locally, @@ -91,7 +90,7 @@ run: ``# noqa`` comments are reserved for rare cases where the recommended style causes severe readability problems. Building documentation -~~~~~~~~~~~~~~~~~~~~~~ +====================== virtualenv's documentation is built using :pypi:`Sphinx`. The documentation is written in reStructuredText. To build it locally, run: @@ -104,32 +103,31 @@ The built documentation can be found in the ``.tox/docs_out`` folder and may be that folder. Release -~~~~~~~ +======= -virtualenv's release schedule is tied to ``pip`` and ``setuptools``. We bundle the latest version of these -libraries so each time there's a new version of any of these, there will be a new virtualenv release shortly afterwards -(we usually wait just a few days to avoid pulling in any broken releases). +virtualenv's release schedule is tied to ``pip`` and ``setuptools``. We bundle the latest version of these libraries so +each time there's a new version of any of these, there will be a new virtualenv release shortly afterwards (we usually +wait just a few days to avoid pulling in any broken releases). Performing a release -^^^^^^^^^^^^^^^^^^^^ +-------------------- -A full release publishes to `PyPI `_, creates a -`GitHub Release `_ with the zipapp attached, and updates -`get-virtualenv `_ so that ``https://bootstrap.pypa.io/virtualenv.pyz`` serves -the new version. +A full release publishes to `PyPI `_, creates a `GitHub Release +`_ with the zipapp attached, and updates `get-virtualenv +`_ so that ``https://bootstrap.pypa.io/virtualenv.pyz`` serves the new version. Version bumping -""""""""""""""" +^^^^^^^^^^^^^^^ The ``--version`` argument to ``tox r -e release`` controls the version. It defaults to ``auto``, which inspects the -``docs/changelog`` directory: if any ``*.feature.rst`` or ``*.removal.rst`` fragments exist, the minor version is bumped, -otherwise the patch version is bumped. You can also pass ``major``, ``minor``, or ``patch`` explicitly. +``docs/changelog`` directory: if any ``*.feature.rst`` or ``*.removal.rst`` fragments exist, the minor version is +bumped, otherwise the patch version is bumped. You can also pass ``major``, ``minor``, or ``patch`` explicitly. **Via GitHub Actions (recommended)** -#. Go to the `Release workflow `_ on GitHub. -#. Click **Run workflow** and select the bump type (``auto``, ``major``, ``minor``, or ``patch``). -#. The workflow runs in two phases: +1. Go to the `Release workflow `_ on GitHub. +2. Click **Run workflow** and select the bump type (``auto``, ``major``, ``minor``, or ``patch``). +3. The workflow runs in two phases: **Build** (nothing is published yet): @@ -149,7 +147,7 @@ otherwise the patch version is bumped. You can also pass ``major``, ``minor``, o **Locally** -#. Generate the changelog, create the release commit, tag, and push: +1. Generate the changelog, create the release commit, tag, and push: .. code-block:: console @@ -157,19 +155,19 @@ otherwise the patch version is bumped. You can also pass ``major``, ``minor``, o Pass ``--version `` to override the default ``auto`` behavior (e.g. ``--version minor``). -#. Build the zipapp: +2. Build the zipapp: .. code-block:: console tox r -e zipapp -#. Create a GitHub Release and attach the zipapp: +3. Create a GitHub Release and attach the zipapp: .. code-block:: console gh release create virtualenv.pyz --generate-notes -#. Update ``get-virtualenv`` with the new zipapp: +4. Update ``get-virtualenv`` with the new zipapp: .. code-block:: console @@ -180,21 +178,22 @@ otherwise the patch version is bumped. You can also pass ``major``, ``minor``, o git -C /tmp/get-virtualenv commit -m "update virtualenv to " git -C /tmp/get-virtualenv push origin main - The push triggers ``get-virtualenv``'s own - `release workflow `_ which - automatically creates a tag and GitHub Release with the zipapp attached. + The push triggers ``get-virtualenv``'s own `release workflow + `_ which automatically creates a tag + and GitHub Release with the zipapp attached. -Contributing -------------- +************** + Contributing +************** Submitting pull requests -~~~~~~~~~~~~~~~~~~~~~~~~ +======================== Submit pull requests 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 virtualenv and it must be available under the MIT -License. Provide tests that cover your changes and run the tests locally first. virtualenv -:ref:`supports ` multiple Python versions and operating systems. Any pull request must -consider and work on all these platforms. +License. Provide tests that cover your changes and run the tests locally first. virtualenv :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 @@ -207,24 +206,23 @@ PR more difficult. Examples include re-flowing text in comments or documentation or whitespace within lines. Such changes can be made separately, as a "formatting cleanup" PR, if needed. Automated testing -~~~~~~~~~~~~~~~~~ +================= -All pull requests and merges to 'main' branch are tested using -`GitHub Actions `_ (configured by -``.github/workflows/check.yaml`` file at the root of the repository). You can find the status and results to the CI runs for your -PR on GitHub's Web UI for the pull request. You can also find links to the CI services' pages for the specific builds in -the form of "Details" links, in case the CI run fails and you wish to view the output. +All pull requests and merges to 'main' branch are tested using `GitHub Actions `_ +(configured by ``.github/workflows/check.yaml`` file at the root of the repository). You can find the status and results +to the CI runs for your PR on GitHub's Web UI for the pull request. You can also find links to the CI services' pages +for the specific builds in the form of "Details" links, in case the CI run fails and you wish to view the output. To trigger CI to run again for a pull request, you can close and open the pull request or submit another change to the pull request. If needed, project maintainers can manually trigger a restart of a job/build. NEWS entries -~~~~~~~~~~~~ +============ The ``changelog.rst`` file is managed using :pypi:`towncrier` and all non trivial changes must be accompanied by a news -entry. To add an entry to the news file, 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). +entry. To add an entry to the news file, 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). Once you have an issue or pull request, you take the number and you create a file inside of the ``docs/changelog`` directory named after that issue number with an extension of: @@ -242,29 +240,28 @@ added a feature and deprecated/removed the old feature at the same time, you wou you may create a file for each of them with the same contents and :pypi:`towncrier` will deduplicate them. Contents of a NEWS entry -^^^^^^^^^^^^^^^^^^^^^^^^ +------------------------ The contents of this file are reStructuredText formatted text that will be used as the content of the news file 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 news file. +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 news file. In order to maintain a consistent style in the ``changelog.rst`` file, it is preferred to keep the news entry 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 news 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 news entry is meant for end users and should only contain details relevant to an end user. Choosing the type of NEWS entry -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +------------------------------- A trivial change is anything that does not warrant an entry in the news file. Some examples are: code refactors that -don't change anything as far as the public is concerned, typo fixes, white space modification, etc. To mark a PR -as trivial a contributor simply needs to add a randomly named, empty file to the ``news/`` directory with the extension -of ``.trivial``. +don't change anything as far as the public is concerned, typo fixes, white space modification, etc. To mark a PR as +trivial a contributor simply needs to add a randomly named, empty file to the ``news/`` directory with the extension of +``.trivial``. 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 virtualenv's issue tracker. virtualenv maintainers provide triage abilities to contributors once they have been around @@ -274,8 +271,8 @@ initiate a vote among the existing maintainers. .. note:: - Upon becoming a maintainer, a person should be given access to various virtualenv-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 virtualenv-related tooling across multiple + platforms. These are noted here for future reference by the maintainers: - GitHub Push Access - PyPI Publishing Access diff --git a/docs/explanation.rst b/docs/explanation.rst new file mode 100644 index 000000000..f81c39884 --- /dev/null +++ b/docs/explanation.rst @@ -0,0 +1,483 @@ +############# + Explanation +############# + +This page explains the design decisions and concepts behind virtualenv. It focuses on understanding why things work the +way they do. + +************************** + virtualenv vs venv vs uv +************************** + +Since Python 3.3, the standard library includes the ``venv`` module, which provides basic virtual environment creation +following `PEP 405 `_. `uv `_ is +a newer, Rust-based tool that also creates virtual environments via ``uv venv``. + +virtualenv occupies a middle ground: faster and more featureful than ``venv``, while remaining a pure Python solution +with a plugin system for extensibility. + +.. list-table:: + :header-rows: 1 + :widths: 20 27 27 26 + + - - + - ``venv`` + - ``virtualenv`` + - `uv `_ + - - Performance + - Slowest (60s+); spawns `pip `_ as a subprocess to seed. + - Fast; caches pre-built install images, subsequent creation < 1 second. + - Fastest; Rust implementation, milliseconds. Does not seed pip/setuptools by default. + - - Extensibility + - No plugin system. + - Plugin system for discovery, creation, seeding, and activation. + - No plugin system. + - - Cross-version + - Only the Python version it runs under. + - Any installed Python via auto-discovery (registry, uv-managed, PATH). + - Any installed or uv-managed Python. + - - Upgradeability + - Tied to Python releases. + - Independent via `PyPI `_. + - Independent via its own release cycle. + - - Programmatic API + - Basic ``create()`` function only. + - Full Python API; can describe environments without creating them. Used by `tox `_, + `poetry `_, `pipx `_, etc. + - Command line only. + - - Best for + - Zero dependencies, basic needs. + - Plugin extensibility, programmatic API, tool compatibility (`tox `_, + `virtualenvwrapper `_). + - Maximum speed, already using ``uv`` for package management. + +.. mermaid:: + + flowchart TD + A{Need plugins or programmatic API?} -->|Yes| V[virtualenv] + A -->|No| B{Already using uv?} + B -->|Yes| U[uv venv] + B -->|No| C{Can install external tools?} + C -->|Yes| D{Speed matters?} + C -->|No| VENV[venv] + D -->|Yes| U + D -->|No| V + + style A fill:#d97706,stroke:#b45309,color:#fff + style B fill:#d97706,stroke:#b45309,color:#fff + style C fill:#d97706,stroke:#b45309,color:#fff + style D fill:#d97706,stroke:#b45309,color:#fff + style V fill:#16a34a,stroke:#15803d,color:#fff + style U fill:#2563eb,stroke:#1d4ed8,color:#fff + style VENV fill:#7c3aed,stroke:#6d28d9,color:#fff + +********************** + How virtualenv works +********************** + +Python packaging often faces a fundamental problem: different applications require different versions of the same +library. If Application A needs ``requests==2.25.1`` but Application B needs ``requests==2.28.0``, installing both into +the global site-packages directory creates a conflict. Only one version can exist in a given location. + +virtualenv solves this by creating isolated Python environments. Each environment has its own installation directories +and can maintain its own set of installed packages, independent of other environments and the system Python. + +virtualenv operates in two distinct phases: + +.. mermaid:: + + flowchart TD + Start([virtualenv command]) --> Phase1[Phase 1: Python Discovery] + Phase1 --> Discover{Find Python interpreter} + Discover -->|Default| SameVersion[Use virtualenv's own Python] + Discover -->|--python flag| CustomVersion[Use specified Python] + CustomVersion --> Phase2[Phase 2: Environment Creation] + SameVersion --> Phase2 + Phase2 --> CreatePython[Create Python matching target interpreter] + CreatePython --> SeedPackages[Install seed packages: pip, setuptools, wheel] + SeedPackages --> ActivationScripts[Install activation scripts] + ActivationScripts --> VCSIgnore[Create VCS ignore files] + VCSIgnore --> Complete([Virtual environment ready]) + + style Start fill:#2563eb,stroke:#1d4ed8,color:#fff + style Phase1 fill:#6366f1,stroke:#4f46e5,color:#fff + style Phase2 fill:#6366f1,stroke:#4f46e5,color:#fff + style Complete fill:#16a34a,stroke:#15803d,color:#fff + style Discover fill:#d97706,stroke:#b45309,color:#fff + +**Phase 1: Discover a Python interpreter** + virtualenv first identifies which Python interpreter to use as the template for the virtual environment. By default, + it uses the same Python version that virtualenv itself is running on. You can override this with the ``--python`` + flag to specify a different interpreter. + +**Phase 2: Create the virtual environment** + Once the target interpreter is identified, virtualenv creates the environment in four steps: + + 1. Create a Python executable matching the target interpreter + 2. Install seed packages (pip, setuptools, wheel) to enable package installation + 3. Install activation scripts for various shells + 4. Create VCS ignore files (currently Git's ``.gitignore``, skip with ``--no-vcs-ignore``) + +An important design principle: virtual environments are not self-contained. A complete Python installation consists of +thousands of files, and copying all of them into every virtual environment would be wasteful. Instead, virtual +environments are lightweight shells that borrow most content from the system Python. They contain only what's needed to +redirect Python's behavior. + +This design has two implications: + +- Environment creation is fast because only a small number of files need to be created. +- Upgrading the system Python might affect existing virtual environments, since they reference the system Python's + standard library and binary extensions. + +The Python executable in a virtual environment is effectively isolated from the one used to create it, but the +supporting files are shared. + +.. warning:: + + If you upgrade your system Python, existing virtual environments will still report the old version (the version + number is embedded in the Python executable itself), but they will use the new version's standard library and binary + extensions. This normally works without issues, but be aware that the environment is effectively running a hybrid of + old and new Python versions. + +****************** + Python discovery +****************** + +Before creating a virtual environment, virtualenv must locate a Python interpreter. The interpreter determines the +virtual environment's Python version, implementation (CPython, PyPy, etc.), and architecture (32-bit or 64-bit). + +The ``--python`` flag accepts several specifier formats: + +**Path specifier** + An absolute or relative path to a Python executable, such as ``/usr/bin/python3.8`` or ``./python``. + +**Version specifier** + A string following the format ``{implementation}{version}{architecture}`` where: + + - Implementation is alphabetic characters (``python`` means any implementation; if omitted, defaults to ``python``). + - Version is dot-separated numbers, optionally followed by ``t`` for free-threading builds. + - Architecture is ``-64`` or ``-32`` (if omitted, means any architecture). + + Examples: + + - ``python3.8.1`` - Any Python implementation with version 3.8.1 + - ``3`` - Any Python implementation with major version 3 + - ``3.13t`` - Any Python implementation version 3.13 with free-threading enabled + - ``cpython3`` - CPython implementation with major version 3 + - ``pypy2`` - PyPy implementation with major version 2 + +**PEP 440 version specifier** + Version constraints using PEP 440 operators: + + - ``>=3.12`` - Any Python 3.12 or later + - ``~=3.11.0`` - Compatible with Python 3.11.0 + - ``cpython>=3.10`` - CPython 3.10 or later + +When you provide a specifier, virtualenv searches for matching interpreters using this strategy: + +.. mermaid:: + + flowchart TD + Start([Python specifier provided]) --> Windows{Running on Windows?} + Windows -->|Yes| Registry[Check Windows Registry per PEP-514] + Windows -->|No| UVManaged + Registry --> RegistryMatch{Match found?} + RegistryMatch -->|Yes| Found([Use matched Python]) + RegistryMatch -->|No| UVManaged[Check uv-managed Python installations] + UVManaged --> UVMatch{Match found?} + UVMatch -->|Yes| Found + UVMatch -->|No| PATH[Search PATH for matching executable] + PATH --> PATHMatch{Match found?} + PATHMatch -->|Yes| Found + PATHMatch -->|No| NotFound([Discovery fails]) + + style Start fill:#2563eb,stroke:#1d4ed8,color:#fff + style Found fill:#16a34a,stroke:#15803d,color:#fff + style NotFound fill:#dc2626,stroke:#b91c1c,color:#fff + style Windows fill:#d97706,stroke:#b45309,color:#fff + style RegistryMatch fill:#d97706,stroke:#b45309,color:#fff + style UVMatch fill:#d97706,stroke:#b45309,color:#fff + style PATHMatch fill:#d97706,stroke:#b45309,color:#fff + +1. **Windows Registry** (Windows only): Check registered Python installations per `PEP 514 + `_. +2. **uv-managed installations**: Check the ``UV_PYTHON_INSTALL_DIR`` environment variable or platform-specific uv Python + directories for managed Python installations. +3. **PATH search**: Search for executables on the ``PATH`` environment variable with names matching the specification. + +.. warning:: + + Virtual environments typically reference the system Python's standard library. If you upgrade the system Python, the + virtual environment will report the old version (embedded in its Python executable) but will actually use the new + version's standard library content. This can cause confusion when debugging version-specific behavior. + + If you use a virtual environment's Python as the target for creating another virtual environment, virtualenv will + detect the system Python version and create an environment matching the actual (upgraded) version, not the version + reported by the virtual environment. + +********** + Creators +********** + +Creators are responsible for constructing the virtual environment structure. virtualenv supports two types of creators: + +**venv creator** + This creator delegates the entire creation process to the standard library's ``venv`` module, following `PEP 405 + `_. The venv creator has two limitations: + + - It only works with Python 3.5 or later. + - It requires spawning a subprocess to invoke the venv module, unless virtualenv is installed in the system Python. + + The subprocess overhead can be significant, especially on Windows where process creation is expensive. + +**builtin creator** + This creator means virtualenv performs the creation itself by knowing exactly which files to create and which system + files to reference. The builtin creator is actually a family of specialized creators for different combinations of + Python implementation (CPython, PyPy) and platform (Windows, POSIX). The name ``builtin`` is an alias that selects + the first available builtin creator for the target environment. + + Because builtin creators don't require subprocess invocation, they're generally faster than the venv creator. + +.. mermaid:: + + flowchart TD + Start([Select creator]) --> Builtin{Builtin creator available?} + Builtin -->|Yes| UseBuiltin([Use builtin creator - faster, no subprocess]) + Builtin -->|No| UseVenv([Use venv creator - delegates to stdlib]) + + style Start fill:#2563eb,stroke:#1d4ed8,color:#fff + style UseBuiltin fill:#16a34a,stroke:#15803d,color:#fff + style UseVenv fill:#7c3aed,stroke:#6d28d9,color:#fff + style Builtin fill:#d97706,stroke:#b45309,color:#fff + +virtualenv defaults to using the builtin creator if one is available for the target environment, falling back to the +venv creator otherwise. + +********* + Seeders +********* + +After creating the virtual environment structure, virtualenv installs seed packages that enable package management +within the environment. The seed packages are: + +- ``pip`` - The package installer for Python (always installed). +- ``setuptools`` - Package development and installation library (disabled by default on Python 3.12+). +- ``wheel`` - Support for the wheel binary package format (only installed by default on Python 3.8). + +virtualenv supports two seeding methods with dramatically different performance characteristics: + +**pip seeder** + This method uses the bundled pip wheel to install seed packages by spawning a child pip process. The subprocess + performs a full installation, including unpacking wheels and generating metadata. This method is reliable but slow, + typically consuming 98% of the total virtual environment creation time. + +**app-data seeder** + This method creates reusable install images in a user application data directory. The first time you create an + environment with specific seed package versions, the app-data seeder builds complete install images and stores them + in the cache. Subsequent environment creations simply link or copy these pre-built images into the virtual + environment's ``site-packages`` directory. + + Performance comparison for creating virtual environments: + + .. mermaid:: + + xychart-beta horizontal + title "Seeding time (seconds, lower is better)" + x-axis ["pip seeder (70s)", "app-data copy Win (8s)", "app-data symlink Win (0.8s)", "app-data symlink Linux/macOS (0.1s)"] + y-axis "Seconds" 0 --> 80 + bar [70, 8, 0.8, 0.1] + + On platforms that support symlinks efficiently (Linux, macOS), the app-data seeder provides nearly instant seeding. + + You can override the cache location using the ``VIRTUALENV_OVERRIDE_APP_DATA`` environment variable. + +.. _wheels: + +Wheel acquisition +================= + +Both seeding methods require wheel files for the seed packages. virtualenv acquires wheels using a priority system: + +.. mermaid:: + + flowchart TD + Start([Need wheel file]) --> Embedded{Found in embedded wheels?} + Embedded -->|Yes| UseEmbedded([Use embedded wheel]) + Embedded -->|No| Upgraded{Found in upgraded wheels?} + Upgraded -->|Yes| UseUpgraded([Use upgraded wheel]) + Upgraded -->|No| Extra{Found in extra-search-dir?} + Extra -->|Yes| UseExtra([Use extra wheel]) + Extra -->|No| Download{Download enabled?} + Download -->|Yes| DownloadPyPI([Download from PyPI]) + Download -->|No| Fail([Seeding fails]) + + style Start fill:#2563eb,stroke:#1d4ed8,color:#fff + style UseEmbedded fill:#16a34a,stroke:#15803d,color:#fff + style UseUpgraded fill:#16a34a,stroke:#15803d,color:#fff + style UseExtra fill:#16a34a,stroke:#15803d,color:#fff + style DownloadPyPI fill:#16a34a,stroke:#15803d,color:#fff + style Fail fill:#dc2626,stroke:#b91c1c,color:#fff + style Embedded fill:#d97706,stroke:#b45309,color:#fff + style Upgraded fill:#d97706,stroke:#b45309,color:#fff + style Extra fill:#d97706,stroke:#b45309,color:#fff + style Download fill:#d97706,stroke:#b45309,color:#fff + +**Embedded wheels** + virtualenv ships with a set of wheels bundled directly into the package. These are tested with the virtualenv + release and provide a baseline set of seed packages. Different Python versions require different package versions, + so virtualenv bundles multiple wheels to support its wide Python version range. + +**Upgraded embedded wheels** + Users can manually upgrade the embedded wheels by running virtualenv with the ``--upgrade-embed-wheels`` flag. This + fetches newer versions of seed packages from PyPI and stores them in the user application data directory. Subsequent + virtualenv invocations will use these upgraded wheels instead of the embedded ones. + + virtualenv can also perform periodic automatic upgrades (see below). + +**Extra search directories** + Users can specify additional directories containing wheels using the ``--extra-search-dir`` flag. This is useful in + air-gapped environments or when using custom package builds. + +**PyPI download** + If no suitable wheel is found in the above locations, or if the ``--download`` flag is set, virtualenv will use pip + to download the latest compatible version from PyPI. + +Periodic update mechanism +========================= + +To keep the seed packages reasonably current without requiring users to manually upgrade virtualenv or run +``--upgrade-embed-wheels``, virtualenv implements a periodic automatic update system: + +.. mermaid:: + + timeline + title Periodic update safety gates + section PyPI release + Package published : New wheel available on PyPI + section 28-day wait + Day 1-28 : Wheel is too new, ignored by virtualenv + section Check interval + Every 14 days : virtualenv checks for eligible wheels + section 1-hour hold + After download : Wheel downloaded but not yet used + +1 hour : Wheel becomes active for new environments + +The 28-day waiting period protects users from automatically adopting newly released packages that might contain bugs. +The 1-hour delay after download ensures continuous integration systems don't start using different package versions +mid-run, which could cause confusing test failures. + +You can disable the periodic update mechanism with the ``--no-periodic-update`` flag. + +.. _distribution_wheels: + +Distribution maintainer patching +================================ + +Operating system distributions and package managers sometimes need to customize which seed package versions virtualenv +uses. They want to align virtualenv's bundled packages with system package versions. + +Distributions can patch the ``virtualenv.seed.wheels.embed`` module, replacing the ``get_embed_wheel`` function with +their own implementation that returns distribution-provided wheels. If they want to use virtualenv's test suite for +validation, they should also provide the ``BUNDLE_FOLDER``, ``BUNDLE_SUPPORT``, and ``MAX`` variables. + +Distributions should also consider patching ``virtualenv.seed.embed.base_embed.PERIODIC_UPDATE_ON_BY_DEFAULT`` to +``False``, allowing the system package manager to control seed package updates rather than virtualenv's periodic update +mechanism. Users can still manually request upgrades via ``--upgrade-embed-wheels``, but automatic updates won't +interfere with system-managed packages. + +************ + Activators +************ + +Activation scripts modify the current shell environment to prioritize the virtual environment's executables. This is +purely a convenience mechanism - you can always use absolute paths to virtual environment executables without +activating. + +What activation does: + +.. mermaid:: + + flowchart TD + Before([Before activation]) --> ModifyPATH[Prepend venv/bin to PATH] + ModifyPATH --> SetVENV[Set VIRTUAL_ENV variable] + SetVENV --> SetPROMPT[Set VIRTUAL_ENV_PROMPT variable] + SetPROMPT --> SetPKG[Set PKG_CONFIG_PATH] + SetPKG --> ModifyPrompt[Modify shell prompt] + ModifyPrompt --> After([After activation]) + + style Before fill:#2563eb,stroke:#1d4ed8,color:#fff + style After fill:#16a34a,stroke:#15803d,color:#fff + style ModifyPATH fill:#6366f1,stroke:#4f46e5,color:#fff + style SetVENV fill:#6366f1,stroke:#4f46e5,color:#fff + style SetPROMPT fill:#6366f1,stroke:#4f46e5,color:#fff + style SetPKG fill:#6366f1,stroke:#4f46e5,color:#fff + style ModifyPrompt fill:#6366f1,stroke:#4f46e5,color:#fff + +**PATH modification** + The activation script prepends the virtual environment's ``bin`` directory (``Scripts`` on Windows) to the ``PATH`` + environment variable. This ensures that when you run ``python``, ``pip``, or other executables, the shell finds the + virtual environment's versions first. + +**Environment variables** + Activation sets several environment variables: + + - ``VIRTUAL_ENV`` - Absolute path to the virtual environment directory. + - ``VIRTUAL_ENV_PROMPT`` - The prompt prefix (the environment name or custom value from ``--prompt``). + - ``PKG_CONFIG_PATH`` - Modified to include the virtual environment's ``lib/pkgconfig`` directory. + +**Prompt modification** + By default, activation prepends the environment name to your shell prompt, typically shown as ``(venv)`` before the + regular prompt. This visual indicator helps you remember which environment is active. You can customize this with + the ``--prompt`` flag when creating the environment, or disable it entirely by setting the + ``VIRTUAL_ENV_DISABLE_PROMPT`` environment variable. + +**Deactivation** + Activation scripts also provide a ``deactivate`` command that reverses the changes, restoring your original PATH and + removing the environment variables and prompt modifications. + +virtualenv provides activation scripts for multiple shells: + +- `Bash `_ (``activate``) +- `Fish `_ (``activate.fish``) +- `Csh/Tcsh `_ (``activate.csh``) +- `PowerShell `_ (``activate.ps1``) +- `Windows Batch `_ + (``activate.bat``) +- `Nushell `_ (``activate.nu``) +- Python (``activate_this.py``) -- for programmatic activation from within a running Python process, see + :ref:`how-to/usage:Programmatic activation` + +.. note:: + + On Windows 7 and later, PowerShell's default execution policy is ``Restricted``, which prevents running the + ``activate.ps1`` script. You can allow locally-generated scripts to run by changing the execution policy: + + .. code-block:: powershell + + Set-ExecutionPolicy RemoteSigned + + Since virtualenv generates ``activate.ps1`` locally for each environment, PowerShell considers it a local script + rather than a remote one and allows execution under the ``RemoteSigned`` policy. + +Remember: activation is optional. The following commands are equivalent: + +.. code-block:: console + + # With activation + source venv/bin/activate + python script.py + deactivate + + # Without activation + venv/bin/python script.py + +For a deeper dive into how activation works under the hood, see Allison Kaptur's blog post `There's no magic: virtualenv +edition `_, which explains how virtualenv uses +``PATH`` and ``PYTHONHOME`` to isolate virtual environments. + +********** + See also +********** + +- :doc:`how-to/usage` - Practical guides for common virtualenv tasks. +- :doc:`reference/cli` - Complete CLI reference documentation. diff --git a/docs/extend.rst b/docs/extend.rst deleted file mode 100644 index 6dc107e3b..000000000 --- a/docs/extend.rst +++ /dev/null @@ -1,88 +0,0 @@ -Extend functionality -==================== - -``virtualenv`` allows one to extend the builtin functionality via a plugin system. To add a plugin you need to: - -- write a python file containing the plugin code which follows our expected interface, -- package it as a python library, -- install it alongside the virtual environment. - -Python discovery ----------------- - -The python discovery mechanism is a component that needs to answer the following question: based on some type of user -input give me a Python interpreter on the machine that matches that. The builtin interpreter tries to discover -an installed Python interpreter (based on PEP-515 and ``PATH`` discovery) on the users machine where the user input is a -python specification. An alternative such discovery mechanism for example would be to use the popular -`pyenv `_ project to discover, and if not present install the requested Python -interpreter. Python discovery mechanisms must be registered under key ``virtualenv.discovery``, and the plugin must -implement :class:`virtualenv.discovery.discover.Discover`: - -.. code-block:: ini - - virtualenv.discovery = - pyenv = virtualenv_pyenv.discovery:PyEnvDiscovery - - -.. currentmodule:: virtualenv.discovery.discover - -.. autoclass:: Discover - :undoc-members: - :members: - - -Creators --------- -Creators are what actually perform the creation of a virtual environment. The builtin virtual environment creators -all achieve this by referencing a global install; but would be just as valid for a creator to install a brand new -entire python under the target path; or one could add additional creators that can create virtual environments for other -python implementations, such as IronPython. They must be registered under and entry point with key -``virtualenv.create`` , and the class must implement :class:`virtualenv.create.creator.Creator`: - -.. code-block:: ini - - virtualenv.create = - cpython3-posix = virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Posix - -.. currentmodule:: virtualenv.create.creator - -.. autoclass:: Creator - :undoc-members: - :members: - :exclude-members: run, set_pyenv_cfg, debug_script, debug_script, validate_dest, debug - - -Seed mechanism --------------- - -Seeders are what given a virtual environment will install somehow some seed packages into it. They must be registered -under and entry point with key ``virtualenv.seed`` , and the class must implement -:class:`virtualenv.seed.seeder.Seeder`: - -.. code-block:: ini - - virtualenv.seed = - db = virtualenv.seed.fromDb:InstallFromDb - -.. currentmodule:: virtualenv.seed.seeder - -.. autoclass:: Seeder - :undoc-members: - :members: - -Activation scripts ------------------- -If you want add an activator for a new shell you can do this by implementing a new activator. They must be registered -under an entry point with key ``virtualenv.activate`` , and the class must implement -:class:`virtualenv.activation.activator.Activator`: - -.. code-block:: ini - - virtualenv.activate = - bash = virtualenv.activation.bash:BashActivator - -.. currentmodule:: virtualenv.activation.activator - -.. autoclass:: Activator - :undoc-members: - :members: diff --git a/docs/how-to/install.rst b/docs/how-to/install.rst new file mode 100644 index 000000000..78ad552c5 --- /dev/null +++ b/docs/how-to/install.rst @@ -0,0 +1,103 @@ +#################### + Install virtualenv +#################### + +virtualenv is a command-line tool, so it should be installed in an isolated environment rather than into your system +Python. Pick the method that fits your setup: + +- `uv `_ -- fast, modern Python package manager. Use this if you already have ``uv`` or are + starting fresh. +- `pipx `_ -- installs Python CLI tools in isolated environments. Use this if you already + have ``pipx`` set up. +- `pip `_ -- the standard Python package installer. Use ``--user`` to avoid modifying + system packages. May not work on distributions with externally-managed Python environments. +- `zipapp `_ -- a self-contained executable requiring no installation. + Use this in CI or environments where you cannot install packages. + +.. mermaid:: + + flowchart TD + A{Can you install packages?} -->|Yes| B{Have uv?} + A -->|No| Z[zipapp] + B -->|Yes| U[uv tool install] + B -->|No| C{Have pipx?} + C -->|Yes| P[pipx install] + C -->|No| D[pip install --user] + + style A fill:#d97706,stroke:#b45309,color:#fff + style B fill:#d97706,stroke:#b45309,color:#fff + style C fill:#d97706,stroke:#b45309,color:#fff + style U fill:#16a34a,stroke:#15803d,color:#fff + style P fill:#16a34a,stroke:#15803d,color:#fff + style D fill:#7c3aed,stroke:#6d28d9,color:#fff + style Z fill:#7c3aed,stroke:#6d28d9,color:#fff + +.. tab:: uv + + Install virtualenv as a `uv tool `_: + + .. code-block:: console + + $ uv tool install virtualenv + + Install the development version: + + .. code-block:: console + + $ uv tool install git+https://github.com/pypa/virtualenv.git@main + +.. tab:: pipx + + Install virtualenv using `pipx `_: + + .. code-block:: console + + $ pipx install virtualenv + + Install the development version: + + .. code-block:: console + + $ pipx install git+https://github.com/pypa/virtualenv.git@main + +.. tab:: pip + + Install virtualenv using `pip `_: + + .. code-block:: console + + $ python -m pip install --user virtualenv + + Install the development version: + + .. code-block:: console + + $ python -m pip install git+https://github.com/pypa/virtualenv.git@main + + .. warning:: + + Some Linux distributions use system-managed Python environments. If you encounter errors about externally-managed + environments, use ``uv tool`` or ``pipx`` instead. + +.. tab:: zipapp + + Download the zipapp file and run it directly: + + .. code-block:: console + + $ python virtualenv.pyz --help + + Download the latest version from https://bootstrap.pypa.io/virtualenv.pyz or a specific version from + ``https://bootstrap.pypa.io/virtualenv/x.y/virtualenv.pyz``. + +********************* + Verify installation +********************* + +Check the installed version: + +.. code-block:: console + + $ virtualenv --version + +See :doc:`../reference/compatibility` for supported Python versions. diff --git a/docs/how-to/usage.rst b/docs/how-to/usage.rst new file mode 100644 index 000000000..1a1286cdd --- /dev/null +++ b/docs/how-to/usage.rst @@ -0,0 +1,317 @@ +################ + Use virtualenv +################ + +************************* + Select a Python version +************************* + +By default, virtualenv uses the same Python version it runs under. Override this with ``--python`` or ``-p``. + +Using version specifiers +======================== + +Specify a Python version by name or version number: + +.. code-block:: console + + $ virtualenv -p python3.8 venv + $ virtualenv -p 3.10 venv + $ virtualenv -p pypy3 venv + +Using PEP 440 specifiers +======================== + +Use `PEP 440 `_ version specifiers to match Python versions: + +.. code-block:: console + + $ virtualenv --python ">=3.12" venv + $ virtualenv --python "~=3.11.0" venv + $ virtualenv --python "cpython>=3.10" venv + +- ``>=3.12`` -- any Python 3.12 or later. +- ``~=3.11.0`` -- compatible release, equivalent to ``>=3.11.0, <3.12.0`` (any 3.11.x patch). +- ``cpython>=3.10`` -- restrict to CPython implementation, 3.10 or later. + +Using free-threading Python +=========================== + +Create an environment with `free-threading Python `_: + +.. code-block:: console + + $ virtualenv -p 3.13t venv + +Using absolute paths +==================== + +Specify the full path to a Python interpreter: + +.. code-block:: console + + $ virtualenv -p /usr/bin/python3.9 venv + +Using ``--try-first-with`` +========================== + +Use ``--try-first-with`` to provide a hint about which Python to check first. Unlike ``--python``, this is a hint rather +than a rule. The interpreter at this path is checked first, but only used if it matches the ``--python`` constraint. + +.. code-block:: console + + $ virtualenv --python ">=3.10" --try-first-with /usr/bin/python3.9 venv + +In this example, /usr/bin/python3.9 is checked first but rejected because it does not satisfy the >=3.10 constraint. + +******************************** + Activate a virtual environment +******************************** + +Activate the environment to modify your shell's PATH and environment variables. + +.. tab:: Bash/Zsh + + .. code-block:: console + + $ source venv/bin/activate + +.. tab:: Fish + + .. code-block:: console + + $ source venv/bin/activate.fish + +.. tab:: PowerShell + + .. code-block:: console + + PS> .\venv\Scripts\Activate.ps1 + + .. note:: + + If you encounter an execution policy error, run ``Set-ExecutionPolicy RemoteSigned`` to allow local scripts. + +.. tab:: CMD + + .. code-block:: console + + > .\venv\Scripts\activate.bat + +.. tab:: Nushell + + .. code-block:: console + + $ overlay use venv/bin/activate.nu + +Deactivate the environment +========================== + +Exit the virtual environment: + +.. code-block:: console + + $ deactivate + +Use without activation +====================== + +Use the environment without activating it by calling executables with their full paths: + +.. code-block:: console + + $ venv/bin/python script.py + $ venv/bin/pip install package + +Customize prompt +================ + +Set a custom prompt prefix: + +.. code-block:: console + + $ virtualenv --prompt myproject venv + +Disable the prompt modification by setting the ``VIRTUAL_ENV_DISABLE_PROMPT`` environment variable. + +Access the prompt string via the ``VIRTUAL_ENV_PROMPT`` environment variable. + +Programmatic activation +======================= + +Activate the environment from within a running Python process using ``activate_this.py``. This modifies ``sys.path`` and +environment variables in the current process so that subsequent imports resolve from the virtual environment. + +.. code-block:: python + + import runpy + + runpy.run_path("venv/bin/activate_this.py") + +A common use case is web applications served by a system-wide WSGI server (such as mod_wsgi or uWSGI) that need to load +packages from a virtual environment: + +.. code-block:: python + + import runpy + from pathlib import Path + + runpy.run_path(str(Path("/var/www/myapp/venv/bin/activate_this.py"))) + + from myapp import create_app # noqa: E402 + + application = create_app() + +******************** + Configure defaults +******************** + +Use a configuration file to set default options for virtualenv. + +Configuration file location +=========================== + +The configuration file is named ``virtualenv.ini`` and located in the platformdirs app config directory. Run +``virtualenv --help`` to see the exact location for your system. + +Override the location with the ``VIRTUALENV_CONFIG_FILE`` environment variable. + +Configuration format +==================== + +Derive configuration keys from command-line options by stripping leading ``-`` and replacing remaining ``-`` with ``_``: + +.. code-block:: ini + + [virtualenv] + python = /opt/python-3.8/bin/python + +Multi-value options +=================== + +Specify multiple values on separate lines: + +.. code-block:: ini + + [virtualenv] + extra_search_dir = + /path/to/dists + /path/to/other/dists + +Environment variables +===================== + +Set options using environment variables with the ``VIRTUALENV_`` prefix and uppercase key names: + +.. code-block:: console + + $ export VIRTUALENV_PYTHON=/opt/python-3.8/bin/python + +For multi-value options, separate values with commas or newlines. + +Override app-data location +========================== + +Set the ``VIRTUALENV_OVERRIDE_APP_DATA`` environment variable to override the default app-data cache directory location. + +Configuration priority +====================== + +Options are resolved in this order (highest to lowest priority): + +.. mermaid:: + + block-beta + columns 1 + A["Command-line arguments (highest)"] + B["Environment variables"] + C["Configuration file"] + D["Default values (lowest)"] + + style A fill:#16a34a,stroke:#15803d,color:#fff + style B fill:#2563eb,stroke:#1d4ed8,color:#fff + style C fill:#d97706,stroke:#b45309,color:#fff + style D fill:#6366f1,stroke:#4f46e5,color:#fff + +*********************** + Control seed packages +*********************** + +Upgrade embedded wheels +======================= + +Update the embedded wheel files to the latest versions: + +.. code-block:: console + + $ virtualenv --upgrade-embed-wheels + +Provide custom wheels +===================== + +Use custom wheel files from a local directory: + +.. code-block:: console + + $ virtualenv --extra-search-dir /path/to/wheels venv + +Download latest from PyPI +========================= + +Download the latest versions of seed packages from PyPI: + +.. code-block:: console + + $ virtualenv --download venv + +Disable periodic updates +======================== + +Disable automatic periodic updates of seed packages: + +.. code-block:: console + + $ virtualenv --no-periodic-update venv + +For distribution maintainers +============================ + +Patch the ``virtualenv.seed.wheels.embed`` module and set ``PERIODIC_UPDATE_ON_BY_DEFAULT`` to ``False`` to disable +periodic updates by default. See :doc:`../explanation` for implementation details. + +********************** + Use from Python code +********************** + +Call virtualenv from Python code using the ``cli_run`` function: + +.. code-block:: python + + from virtualenv import cli_run + + cli_run(["venv"]) + +Pass options as list elements: + +.. code-block:: python + + cli_run(["-p", "python3.8", "--without-pip", "myenv"]) + +Use the returned session object to access environment details: + +.. code-block:: python + + result = cli_run(["venv"]) + print(result.creator.dest) # path to created environment + print(result.creator.exe) # path to python executable + +Use ``session_via_cli`` to describe the environment without creating it: + +.. code-block:: python + + from virtualenv import session_via_cli + + session = session_via_cli(["venv"]) + # inspect session.creator, session.seeder, session.activators + +See :doc:`../reference/api` for complete API documentation. diff --git a/docs/index.rst b/docs/index.rst index 52484a9ae..050eed9f5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,122 +1,137 @@ -virtualenv -========== +############ + virtualenv +############ .. image:: https://img.shields.io/pypi/v/virtualenv?style=flat-square - :target: https://pypi.org/project/virtualenv/#history - :alt: Latest version on PyPI + :target: https://pypi.org/project/virtualenv/#history + :alt: Latest version on PyPI + .. image:: https://img.shields.io/pypi/implementation/virtualenv?style=flat-square - :alt: PyPI - Implementation + :alt: PyPI - Implementation + .. image:: https://img.shields.io/pypi/pyversions/virtualenv?style=flat-square - :alt: PyPI - Python Version + :alt: PyPI - Python Version + .. image:: https://readthedocs.org/projects/virtualenv/badge/?version=latest&style=flat-square - :target: https://virtualenv.pypa.io - :alt: Documentation status + :target: https://virtualenv.pypa.io + :alt: Documentation status + .. image:: https://img.shields.io/discord/803025117553754132 - :target: https://discord.gg/pypa - :alt: Discord + :target: https://discord.gg/pypa + :alt: Discord + .. image:: https://img.shields.io/pypi/dm/virtualenv?style=flat-square - :target: https://pypistats.org/packages/virtualenv - :alt: PyPI - Downloads + :target: https://pypistats.org/packages/virtualenv + :alt: PyPI - Downloads + .. image:: https://img.shields.io/pypi/l/virtualenv?style=flat-square - :target: https://opensource.org/licenses/MIT - :alt: PyPI - License + :target: https://opensource.org/licenses/MIT + :alt: PyPI - License + .. image:: https://img.shields.io/github/issues/pypa/virtualenv?style=flat-square - :target: https://github.com/pypa/virtualenv/issues - :alt: Open issues + :target: https://github.com/pypa/virtualenv/issues + :alt: Open issues + .. image:: https://img.shields.io/github/issues-pr/pypa/virtualenv?style=flat-square - :target: https://github.com/pypa/virtualenv/pulls - :alt: Open pull requests + :target: https://github.com/pypa/virtualenv/pulls + :alt: Open pull requests + .. image:: https://img.shields.io/github/stars/pypa/virtualenv?style=flat-square - :target: https://pypistats.org/packages/virtualenv - :alt: Package popularity + :target: https://pypistats.org/packages/virtualenv + :alt: Package popularity -``virtualenv`` is a tool to create isolated Python environments. +``virtualenv`` is a tool to create isolated Python environments. Since Python 3.3, a subset of it has been integrated +into the standard library under the ``venv`` module. For how ``virtualenv`` compares to the stdlib ``venv`` module, see +:doc:`explanation`. -virtualenv vs venv ------------------- +****************** + Quick navigation +****************** -Since Python ``3.3``, a subset of it has been -integrated into the standard library under the `venv module `_. The -``venv`` module does not offer all features of this library, to name just a few more prominent: +**Tutorials** - Learn by doing -- is slower (by not having the ``app-data`` seed method), -- is not as extendable, -- cannot create virtual environments for arbitrarily installed python versions (and automatically discover these), -- is not upgrade-able via `pip `_, -- does not have as rich programmatic API (describe virtual environments without creating them). +- :doc:`tutorial/getting-started` — Create your first virtual environment and learn the basic workflow -Concept and purpose of virtualenv ---------------------------------- +**How-to guides** - Solve specific problems -The basic problem being addressed is one of dependencies and versions, and indirectly permissions. -Imagine you have an application that needs version ``1`` of ``LibFoo``, but another application requires version -``2``. How can you use both these libraries? If you install everything into your host python (e.g. ``python3.8``) -it's easy to end up in a situation where two packages have conflicting requirements. +- :doc:`how-to/install` — Install virtualenv on your system +- :doc:`how-to/usage` — Select Python versions, activate environments, configure defaults, and use from Python code -Or more generally, what if you want to install an application *and leave it be*? If an application works, any change -in its libraries or the versions of those libraries can break the application. Also, what if you can't install packages -into the global ``site-packages`` directory, due to not having permissions to change the host python environment? +**Reference** - Technical information -In all these cases, ``virtualenv`` can help you. It creates an environment that has its own installation directories, -that doesn't share libraries with other virtualenv environments (and optionally doesn't access the globally installed -libraries either). +- :doc:`reference/compatibility` — Supported Python versions and operating systems +- :doc:`reference/cli` — Command line options and flags +- :doc:`reference/api` — Programmatic Python API reference +**Explanation** - Understand the concepts -Compatibility -------------- +- :doc:`explanation` — How virtualenv works under the hood and why it exists -With the release of virtualenv 20.22, April 2023, (`release note `__) target interpreters are now limited to Python v. 3.7+. +**Extensions** -Trying to use an earlier version will normally result in the target interpreter raising a syntax error. This virtualenv tool will then print some details about the exception and abort, ie no explicit warning about trying to use an outdated/incompatible version. It may look like this: +- :doc:`plugin/index` — Extend virtualenv with custom creators, seeders, and activators -.. code-block:: console +****************** + Related projects +****************** - $ virtualenv --discovery pyenv -p python3.6 foo - RuntimeError: failed to query /home/velle/.pyenv/versions/3.6.15/bin/python3.6 with code 1 err: ' File "/home/velle/.virtualenvs/toxrunner/lib/python3.12/site-packages/virtualenv/discovery/py_info.py", line 7 - from __future__ import annotations - ^ - SyntaxError: future feature annotations is not defined +Several tools build on virtualenv to provide higher-level workflows: +- `virtualenvwrapper `_ — Shell wrapper for creating and managing + multiple virtualenvs +- `pew `_ — Python Env Wrapper, a set of commands to manage multiple virtual + environments +- `tox `_ — Automate testing across multiple Python versions +- `nox `_ — Flexible test automation in Python -In tox, even if the interpreter is installed and available, the message is (somewhat misleading): +******************** + External resources +******************** -.. code-block:: console +Learn more about virtualenv from these community resources: - py36: skipped because could not find python interpreter with spec(s): py36 +- `Corey Schafer's virtualenv tutorial `_ — Video walkthrough for beginners +- `Bernat Gabor's status quo `_ — Talk about the current state of Python + packaging +- `Carl Meyer's reverse-engineering `_ — + Deep dive into how virtualenv works internally +.. toctree:: + :hidden: + :caption: Tutorial + tutorial/getting-started -Useful links ------------- +.. toctree:: + :hidden: + :caption: How-to guides -**Related projects, that build abstractions on top of virtualenv** + how-to/install + how-to/usage -* :pypi:`virtualenvwrapper` - a useful set of scripts for creating and deleting virtual environments -* :pypi:`pew` - provides a set of commands to manage multiple virtual environments -* :pypi:`tox` - a generic virtualenv management and test automation command line tool, driven by a ``tox.ini`` - configuration file -* :pypi:`nox` - a tool that automates testing in multiple Python environments, similar to tox, - driven by a ``noxfile.py`` configuration file +.. toctree:: + :hidden: + :caption: Reference -**Tutorials** + reference/compatibility + reference/cli + reference/api -* `Corey Schafer tutorial `_ on how to use it -* `Using virtualenv with mod_wsgi `_ +.. toctree:: + :hidden: + :caption: Explanation -**Presenting how the package works from within** + explanation -* `Bernat Gabor: status quo of virtual environments `_ -* `Carl Meyer: Reverse-engineering Ian Bicking's brain: inside pip and virtualenv - `_ +.. toctree:: + :hidden: + :caption: Extend -.. comment: split here + plugin/index .. toctree:: - :hidden: - - installation - user_guide - cli_interface - extend - development - changelog + :hidden: + :caption: Project + + development + changelog diff --git a/docs/installation.rst b/docs/installation.rst deleted file mode 100644 index b82d1dd19..000000000 --- a/docs/installation.rst +++ /dev/null @@ -1,124 +0,0 @@ -Installation -============ - -via pipx --------- - -:pypi:`virtualenv` is a CLI tool that needs a Python interpreter to run. If you already have a ``Python 3.7+`` -interpreter the best is to use :pypi:`pipx` to install virtualenv into an isolated environment. This has the added -benefit that later you'll be able to upgrade virtualenv without affecting other parts of the system. - -.. code-block:: console - - pipx install virtualenv - virtualenv --help - -via pip -------- - -Alternatively you can install it within the global Python interpreter itself (perhaps as a user package via the -``--user`` flag). Be cautious if you are using a python install that is managed by your operating system or -another package manager. ``pip`` might not coordinate with those tools, and may leave your system in an -inconsistent state. Note, if you go down this path you need to ensure pip is new enough per the subsections below: - -.. code-block:: console - - python -m pip install --user virtualenv - python -m virtualenv --help - -wheel -~~~~~ -Installing virtualenv via a wheel (default with pip) requires an installer that can understand the ``python-requires`` -tag (see `PEP-503 `_), with pip this is version ``9.0.0`` (released 2016 -November). Furthermore, in case you're not installing it via the PyPi you need to be using a mirror that correctly -forwards the ``python-requires`` tag (notably the OpenStack mirrors don't do this, or older -`devpi `_ versions - added with version ``4.7.0``). - -.. _sdist: - -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 on 2018 July). If you cannot upgrade your pip to support this you need to ensure that the build -requirements from `pyproject.toml `_ are satisfied -before triggering the install. - -via zipapp ----------- - -You can use virtualenv without installing it too. We publish a Python -`zipapp `_, you can just download this from -`https://bootstrap.pypa.io/virtualenv.pyz `_ and invoke this package -with a python interpreter: - -.. code-block:: console - - python virtualenv.pyz --help - -The root level zipapp is always the current latest release. To get the last supported zipapp against a given python -minor release use the link ``https://bootstrap.pypa.io/virtualenv/x.y/virtualenv.pyz``, e.g. for the last virtualenv -supporting Python 3.11 use -`https://bootstrap.pypa.io/virtualenv/3.11/virtualenv.pyz `_. - -If you are looking for past version of virtualenv.pyz they are available here: - -.. code-block:: console - - https://github.com/pypa/get-virtualenv/blob//public//virtualenv.pyz?raw=true - -latest unreleased ------------------ -Installing an unreleased version is discouraged and should be only done for testing purposes. If you do so you'll need -a pip version of at least ``18.0.0`` and use the following command: - - -.. code-block:: console - - pip install git+https://github.com/pypa/virtualenv.git@main - -.. _compatibility-requirements: - -Python and OS Compatibility ---------------------------- - -virtualenv works with the following Python interpreter implementations: - -- `CPython `_: ``3.13 >= python_version >= 3.7`` -- `PyPy `_: ``3.10 >= python_version >= 3.7`` - -This means virtualenv works on the latest patch version of each of these minor versions. Previous patch versions are -supported on a best effort approach. - -CPython is shipped in multiple forms, and each OS repackages it, often applying some customization along the way. -Therefore we cannot say universally that we support all platforms, but rather specify some we test against. In case -of ones not specified here the support is unknown, though likely will work. If you find some cases please open a feature -request on our issue tracker. - -Note: - -- as of ``20.27.0`` -- ``2024-10-17`` -- we no longer support running under Python ``<=3.7``, -- as of ``20.18.0`` -- ``2023-02-06`` -- we no longer support running under Python ``<=3.6``, -- as of ``20.22.0`` -- ``2023-04-19`` -- we no longer support creating environments for Python ``<=3.6``. - -Linux -~~~~~ -- installations from `python.org `_ -- Ubuntu 16.04+ (both upstream and `deadsnakes `_ builds) -- Fedora -- RHEL and CentOS -- OpenSuse -- Arch Linux - -macOS -~~~~~ -In case of macOS we support: - -- installations from `python.org `_, -- python versions installed via `brew `_, -- Python 3 part of XCode (Python framework - ``/Library/Frameworks/Python3.framework/``). - -Windows -~~~~~~~ -- Installations from `python.org `_ -- Windows Store Python - note only `version 3.8+ `_ diff --git a/docs/plugin/api.rst b/docs/plugin/api.rst new file mode 100644 index 000000000..dee75e40e --- /dev/null +++ b/docs/plugin/api.rst @@ -0,0 +1,79 @@ +###################### + Plugin API reference +###################### + +This page documents the interfaces that plugins must implement. + +*********** + Discovery +*********** + +Discovery plugins locate Python interpreters for creating virtual environments. + +.. currentmodule:: virtualenv.discovery.discover + +.. autoclass:: Discover + :undoc-members: + :members: + +PythonInfo +========== + +Discovery plugins return a ``PythonInfo`` object describing the located interpreter. + +.. currentmodule:: virtualenv.discovery.py_info + +.. autoclass:: PythonInfo + :undoc-members: + :members: + +********** + App data +********** + +The application data interface used by plugins for caching. + +.. currentmodule:: virtualenv.app_data.base + +.. autoclass:: AppData + :members: + +********** + Creators +********** + +Creator plugins build the virtual environment directory structure and install the Python interpreter. + +.. currentmodule:: virtualenv.create.creator + +.. autoclass:: CreatorMeta + :members: + +.. autoclass:: Creator + :undoc-members: + :members: + :exclude-members: run, set_pyenv_cfg, debug_script, validate_dest, debug + +********* + Seeders +********* + +Seeder plugins install initial packages (like pip, setuptools, wheel) into the virtual environment. + +.. currentmodule:: virtualenv.seed.seeder + +.. autoclass:: Seeder + :undoc-members: + :members: + +************ + Activators +************ + +Activator plugins generate shell-specific activation scripts. + +.. currentmodule:: virtualenv.activation.activator + +.. autoclass:: Activator + :undoc-members: + :members: diff --git a/docs/plugin/architecture.rst b/docs/plugin/architecture.rst new file mode 100644 index 000000000..4ce911622 --- /dev/null +++ b/docs/plugin/architecture.rst @@ -0,0 +1,125 @@ +##################### + Plugin architecture +##################### + +This page explains how virtualenv's plugin system works internally. + +************** + Entry points +************** + +virtualenv uses Python entry points (``setuptools`` / ``importlib.metadata``) to discover plugins. Each plugin registers +under one of four entry point groups: + +- ``virtualenv.discovery`` +- ``virtualenv.create`` +- ``virtualenv.seed`` +- ``virtualenv.activate`` + +At startup, virtualenv loads all registered entry points from these groups and makes them available as CLI options. +Built-in implementations are registered in virtualenv's own ``pyproject.toml``, while third-party plugins register their +entry points in their own package metadata. + +When a package with virtualenv plugins is installed in the same environment as virtualenv, the plugins become +immediately available without additional configuration. + +****************** + Plugin lifecycle +****************** + +The following diagram shows how plugins are discovered and executed: + +.. mermaid:: + + sequenceDiagram + participant User + participant CLI + participant EntryPoints + participant Discovery + participant Creator + participant Seeder + participant Activator + + rect rgba(37, 99, 235, 0.15) + User->>CLI: virtualenv myenv + CLI->>EntryPoints: Load plugins from all groups + EntryPoints-->>CLI: Available plugins + CLI->>CLI: Build argument parser with plugin options + CLI->>User: Parse CLI arguments + User-->>CLI: Selected options + end + + rect rgba(22, 163, 106, 0.15) + CLI->>Discovery: Run selected discovery plugin + Discovery-->>CLI: PythonInfo + CLI->>Creator: Create environment with PythonInfo + Creator-->>CLI: Created environment + CLI->>Seeder: Seed packages into environment + Seeder-->>CLI: Seeded environment + CLI->>Activator: Generate activation scripts + Activator-->>CLI: Complete environment + end + +The lifecycle follows these stages: + +1. virtualenv starts and discovers all entry points from the four plugin groups +2. The CLI parser is built dynamically, incorporating options from all discovered plugins +3. User arguments are parsed to select which discovery, creator, seeder, and activator plugins to use +4. Selected plugins execute in sequence: discover → create → seed → activate +5. Each stage passes its output to the next stage + +************************ + Extension point design +************************ + +Each extension point follows a consistent pattern: + +Base abstract class + Each extension point defines a base abstract class (``Discover``, ``Creator``, ``Seeder``, ``Activator``) that + specifies the interface plugins must implement. + +Built-in implementations + virtualenv includes built-in implementations registered as entry points in its own ``pyproject.toml``. For example, + the built-in CPython creator is registered as ``cpython3-posix``. + +Third-party plugins + External packages implement the base interface and register their own entry points under the same group. When + installed, they appear alongside built-in options. + +CLI selection + Command-line flags (``--discovery``, ``--creator``, ``--seeder``, ``--activators``) allow users to select which + implementation to use. Multiple activators can be selected simultaneously. + +Parser integration + Each plugin can contribute CLI arguments through the ``add_parser_arguments`` classmethod. These arguments appear in + ``virtualenv --help`` and are available when the plugin is selected. + +********************** + How plugins interact +********************** + +Plugins execute in a pipeline where each stage depends on the previous one: + +Discovery → Creator + The discovery plugin produces a ``PythonInfo`` object describing the source Python interpreter. This object contains + metadata about the Python version, platform, paths, and capabilities. The creator plugin receives this + ``PythonInfo`` and uses it to determine how to build the virtual environment structure. + +Creator → Seeder + The creator plugin produces a ``Creator`` object representing the newly created virtual environment. This includes + paths to the environment's ``bin`` directory, site-packages, and Python executable. The seeder plugin uses these + paths to install packages. + +Seeder → Activator + After seeding completes, activator plugins use the ``Creator`` object to generate shell activation scripts. These + scripts reference the environment's bin directory and other paths to configure the shell environment. + +This pipeline ensures that each plugin has the information it needs from previous stages. The ``PythonInfo`` flows from +discovery to creator, and the ``Creator`` object flows from creator to both seeder and activators. + +Plugin isolation +================ + +Plugins within the same extension point do not interact with each other. Only one discovery and one creator plugin can +run per invocation, though multiple activators can run simultaneously. This isolation keeps plugins simple and focused +on their specific task. diff --git a/docs/plugin/how-to.rst b/docs/plugin/how-to.rst new file mode 100644 index 000000000..7c11a91f8 --- /dev/null +++ b/docs/plugin/how-to.rst @@ -0,0 +1,210 @@ +###################### + Plugin how-to guides +###################### + +This page provides task-oriented guides for creating each type of virtualenv plugin. + +*************************** + Create a discovery plugin +*************************** + +Discovery plugins locate Python interpreters. Register your plugin under the ``virtualenv.discovery`` entry point group. + +Implement the ``Discover`` interface: + +.. code-block:: python + + from virtualenv.discovery.discover import Discover + from virtualenv.discovery.py_info import PythonInfo + + + class CustomDiscovery(Discover): + @classmethod + def add_parser_arguments(cls, parser): + parser.add_argument("--custom-opt", help="custom discovery option") + + def __init__(self, options): + super().__init__(options) + self.custom_opt = options.custom_opt + + def run(self): + # Locate Python interpreter and return PythonInfo + python_exe = self._find_python() + return PythonInfo.from_exe(str(python_exe)) + + def _find_python(self): + # Implementation-specific logic + pass + +Register the entry point: + +.. code-block:: ini + + [virtualenv.discovery] + custom = your_package.discovery:CustomDiscovery + +************************* + Create a creator plugin +************************* + +Creator plugins build the virtual environment structure. Register under ``virtualenv.create``. + +Implement the ``Creator`` interface: + +.. code-block:: python + + from virtualenv.create.creator import Creator + + + class CustomCreator(Creator): + @classmethod + def add_parser_arguments(cls, parser, interpreter): + parser.add_argument("--custom-creator-opt", help="custom creator option") + + def __init__(self, options, interpreter): + super().__init__(options, interpreter) + self.custom_opt = options.custom_creator_opt + + def create(self): + # Create directory structure + self.bin_dir.mkdir(parents=True, exist_ok=True) + # Copy or symlink Python executable + self.install_python() + # Set up site-packages + self.install_site_packages() + # Write pyvenv.cfg + self.set_pyenv_cfg() + +Register the entry point using a naming pattern that matches platform and Python version: + +.. code-block:: ini + + [virtualenv.create] + cpython3-posix = virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Posix + cpython3-win = virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Windows + +************************ + Create a seeder plugin +************************ + +Seeder plugins install initial packages into the virtual environment. Register under ``virtualenv.seed``. + +Implement the ``Seeder`` interface: + +.. code-block:: python + + from virtualenv.seed.seeder import Seeder + + + class CustomSeeder(Seeder): + @classmethod + def add_parser_arguments(cls, parser, interpreter, app_data): + parser.add_argument("--custom-seed-opt", help="custom seeder option") + + def __init__(self, options, enabled, app_data): + super().__init__(options, enabled, app_data) + self.custom_opt = options.custom_seed_opt + + def run(self, creator): + # Install packages into creator.bin_dir / creator.script("pip") + self._install_packages(creator) + + def _install_packages(self, creator): + # Implementation-specific logic + pass + +Register the entry point: + +.. code-block:: ini + + [virtualenv.seed] + custom = your_package.seed:CustomSeeder + +**************************** + Create an activator plugin +**************************** + +Activator plugins generate shell activation scripts. Register under ``virtualenv.activate``. + +Implement the ``Activator`` interface: + +.. code-block:: python + + from virtualenv.activation.activator import Activator + + + class CustomShellActivator(Activator): + def generate(self, creator): + # Generate activation script content + script_content = self._render_template(creator) + # Write to activation directory + dest = creator.bin_dir / self.script_name + dest.write_text(script_content) + + def _render_template(self, creator): + # Return activation script content + return f""" + # Custom shell activation script + export VIRTUAL_ENV="{creator.dest}" + export PATH="{creator.bin_dir}:$PATH" + """ + + @property + def script_name(self): + return "activate.custom" + +Register the entry point: + +.. code-block:: ini + + [virtualenv.activate] + bash = virtualenv.activation.bash:BashActivator + fish = virtualenv.activation.fish:FishActivator + custom = your_package.activation:CustomShellActivator + +********************************* + Package and distribute a plugin +********************************* + +Use ``pyproject.toml`` to declare entry points: + +.. code-block:: toml + + [project] + name = "virtualenv-custom-plugin" + version = "1.0.0" + dependencies = ["virtualenv>=20.0.0"] + + [project.entry-points."virtualenv.discovery"] + custom = "virtualenv_custom.discovery:CustomDiscovery" + + [project.entry-points."virtualenv.create"] + custom-posix = "virtualenv_custom.creator:CustomCreator" + + [project.entry-points."virtualenv.seed"] + custom = "virtualenv_custom.seeder:CustomSeeder" + + [project.entry-points."virtualenv.activate"] + custom = "virtualenv_custom.activator:CustomActivator" + + [build-system] + requires = ["setuptools>=61"] + build-backend = "setuptools.build_meta" + +Install your plugin alongside virtualenv: + +.. code-block:: console + + $ pip install virtualenv-custom-plugin + +Or in development mode: + +.. code-block:: console + + $ pip install -e /path/to/virtualenv-custom-plugin + +Test your plugin by creating a virtual environment: + +.. code-block:: console + + $ virtualenv --discovery=custom --creator=custom-posix --seeder=custom --activators=custom test-env diff --git a/docs/plugin/index.rst b/docs/plugin/index.rst new file mode 100644 index 000000000..79b39f952 --- /dev/null +++ b/docs/plugin/index.rst @@ -0,0 +1,39 @@ +######### + Plugins +######### + +virtualenv can be extended via plugins using Python entry points. Plugins are automatically discovered from the Python +environment where virtualenv is installed, allowing you to customize how virtual environments are created, seeded, and +activated. + +****************** + Extension points +****************** + +virtualenv provides four extension points through entry point groups: + +``virtualenv.discovery`` + Python interpreter discovery plugins. These plugins locate and identify Python interpreters that will be used as the + base for creating virtual environments. + +``virtualenv.create`` + Virtual environment creator plugins. These plugins handle the actual creation of the virtual environment structure, + including copying or symlinking the Python interpreter and standard library. + +``virtualenv.seed`` + Seed package installer plugins. These plugins install initial packages (like pip, setuptools, wheel) into newly + created virtual environments. + +``virtualenv.activate`` + Shell activation script plugins. These plugins generate shell-specific activation scripts that modify the + environment to use the virtual environment. + +All extension points follow a common pattern: virtualenv discovers registered entry points, builds CLI options from +them, and executes the selected implementations during environment creation. + +.. toctree:: + + tutorial + how-to + api + architecture diff --git a/docs/plugin/tutorial.rst b/docs/plugin/tutorial.rst new file mode 100644 index 000000000..abe20488d --- /dev/null +++ b/docs/plugin/tutorial.rst @@ -0,0 +1,116 @@ +################### + Your first plugin +################### + +This tutorial walks through creating a simple discovery plugin that locates Python interpreters managed by pyenv. + +****************************** + Create the package structure +****************************** + +Set up a new Python package with the following structure: + +.. code-block:: text + + virtualenv-pyenv/ + ├── pyproject.toml + └── src/ + └── virtualenv_pyenv/ + └── __init__.py + +*************************** + Configure the entry point +*************************** + +In ``pyproject.toml``, declare your plugin as an entry point under the ``virtualenv.discovery`` group: + +.. code-block:: toml + + [project] + name = "virtualenv-pyenv" + version = "0.1.0" + dependencies = ["virtualenv>=20"] + + [project.entry-points."virtualenv.discovery"] + pyenv = "virtualenv_pyenv:PyEnvDiscovery" + + [build-system] + requires = ["setuptools>=61"] + build-backend = "setuptools.build_meta" + +********************** + Implement the plugin +********************** + +In ``src/virtualenv_pyenv/__init__.py``, implement the discovery plugin by subclassing ``Discover``: + +.. code-block:: python + + from __future__ import annotations + + import subprocess + from pathlib import Path + + from virtualenv.discovery.discover import Discover + from virtualenv.discovery.py_info import PythonInfo + + + class PyEnvDiscovery(Discover): + def __init__(self, options): + super().__init__(options) + self.python_spec = options.python if options.python else "python" + + @classmethod + def add_parser_arguments(cls, parser): + parser.add_argument( + "--python", + dest="python", + metavar="py", + type=str, + default=None, + help="pyenv Python version to use (e.g., 3.11.0)", + ) + + def run(self): + try: + result = subprocess.run( + ["pyenv", "which", "python"], + capture_output=True, + text=True, + check=True, + ) + python_path = Path(result.stdout.strip()) + return PythonInfo.from_exe(str(python_path)) + except (subprocess.CalledProcessError, FileNotFoundError) as e: + raise RuntimeError(f"Failed to locate pyenv Python: {e}") + +******************** + Install the plugin +******************** + +Install your plugin in development mode alongside virtualenv: + +.. code-block:: console + + $ pip install -e virtualenv-pyenv/ + +******************* + Verify the plugin +******************* + +Check that virtualenv recognizes your plugin by running: + +.. code-block:: console + + $ virtualenv --discovery help + +The output should list ``pyenv`` as an available discovery mechanism. You can now use it: + +.. code-block:: console + + $ virtualenv --discovery=pyenv myenv + created virtual environment CPython3.11.0.final.0-64 in 234ms + creator CPython3Posix(dest=/path/to/myenv, clear=False, no_vcs_ignore=False, global=False) + seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/path) + added seed packages: pip==23.0, setuptools==65.5.0, wheel==0.38.4 + activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator diff --git a/docs/reference/api.rst b/docs/reference/api.rst new file mode 100644 index 000000000..30a5f6cf9 --- /dev/null +++ b/docs/reference/api.rst @@ -0,0 +1,40 @@ +.. _programmatic_api: + +######## + Python +######## + +The primary interface to ``virtualenv`` is the command line application. However, it can also be used programmatically +via the ``virtualenv.cli_run`` function and the ``Session`` class. + +See :doc:`../how-to/usage` for usage examples. + +******************* + virtualenv module +******************* + +.. automodule:: virtualenv + :members: + +*************** + Session class +*************** + +The ``Session`` class represents a virtualenv creation session and provides access to the created environment's +properties. + +.. currentmodule:: virtualenv.run.session + +.. autoclass:: Session + :members: + +******************* + VirtualEnvOptions +******************* + +Options namespace passed to plugin constructors, populated from the CLI, environment variables, and configuration files. + +.. currentmodule:: virtualenv.config.cli.parser + +.. autoclass:: VirtualEnvOptions + :members: diff --git a/docs/reference/cli.rst b/docs/reference/cli.rst new file mode 100644 index 000000000..bd7f889b5 --- /dev/null +++ b/docs/reference/cli.rst @@ -0,0 +1,18 @@ +############## + Command line +############## + +``virtualenv`` is primarily a command line application. All options have sensible defaults, and there is one required +argument: the name or path of the virtual environment to create. + +See :doc:`../how-to/usage` for how to select Python versions, configure defaults, and use environment variables. + +********************** + Command line options +********************** + +:command:`virtualenv [OPTIONS]` + +.. table_cli:: + :module: virtualenv.run + :func: build_parser_only diff --git a/docs/reference/compatibility.rst b/docs/reference/compatibility.rst new file mode 100644 index 000000000..0aef19f4c --- /dev/null +++ b/docs/reference/compatibility.rst @@ -0,0 +1,89 @@ +.. _compatibility-requirements: + +############### + Compatibility +############### + +********************************** + Supported Python implementations +********************************** + +``virtualenv`` works with the following Python interpreter implementations. Only the latest patch version of each minor +version is fully supported; previous patch versions work on a best effort basis. + +CPython +======= + +``3.14 >= python_version >= 3.8`` + +PyPy +==== + +``3.11 >= python_version >= 3.8`` + +GraalPy +======= + +``24.1`` and later (Linux and macOS only). + +**************** + Support policy +**************** + +- **New versions** are added close to their release date, typically during the beta phase. +- **Old versions** are dropped 18 months after `CPython EOL `_, giving users + plenty of time to migrate. + +************************** + Version support timeline +************************** + +Major version support changes: + +- **20.27.0** (2024-10-17): dropped support for running under Python 3.7 and earlier. +- **20.22.0** (2023-04-19): dropped support for creating environments for Python 3.6 and earlier. +- **20.18.0** (2023-02-06): dropped support for running under Python 3.6 and earlier. + +***************************** + Supported operating systems +***************************** + +CPython is shipped in multiple forms, and each OS repackages it, often applying some customization. The platforms listed +below are tested. Unlisted platforms may work but are not explicitly supported. If you encounter issues on unlisted +platforms, please open a feature request. + +Cross-platform +============== + +These Python distributions work on Linux, macOS, and Windows: + +- Installations from `python.org `_ +- `python-build-standalone `_ builds (used by `uv + `_ and `mise `_) + +Linux +===== + +- Ubuntu 16.04 and later (both upstream and `deadsnakes `_ + builds) +- Fedora +- RHEL and CentOS +- OpenSuse +- Arch Linux + +macOS +===== + +- Python versions installed via `Homebrew `_ (works, but `not recommended + `_ -- Homebrew may upgrade or remove Python versions + without warning, breaking existing virtual environments) +- Python 3 part of XCode (Python framework builds at ``/Library/Frameworks/Python3.framework/``) + +.. note:: + + Framework builds do not support copy-based virtual environments. Use symlink or hardlink creation methods instead. + +Windows +======= + +- `Windows Store `_ Python 3.8 and later diff --git a/docs/tutorial/getting-started.rst b/docs/tutorial/getting-started.rst new file mode 100644 index 000000000..82d04953f --- /dev/null +++ b/docs/tutorial/getting-started.rst @@ -0,0 +1,242 @@ +################# + Getting started +################# + +This tutorial will teach you the basics of virtualenv through hands-on practice. You'll create your first virtual +environment, install packages, and learn how to manage project dependencies. + +*************** + Prerequisites +*************** + +Before starting this tutorial, you need: + +- Python 3.8 or later installed on your system. +- virtualenv installed (see :doc:`../how-to/install`). + +*************************************** + Create your first virtual environment +*************************************** + +Let's create a virtual environment called ``myproject``: + +.. code-block:: console + + $ virtualenv myproject + created virtual environment CPython3.13.2.final.0-64 in 200ms + creator CPython3Posix(dest=/home/user/myproject, clear=False, no_vcs_ignore=False, global=False) + seeder FromAppData(download=False, pip=bundle, setuptools=bundle, via=copy, app_data_dir=/home/user/.cache/virtualenv) + activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator + +This creates a new directory called ``myproject`` containing a complete, isolated Python environment with its own copy +of Python, pip, and other tools. + +************************** + Activate the environment +************************** + +To use your virtual environment, you can activate it. The activation command differs by platform: + +.. tab:: Linux/macOS + + .. code-block:: console + + $ source myproject/bin/activate + +.. tab:: Windows (PowerShell) + + .. code-block:: console + + PS> .\myproject\Scripts\Activate.ps1 + +.. tab:: Windows (CMD) + + .. code-block:: console + + C:\> .\myproject\Scripts\activate.bat + +After activation, your prompt changes to show the active environment: + +.. code-block:: console + + (myproject) $ + +You can verify that Python is now running from inside the virtual environment: + +.. tab:: Linux/macOS + + .. code-block:: console + + (myproject) $ which python + /home/user/myproject/bin/python + +.. tab:: Windows (PowerShell) + + .. code-block:: console + + (myproject) PS> where.exe python + C:\Users\user\myproject\Scripts\python.exe + +.. tab:: Windows (CMD) + + .. code-block:: console + + (myproject) C:\> where.exe python + C:\Users\user\myproject\Scripts\python.exe + +******************* + Install a package +******************* + +With the environment activated, install a package using pip: + +.. code-block:: console + + (myproject) $ pip install requests + Collecting requests + Using cached requests-2.32.3-py3-none-any.whl (64 kB) + Installing collected packages: requests + Successfully installed requests-2.32.3 + +Verify that the package is installed only inside your virtual environment: + +.. code-block:: console + + (myproject) $ python -c "import requests; print(requests.__file__)" + /home/user/myproject/lib/python3.13/site-packages/requests/__init__.py + +The path shows that ``requests`` is installed in the virtual environment, not in your system Python. + +************ + Deactivate +************ + +When you're done working in the virtual environment, deactivate it: + +.. code-block:: console + + (myproject) $ deactivate + $ + +The prompt returns to normal, and Python commands now use your system Python again. + +************************ + Use without activation +************************ + +Activation is a convenience, not a requirement. You can run any executable from the virtual environment directly by +using its full path: + +.. tab:: Linux/macOS + + .. code-block:: console + + $ myproject/bin/python -c "import sys; print(sys.prefix)" + /home/user/myproject + + $ myproject/bin/pip install httpx + +.. tab:: Windows (PowerShell) + + .. code-block:: console + + PS> .\myproject\Scripts\python.exe -c "import sys; print(sys.prefix)" + C:\Users\user\myproject + + PS> .\myproject\Scripts\pip.exe install httpx + +.. tab:: Windows (CMD) + + .. code-block:: console + + C:\> .\myproject\Scripts\python.exe -c "import sys; print(sys.prefix)" + C:\Users\user\myproject + + C:\> .\myproject\Scripts\pip.exe install httpx + +This is especially useful in scripts, CI pipelines, and automation where modifying the shell environment is unnecessary. + +*********************** + Set up a real project +*********************** + +Now let's apply what you've learned to a real project workflow: + +.. code-block:: console + + $ mkdir myapp && cd myapp + $ virtualenv venv + $ source venv/bin/activate # or use the appropriate command for your platform + (venv) $ pip install flask requests + (venv) $ pip freeze > requirements.txt + +The ``requirements.txt`` file now contains your project's dependencies: + +.. code-block:: text + + blinker==1.9.0 + certifi==2025.1.31 + charset-normalizer==3.4.1 + click==8.1.8 + flask==3.1.0 + idna==3.10 + itsdangerous==2.2.0 + Jinja2==3.1.5 + MarkupSafe==3.0.2 + requests==2.32.3 + urllib3==2.3.0 + werkzeug==3.1.3 + +This file lets you recreate the exact environment later. Let's test this: + +.. code-block:: console + + (venv) $ deactivate + $ rm -rf venv + $ virtualenv venv + $ source venv/bin/activate + (venv) $ pip install -r requirements.txt + +All packages are reinstalled exactly as before. Here's the complete workflow: + +.. mermaid:: + + graph TD + A[Create virtual environment] --> B[Activate] + B --> C[Install packages] + C --> D[Freeze to requirements.txt] + D --> E[Deactivate & clean up] + E --> F[Recreate virtual environment] + F --> G[Install from requirements.txt] + G --> H[Ready to work] + + style A fill:#2563eb,stroke:#1d4ed8,color:#fff + style B fill:#6366f1,stroke:#4f46e5,color:#fff + style C fill:#6366f1,stroke:#4f46e5,color:#fff + style D fill:#6366f1,stroke:#4f46e5,color:#fff + style E fill:#d97706,stroke:#b45309,color:#fff + style F fill:#6366f1,stroke:#4f46e5,color:#fff + style G fill:#6366f1,stroke:#4f46e5,color:#fff + style H fill:#16a34a,stroke:#15803d,color:#fff + +****************** + What you learned +****************** + +In this tutorial, you learned how to: + +- Create a virtual environment with ``virtualenv``. +- Activate and deactivate virtual environments on different platforms. +- Install packages in isolation from your system Python. +- Save project dependencies with ``pip freeze``. +- Reproduce environments using ``requirements.txt``. + +************ + Next steps +************ + +Now that you understand the basics, explore these topics: + +- :doc:`../how-to/usage` for selecting specific Python versions, configuring defaults, and advanced usage patterns. +- :doc:`../explanation` for understanding how virtualenv works under the hood and how it compares to ``venv``. +- :doc:`../reference/cli` for all available command line options and flags. diff --git a/docs/user_guide.rst b/docs/user_guide.rst deleted file mode 100644 index 82cc235b1..000000000 --- a/docs/user_guide.rst +++ /dev/null @@ -1,311 +0,0 @@ -User Guide -========== - -Quick start ------------ -Create the environment (creates a folder in your current directory) - .. code-block:: console - - virtualenv env_name -In Linux or Mac, activate the new python environment - .. code-block:: console - - source env_name/bin/activate -Or in Windows - .. code-block:: console - - .\env_name\Scripts\activate -Confirm that the env is successfully selected - .. code-block:: console - - which python3 - - -Introduction ------------- - -Virtualenv has one basic command: - -.. code-block:: console - - virtualenv venv - -.. note:: - - When creating a virtual environment, it's recommended to use a specific Python version, for example, by invoking - virtualenv with ``python3.10 -m virtualenv venv``. If you use a generic command like ``python3 -m virtualenv venv``, - the created environment will be linked to ``/usr/bin/python3``. This can be problematic because when a new Python - version is installed on the system, the ``/usr/bin/python3`` symlink will likely be updated to point to the new - version. This will cause the virtual environment to inadvertently use the new Python version, which is often not the - desired behavior. Using a specific version ensures that the virtual environment is tied to that exact version, - providing stability and predictability. - -This will create a python virtual environment of the same version as virtualenv, installed into the subdirectory -``venv``. The command line tool has quite a few of flags that modify the tool's behavior, for a -full list make sure to check out :ref:`cli_flags`. - -The tool works in two phases: - -- **Phase 1** discovers a python interpreter to create a virtual environment from (by default this is the same python - as the one ``virtualenv`` is running from, however we can change this via the :option:`p` option). -- **Phase 2** creates a virtual environment at the specified destination (:option:`dest`), this can be broken down into - four further sub-steps: - - - create a python that matches the target python interpreter from phase 1, - - install (bootstrap) seed packages (one or more of :pypi:`pip`, :pypi:`setuptools`, :pypi:`wheel`) in the created - virtual environment, - - install activation scripts into the binary directory of the virtual environment (these will allow end users to - *activate* the virtual environment from various shells). - - create files that mark the virtual environment as to be ignored by version control systems (currently we support - Git only, as Mercurial, Bazaar or SVN do not support ignore files in subdirectories). This step can be skipped - with the :option:`no-vcs-ignore` option. - - -The python in your new virtualenv is effectively isolated from the python that was used to create it. - -Python discovery ----------------- - -The first thing we need to be able to create a virtual environment is a python interpreter. This will describe to the -tool what type of virtual environment you would like to create, think of it as: version, architecture, implementation. - -``virtualenv`` being a python application has always at least one such available, the one ``virtualenv`` itself is -using, and as such this is the default discovered element. This means that if you install ``virtualenv`` under -python ``3.8``, virtualenv will by default create virtual environments that are also of version ``3.8``. - -Created python virtual environments are usually not self-contained. A complete python packaging is usually made up of -thousands of files, so it's not efficient to install the entire python again into a new folder. Instead virtual -environments are mere shells, that contain little within themselves, and borrow most from the system python (this is what -you installed, when you installed python itself). This does mean that if you upgrade your system python your virtual -environments *might* break, so watch out. The upside of this, referring to the system python, is that creating virtual -environments can be fast. - -Here we'll describe the built-in mechanism (note this can be extended though by plugins). The CLI flag :option:`p` or -:option:`python` allows you to specify a python specifier for what type of virtual environment you would like, the -format is either: - -- a relative/absolute path to a Python interpreter, - -- a specifier identifying the Python implementation, version, architecture in the following format: - - .. code-block:: - - {python implementation name}{version}{architecture} - - We have the following restrictions: - - - the python implementation is all alphabetic characters (``python`` means any implementation, and if is missing it - defaults to ``python``), - - the version is a dot separated version number optionally followed by ``t`` for free-threading, - - the architecture is either ``-64`` or ``-32`` (missing means ``any``). - - For example: - - - ``python3.8.1`` means any python implementation having the version ``3.8.1``, - - ``3`` means any python implementation having the major version ``3``, - - ``3.13t`` means any python implementation having the version ``3.13`` with free threading, - - ``cpython3`` means a ``CPython`` implementation having the version ``3``, - - ``pypy2`` means a python interpreter with the ``PyPy`` implementation and major version ``2``. - - Given the specifier ``virtualenv`` will apply the following strategy to discover/find the system executable: - - - If we're on Windows look into the Windows registry, and check if we see any registered Python implementations that - match the specification. This is in line with expectation laid out inside - `PEP-514 `_ - - If `uv-managed `_ Python installations are available, use the - first one that matches the specification. - - Try to discover a matching python executable within the folders enumerated on the ``PATH`` environment variable. - In this case we'll try to find an executable that has a name roughly similar to the specification (for exact logic, - please see the implementation code). - -.. warning:: - - As detailed above, virtual environments usually just borrow things from the system Python, they don't actually contain - all the data from the system Python. The version of the python executable is hardcoded within the python exe itself. - Therefore, if you upgrade your system Python, your virtual environment will still report the version before the - upgrade, even though now other than the executable all additional content (standard library, binary libs, etc) are - of the new version. - - Barring any major incompatibilities (rarely the case) the virtual environment will continue working, but other than - the content embedded within the python executable it will behave like the upgraded version. If such a virtual - environment python is specified as the target python interpreter, we will create virtual environments that match the - new system Python version, not the version reported by the virtual environment. - -Creators --------- - -These are what actually setup the virtual environment, usually as a reference against the system python. virtualenv -at the moment has two types of virtual environments: - -- ``venv`` - this delegates the creation process towards the ``venv`` module, as described in - `PEP 405 `_. This is only available on Python interpreters having version - ``3.5`` or later, and also has the downside that virtualenv **must** create a process to invoke that module (unless - virtualenv is installed in the system python), which can be an expensive operation (especially true on Windows). - -- ``builtin`` - this means ``virtualenv`` is able to do the creation operation itself (by knowing exactly what files to - create and what system files need to be referenced). The creator with name ``builtin`` is an alias on the first - creator that's of this type (we provide creators for various target environments, that all differ in actual create - operations, such as CPython 2 on Windows, PyPy2 on Windows, CPython3 on Posix, PyPy3 on Posix, and so on; for a full - list see :option:`creator`). - -Seeders -------- -These will install for you some seed packages (one or more of: :pypi:`pip`, :pypi:`setuptools`, :pypi:`wheel`) that -enables you to install additional python packages into the created virtual environment (by invoking pip). Installing -:pypi:`setuptools` is disabled by default on Python 3.12+ environments. :pypi:`wheel` is only installed on Python 3.8, by default. There are two main seed mechanisms available: - -- ``pip`` - this method uses the bundled pip with virtualenv to install the seed packages (note, a new child process - needs to be created to do this, which can be expensive especially on Windows). -- ``app-data`` - this method uses the user application data directory to create install images. These images are needed - to be created only once, and subsequent virtual environments can just link/copy those images into their pure python - library path (the ``site-packages`` folder). This allows all but the first virtual environment creation to be blazing - fast (a ``pip`` mechanism takes usually 98% of the virtualenv creation time, so by creating this install image that - we can just link into the virtual environments install directory we can achieve speedups of shaving the initial - 1 minute and 10 seconds down to just 8 seconds in case of a copy, or ``0.8`` seconds in case symlinks are available - - this is on Windows, Linux/macOS with symlinks this can be as low as ``100ms`` from 3+ seconds). - To override the filesystem location of the seed cache, one can use the - ``VIRTUALENV_OVERRIDE_APP_DATA`` environment variable. - -.. _wheels: - -Wheels -~~~~~~ - -To install a seed package via either ``pip`` or ``app-data`` method virtualenv needs to acquire a wheel of the target -package. These wheels may be acquired from multiple locations as follows: - -- ``virtualenv`` ships out of box with a set of embed ``wheels`` for all three seed packages (:pypi:`pip`, - :pypi:`setuptools`, :pypi:`wheel`). These are packaged together with the virtualenv source files, and only change upon - upgrading virtualenv. Different Python versions require different versions of these, and because virtualenv supports a - wide range of Python versions, the number of embedded wheels out of box is greater than 3. Whenever newer versions of - these embedded packages are released upstream ``virtualenv`` project upgrades them, and does a new release. Therefore, - upgrading virtualenv periodically will also upgrade the version of the seed packages. -- However, end users might not be able to upgrade virtualenv at the same speed as we do new releases. Therefore, a user - might request to upgrade the list of embedded wheels by invoking virtualenv with the :option:`upgrade-embed-wheels` - flag. If the operation is triggered in such a manual way subsequent runs of virtualenv will always use the upgraded - embed wheels. - - The operation can trigger automatically too, as a background process upon invocation of virtualenv, if no such upgrade - has been performed in the last 14 days. It will only start using automatically upgraded wheel if they have been - released for more than 28 days, and the automatic upgrade finished at least an hour ago: - - - the 28 days period should guarantee end users are not pulling in automatically releases that have known bugs within, - - the one hour period after the automatic upgrade finished is implemented so that continuous integration services do - not start using a new embedded versions half way through. - - - The automatic behavior might be disabled via the :option:`no-periodic-update` configuration flag/option. To acquire - the release date of a package virtualenv will perform the following: - - - lookup ``https://pypi.org/pypi//json`` (primary truth source), - - save the date the version was first discovered, and wait until 28 days passed. -- Users can specify a set of local paths containing additional wheels by using the :option:`extra-search-dir` command - line argument flag. - -When searching for a wheel to use virtualenv performs lookup in the following order: - -- embedded wheels, -- upgraded embedded wheels, -- extra search dir. - -Bundled wheels are all three above together. If neither of the locations contain the requested wheel version or -:option:`download` option is set will use ``pip`` download to load the latest version available from the index server. - -.. _distribution_wheels: - -Embed wheels for distributions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Custom distributions often want to use their own set of wheel versions to distribute instead of the one virtualenv -releases on PyPi. The reason for this is trying to keep the system versions of those packages in sync with what -virtualenv uses. In such cases they should patch the module `virtualenv.seed.wheels.embed -`_, making sure to provide the function -``get_embed_wheel`` (which returns the wheel to use given a distribution/python version). The ``BUNDLE_FOLDER``, -``BUNDLE_SUPPORT`` and ``MAX`` variables are needed if they want to use virtualenv's test suite to validate. - -Furthermore, they might want to disable the periodic update by patching the -`virtualenv.seed.embed.base_embed.PERIODIC_UPDATE_ON_BY_DEFAULT -`_ -to ``False``, and letting the system update mechanism to handle this. Note in this case the user might still request an -upgrade of the embedded wheels by invoking virtualenv via :option:`upgrade-embed-wheels`, but no longer happens -automatically, and will not alter the OS provided wheels. - -Activators ----------- -These are activation scripts that will mangle with your shell's settings to ensure that commands from within the python -virtual environment take priority over your system paths. For example, if invoking ``pip`` from your shell returned the -system python's pip before activation, once you do the activation this should refer to the virtual environments ``pip``. -Note, though that all we do is change priority; so, if your virtual environments ``bin``/``Scripts`` folder does not -contain some executable, this will still resolve to the same executable it would have resolved before the activation. - -For a list of shells we provide activators see :option:`activators`. The location of these is right alongside the Python -executables: usually ``Scripts`` folder on Windows, ``bin`` on POSIX. They are called ``activate``, plus an -extension that's specific per activator, with no extension for Bash. You can invoke them, usually by source-ing them. -The source command might vary by shell - e.g. on Bash it’s ``source`` (or ``.``): - -.. code-block:: console - - source venv/bin/activate - -The activate script prepends the virtual environment’s binary folder onto the ``PATH`` environment variable. It’s -really just convenience for doing so, since you could do the same yourself. - -Note that you don't have to activate a virtual environment to use it. You can instead use the full paths to its -executables, rather than relying on your shell to resolve them to your virtual environment. - -Activator scripts also modify your shell prompt to indicate which environment is currently active, by prepending the -environment name (or the name specified by ``--prompt`` when initially creating the environment) in brackets, like -``(venv)``. You can disable this behavior by setting the environment variable ``VIRTUAL_ENV_DISABLE_PROMPT`` to any -value. You can also get the environment name via the environment variable ``VIRTUAL_ENV_PROMPT`` if you want to -customize your prompt, for example. - -The scripts also provision a ``deactivate`` command that will allow you to undo the operation: - -.. code-block:: console - - deactivate - -.. note:: - - If using Powershell, the ``activate`` script is subject to the - `execution policies `_ on the system. By default, Windows - 7 and later, the system's execution policy is set to ``Restricted``, meaning no scripts like the ``activate`` script - are allowed to be executed. - - However, that can't stop us from changing that slightly to allow it to be executed. You may relax the system - execution policy to allow running of local scripts without verifying the code signature using the following: - - .. code-block:: powershell - - Set-ExecutionPolicy RemoteSigned - - Since the ``activate.ps1`` script is generated locally for each virtualenv, it is not considered a remote script and - can then be executed. - -A longer explanation of this can be found within Allison Kaptur's 2013 blog post: `There's no magic: virtualenv -edition `_ explains how virtualenv uses bash and -Python and ``PATH`` and ``PYTHONHOME`` to isolate virtual environments' paths. - -.. _programmatic_api: - -Programmatic API ----------------- - -At the moment ``virtualenv`` offers only CLI level interface. If you want to trigger invocation of Python environments -from within Python you should be using the ``virtualenv.cli_run`` method; this takes an ``args`` argument where you can -pass the options the same way you would from the command line. The run will return a session object containing data -about the created virtual environment. - -.. code-block:: python - - from virtualenv import cli_run - - cli_run(["venv"]) - -.. automodule:: virtualenv - :members: - -.. currentmodule:: virtualenv.run.session - -.. autoclass:: Session - :members: diff --git a/pyproject.toml b/pyproject.toml index dc44456ec..f661e3174 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,9 +53,14 @@ dependencies = [ ] optional-dependencies.docs = [ "furo>=2023.7.26", + "pre-commit-uv>=4.1.4", "proselint>=0.13", "sphinx>=7.1.2,!=7.3", "sphinx-argparse>=0.4", + "sphinx-autodoc-typehints>=3.6.2", + "sphinx-copybutton>=0.5.2", + "sphinx-inline-tabs>=2025.12.21.14", + "sphinxcontrib-mermaid>=2", "sphinxcontrib-towncrier>=0.2.1a0", "towncrier>=23.6", ] @@ -127,9 +132,15 @@ lint.ignore = [ "CPY", # No copyright header "D10", # no docstrings "D40", # no imperative mode for docstrings + "D200", # fits on one line - conflicts with docstrfmt "D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible + "D205", # 1 blank line required between summary and description - conflicts with docstrfmt + "D209", # multi-line docstring closing quotes - conflicts with docstrfmt "D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible + "D213", # multi-line-summary-second-line - conflicts with docstrfmt + "D301", # use r if any backslashes - conflicts with docstrfmt "DOC", # no restructuredtext support + "E501", # line too long - handled by ruff format and docstrfmt "INP001", # ignore implicit namespace packages "ISC001", # conflict with formatter "PLR0914", # Too many local variables @@ -211,3 +222,4 @@ directory = "docs/changelog" title_format = false issue_format = ":issue:`{issue}`" template = "docs/changelog/template.jinja2" +underlines = [ "*", "=", "-" ] diff --git a/src/virtualenv/__main__.py b/src/virtualenv/__main__.py index 49f59da38..c36e5cba6 100644 --- a/src/virtualenv/__main__.py +++ b/src/virtualenv/__main__.py @@ -18,7 +18,7 @@ def run(args=None, options=None, env=None): if args is None: args = sys.argv[1:] try: - session = cli_run(args, options, env) + session = cli_run(args, options, env=env) LOGGER.warning(LogSession(session, start)) except ProcessCallFailedError as exception: print(f"subprocess call failed for {exception.cmd} with code {exception.code}") # noqa: T201 diff --git a/src/virtualenv/activation/activator.py b/src/virtualenv/activation/activator.py index dd404b47c..8fc29c8b6 100644 --- a/src/virtualenv/activation/activator.py +++ b/src/virtualenv/activation/activator.py @@ -2,45 +2,55 @@ import os from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from argparse import ArgumentParser + from pathlib import Path + + from virtualenv.config.cli.parser import VirtualEnvOptions + from virtualenv.create.creator import Creator + from virtualenv.discovery.py_info import PythonInfo class Activator(ABC): """Generates activate script for the virtual environment.""" - def __init__(self, options) -> None: - """ - Create a new activator generator. + def __init__(self, options: VirtualEnvOptions) -> None: + """Create a new activator generator. :param options: the parsed options as defined within :meth:`add_parser_arguments` + """ self.flag_prompt = os.path.basename(os.getcwd()) if options.prompt == "." else options.prompt @classmethod - def supports(cls, interpreter): # noqa: ARG003 - """ - Check if the activation script is supported in the given interpreter. + def supports(cls, interpreter: PythonInfo) -> bool: # noqa: ARG003 + """Check if the activation script is supported in the given interpreter. :param interpreter: the interpreter we need to support - :return: ``True`` if supported, ``False`` otherwise + + :returns: ``True`` if supported, ``False`` otherwise + """ return True @classmethod # noqa: B027 - def add_parser_arguments(cls, parser, interpreter): - """ - Add CLI arguments for this activation script. + def add_parser_arguments(cls, parser: ArgumentParser, interpreter: PythonInfo) -> None: + """Add CLI arguments for this activation script. :param parser: the CLI parser :param interpreter: the interpreter this virtual environment is based of + """ @abstractmethod - def generate(self, creator): - """ - Generate activate script for the given creator. + def generate(self, creator: Creator) -> list[Path]: + """Generate activate script for the given creator. + + :param creator: the creator (based of :class:`virtualenv.create.creator.Creator`) we used to create this virtual + environment - :param creator: the creator (based of :class:`virtualenv.create.creator.Creator`) we used to create this \ - virtual environment """ raise NotImplementedError diff --git a/src/virtualenv/activation/nushell/__init__.py b/src/virtualenv/activation/nushell/__init__.py index d3b312497..4f46431a0 100644 --- a/src/virtualenv/activation/nushell/__init__.py +++ b/src/virtualenv/activation/nushell/__init__.py @@ -9,13 +9,12 @@ def templates(self): @staticmethod def quote(string): - """ - Nushell supports raw strings like: r###'this is a string'###. + """Nushell supports raw strings like: r###'this is a string'###. https://github.com/nushell/nushell.github.io/blob/main/book/working_with_strings.md - This method finds the maximum continuous sharps in the string and then - quote it with an extra sharp. + This method finds the maximum continuous sharps in the string and then quote it with an extra sharp. + """ max_sharps = 0 current_sharps = 0 diff --git a/src/virtualenv/activation/powershell/__init__.py b/src/virtualenv/activation/powershell/__init__.py index 8489656cc..bb80a6704 100644 --- a/src/virtualenv/activation/powershell/__init__.py +++ b/src/virtualenv/activation/powershell/__init__.py @@ -9,13 +9,13 @@ def templates(self): @staticmethod def quote(string): - """ - This should satisfy PowerShell quoting rules [1], unless the quoted - string is passed directly to Windows native commands [2]. + """This should satisfy PowerShell quoting rules [1], unless the quoted string is passed directly to Windows native commands [2]. [1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules - [2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters - """ # noqa: D205 + [2]: + https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters + + """ string = string.replace("'", "''") return f"'{string}'" diff --git a/src/virtualenv/activation/python/activate_this.py b/src/virtualenv/activation/python/activate_this.py index 99fcd9aba..69a4b67c8 100644 --- a/src/virtualenv/activation/python/activate_this.py +++ b/src/virtualenv/activation/python/activate_this.py @@ -1,10 +1,9 @@ -""" -Activate virtualenv for current interpreter: +"""Activate virtualenv for current interpreter: -import runpy -runpy.run_path(this_file) +import runpy runpy.run_path(this_file) This can be used when you must use an existing Python interpreter, not the virtualenv bin/python. + """ # noqa: D415 from __future__ import annotations diff --git a/src/virtualenv/activation/via_template.py b/src/virtualenv/activation/via_template.py index 85f932605..2f5e8b6fd 100644 --- a/src/virtualenv/activation/via_template.py +++ b/src/virtualenv/activation/via_template.py @@ -24,11 +24,12 @@ def templates(self): @staticmethod def quote(string): - """ - Quote strings in the activation script. + """Quote strings in the activation script. :param string: the string to quote - :return: quoted string that works in the activation script + + :returns: quoted string that works in the activation script + """ return shlex.quote(string) diff --git a/src/virtualenv/app_data/base.py b/src/virtualenv/app_data/base.py index 2077deebd..b7acaf0be 100644 --- a/src/virtualenv/app_data/base.py +++ b/src/virtualenv/app_data/base.py @@ -4,52 +4,91 @@ from abc import ABC, abstractmethod from contextlib import contextmanager +from typing import TYPE_CHECKING from virtualenv.info import IS_ZIPAPP +if TYPE_CHECKING: + from collections.abc import Generator + from pathlib import Path + class AppData(ABC): """Abstract storage interface for the virtualenv application.""" @abstractmethod - def close(self): + def close(self) -> None: """Called before virtualenv exits.""" @abstractmethod - def reset(self): + def reset(self) -> None: """Called when the user passes in the reset app data.""" @abstractmethod - def py_info(self, path): + def py_info(self, path: Path) -> ContentStore: + """Return a content store for cached interpreter information at the given path. + + :param path: the interpreter executable path + + :returns: a content store for the cached data + + """ raise NotImplementedError @abstractmethod - def py_info_clear(self): + def py_info_clear(self) -> None: + """Clear all cached interpreter information.""" raise NotImplementedError @property - def can_update(self): + def can_update(self) -> bool: + """``True`` if this app data store supports updating cached content.""" raise NotImplementedError @abstractmethod - def embed_update_log(self, distribution, for_py_version): + def embed_update_log(self, distribution: str, for_py_version: str) -> ContentStore: + """Return a content store for the embed update log of a distribution. + + :param distribution: the package name (e.g. ``pip``) + :param for_py_version: the target Python version string + + :returns: a content store for the update log + + """ raise NotImplementedError @property - def house(self): + def house(self) -> Path: + """The root directory of the application data store.""" raise NotImplementedError @property - def transient(self): + def transient(self) -> bool: + """``True`` if this app data store is transient and does not persist across runs.""" raise NotImplementedError @abstractmethod - def wheel_image(self, for_py_version, name): + def wheel_image(self, for_py_version: str, name: str) -> Path: + """Return the path to a cached wheel image. + + :param for_py_version: the target Python version string + :param name: the package name + + :returns: the path to the cached wheel + + """ raise NotImplementedError @contextmanager - def ensure_extracted(self, path, to_folder=None): - """Some paths might be within the zipapp, unzip these to a path on the disk.""" + def ensure_extracted(self, path: Path, to_folder: Path | None = None) -> Generator[Path]: + """Ensure a path is available on disk, extracting from zipapp if needed. + + :param path: the path to ensure is available + :param to_folder: optional target directory for extraction + + :returns: yields the usable path on disk + + """ if IS_ZIPAPP: with self.extract(path, to_folder) as result: yield result @@ -58,36 +97,67 @@ def ensure_extracted(self, path, to_folder=None): @abstractmethod @contextmanager - def extract(self, path, to_folder): + def extract(self, path: Path, to_folder: Path | None) -> Generator[Path]: + """Extract a path from the zipapp to a location on disk. + + :param path: the path to extract + :param to_folder: optional target directory + + :returns: yields the extracted path + + """ raise NotImplementedError @abstractmethod @contextmanager - def locked(self, path): + def locked(self, path: Path) -> Generator[None]: + """Acquire an exclusive lock on the given path. + + :param path: the path to lock + + """ raise NotImplementedError class ContentStore(ABC): + """A store for reading and writing cached content.""" + @abstractmethod - def exists(self): + def exists(self) -> bool: + """Check if the stored content exists. + + :returns: ``True`` if content exists + + """ raise NotImplementedError @abstractmethod - def read(self): + def read(self) -> str: + """Read the stored content. + + :returns: the stored content as a string + + """ raise NotImplementedError @abstractmethod - def write(self, content): + def write(self, content: str) -> None: + """Write content to the store. + + :param content: the content to write + + """ raise NotImplementedError @abstractmethod - def remove(self): + def remove(self) -> None: + """Remove the stored content.""" raise NotImplementedError @abstractmethod @contextmanager - def locked(self): - pass + def locked(self) -> Generator[None]: + """Acquire an exclusive lock on this content store.""" __all__ = [ diff --git a/src/virtualenv/app_data/via_disk_folder.py b/src/virtualenv/app_data/via_disk_folder.py index 0d020259f..e768749d0 100644 --- a/src/virtualenv/app_data/via_disk_folder.py +++ b/src/virtualenv/app_data/via_disk_folder.py @@ -1,25 +1,27 @@ -""" -A rough layout of the current storage goes as: - -virtualenv-app-data -├── py - -│ └── *.json/lock -├── wheel -│ ├── house -│ │ └── *.whl -│ └── -> 3.9 -│ ├── img- -│ │ └── image -│ │ └── -> CopyPipInstall / SymlinkPipInstall -│ │ └── -> pip-20.1.1-py2.py3-none-any -│ └── embed -│ └── 3 -> json format versioning -│ └── *.json -> for every distribution contains data about newer embed versions and releases -└─── unzip - └── - ├── py_info.py - ├── debug.py - └── _virtualenv.py +r"""A rough layout of the current storage goes as: + +:: + + virtualenv-app-data + ├── py - + │ └── *.json/lock + ├── wheel + │ ├── house + │ │ └── *.whl + │ └── -> 3.9 + │ ├── img- + │ │ └── image + │ │ └── -> CopyPipInstall / SymlinkPipInstall + │ │ └── -> pip-20.1.1-py2.py3-none-any + │ └── embed + │ └── 3 -> json format versioning + │ └── *.json -> for every distribution contains data about newer embed versions and releases + └─── unzip + └── + ├── py_info.py + ├── debug.py + └── _virtualenv.py + """ # noqa: D415 from __future__ import annotations diff --git a/src/virtualenv/config/cli/parser.py b/src/virtualenv/config/cli/parser.py index 499840fe3..6bbd991f2 100644 --- a/src/virtualenv/config/cli/parser.py +++ b/src/virtualenv/config/cli/parser.py @@ -3,6 +3,7 @@ import os from argparse import SUPPRESS, ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace from collections import OrderedDict +from typing import Any from virtualenv.config.convert import get_type from virtualenv.config.env_var import get_env_var @@ -10,27 +11,46 @@ class VirtualEnvOptions(Namespace): - def __init__(self, **kwargs) -> None: + def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self._src = None - self._sources = {} + self._src: str | None = None + self._sources: dict[str, str] = {} - def set_src(self, key, value, src): + def set_src(self, key: str, value: Any, src: str) -> None: + """Set an option value and record where it came from. + + :param key: the option name + :param value: the option value + :param src: the source of the value (e.g. ``"cli"``, ``"env var"``, ``"default"``) + + """ setattr(self, key, value) if src.startswith("env var"): src = "env var" self._sources[key] = src - def __setattr__(self, key, value) -> None: - if getattr(self, "_src", None) is not None: - self._sources[key] = self._src + def __setattr__(self, key: str, value: Any) -> None: + if (src := getattr(self, "_src", None)) is not None: + self._sources[key] = src super().__setattr__(key, value) - def get_source(self, key): + def get_source(self, key: str) -> str | None: + """Return the source that provided a given option value. + + :param key: the option name + + :returns: the source string (e.g. ``"cli"``, ``"env var"``, ``"default"``), or ``None`` if not tracked + + """ return self._sources.get(key) @property - def verbosity(self): + def verbosity(self) -> int | None: + """The verbosity level, computed as ``verbose - quiet``, clamped to zero. + + :returns: the verbosity level, or ``None`` if neither ``--verbose`` nor ``--quiet`` has been parsed yet + + """ if not hasattr(self, "verbose") and not hasattr(self, "quiet"): return None return max(self.verbose - self.quiet, 0) diff --git a/src/virtualenv/config/convert.py b/src/virtualenv/config/convert.py index ef7581dbd..591b5abb9 100644 --- a/src/virtualenv/config/convert.py +++ b/src/virtualenv/config/convert.py @@ -58,11 +58,10 @@ def convert(self, value, flatten=True): # noqa: ARG002, FBT002 return [self.as_type(i) for i in result] def split_values(self, value): - """ - Split the provided value into a list. + """Split the provided value into a list. + + First this is done by newlines. If there were no newlines in the text, then we next try to split by comma. - First this is done by newlines. If there were no newlines in the text, - then we next try to split by comma. """ if isinstance(value, (str, bytes)): # Use `splitlines` rather than a custom check for whether there is diff --git a/src/virtualenv/config/env_var.py b/src/virtualenv/config/env_var.py index e12723471..c278b6e35 100644 --- a/src/virtualenv/config/env_var.py +++ b/src/virtualenv/config/env_var.py @@ -6,13 +6,14 @@ def get_env_var(key, as_type, env): - """ - Get the environment variable option. + """Get the environment variable option. :param key: the config key requested :param as_type: the type we would like to convert it to :param env: environment variables to use - :return: + + :returns: the converted value and source, or None if not set + """ environ_key = f"VIRTUALENV_{key.upper()}" if env.get(environ_key): diff --git a/src/virtualenv/create/creator.py b/src/virtualenv/create/creator.py index fe9e8fac2..edd481230 100644 --- a/src/virtualenv/create/creator.py +++ b/src/virtualenv/create/creator.py @@ -12,6 +12,13 @@ from pathlib import Path from typing import TYPE_CHECKING +if TYPE_CHECKING: + from argparse import ArgumentParser + + from virtualenv.app_data.base import AppData + from virtualenv.config.cli.parser import VirtualEnvOptions + from virtualenv.discovery.py_info import PythonInfo + from virtualenv.discovery.cached_py_info import LogCmd from virtualenv.util.path import safe_delete from virtualenv.util.subprocess import run_cmd @@ -32,12 +39,12 @@ def __init__(self) -> None: class Creator(ABC): """A class that given a python Interpreter creates a virtual environment.""" - def __init__(self, options, interpreter) -> None: - """ - Construct a new virtual environment creator. + def __init__(self, options: VirtualEnvOptions, interpreter: PythonInfo) -> None: + """Construct a new virtual environment creator. :param options: the CLI option as parsed from :meth:`add_parser_arguments` :param interpreter: the interpreter to create virtual environment from + """ self.interpreter = interpreter self._debug = None @@ -54,11 +61,6 @@ def __init__(self, options, interpreter) -> None: @property def exe(self) -> Path: ... - if TYPE_CHECKING: - - @property - def exe(self) -> Path: ... - def __repr__(self) -> str: return f"{self.__class__.__name__}({', '.join(f'{k}={v}' for k, v in self._args())})" @@ -70,25 +72,32 @@ def _args(self): ] @classmethod - def can_create(cls, interpreter): # noqa: ARG003 - """ - Determine if we can create a virtual environment. + def can_create(cls, interpreter: PythonInfo) -> CreatorMeta | None: # noqa: ARG003 + """Determine if we can create a virtual environment. :param interpreter: the interpreter in question - :return: ``None`` if we can't create, any other object otherwise that will be forwarded to \ - :meth:`add_parser_arguments` + + :returns: ``None`` if we can't create, any other object otherwise that will be forwarded to + :meth:`add_parser_arguments` + """ - return True + return True # type: ignore[return-value] @classmethod - def add_parser_arguments(cls, parser, interpreter, meta, app_data): # noqa: ARG003 - """ - Add CLI arguments for the creator. + def add_parser_arguments( + cls, + parser: ArgumentParser, + interpreter: PythonInfo, # noqa: ARG003 + meta: CreatorMeta, # noqa: ARG003 + app_data: AppData, # noqa: ARG003 + ) -> None: + """Add CLI arguments for the creator. :param parser: the CLI parser :param app_data: the application data folder :param interpreter: the interpreter we're asked to create virtual environment for :param meta: value as returned by :meth:`can_create` + """ parser.add_argument( "dest", @@ -111,7 +120,7 @@ def add_parser_arguments(cls, parser, interpreter, meta, app_data): # noqa: ARG ) @abstractmethod - def create(self): + def create(self) -> None: """Perform the virtual environment creation.""" raise NotImplementedError @@ -214,7 +223,7 @@ def setup_ignore_vcs(self): @property def debug(self): - """:return: debug information about the virtual environment (only valid after :meth:`create` has run)""" + """:returns: debug information about the virtual environment (only valid after :meth:`create` has run)""" if self._debug is None and self.exe is not None: self._debug = get_env_debug_info(self.exe, self.debug_script(), self.app_data, self.env) return self._debug diff --git a/src/virtualenv/create/debug.py b/src/virtualenv/create/debug.py index 3c03e4c62..3672e4576 100644 --- a/src/virtualenv/create/debug.py +++ b/src/virtualenv/create/debug.py @@ -27,7 +27,7 @@ def run(): # noqa: C901 # this is possible if the standard library cannot be accessed OrderedDict = dict # type: ignore[misc] # pragma: no cover # noqa: N806 - result = OrderedDict([("sys", OrderedDict())]) + result: dict = OrderedDict([("sys", OrderedDict())]) path_keys = ( "executable", "_base_executable", diff --git a/src/virtualenv/create/via_global_ref/_virtualenv.py b/src/virtualenv/create/via_global_ref/_virtualenv.py index 0d95b28e0..7850ce321 100644 --- a/src/virtualenv/create/via_global_ref/_virtualenv.py +++ b/src/virtualenv/create/via_global_ref/_virtualenv.py @@ -10,12 +10,11 @@ def patch_dist(dist): - """ - Distutils allows user to configure some arguments via a configuration file: - https://docs.python.org/3/install/index.html#distutils-configuration-files. + """Distutils allows user to configure some arguments via a configuration file: https://docs.python.org/3/install/index.html#distutils-configuration-files. Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up. - """ # noqa: D205 + + """ # we cannot allow some install config as that would get packages installed outside of the virtual environment old_parse_config_files = dist.Distribution.parse_config_files diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py index 7d27be8e8..4b6d1ae95 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py @@ -185,18 +185,16 @@ def _is_pywin32_dll(cls, filename): @classmethod def python_zip(cls, interpreter): + """``python{VERSION}.zip`` contains compiled ``*.pyc`` std lib packages, where ``VERSION`` is ``py_version_nodot`` var from the ``sysconfig`` module. + + See https://docs.python.org/3/using/windows.html#the-embeddable-package, ``discovery.py_info.PythonInfo`` class + (interpreter), and ``python -m sysconfig`` output. + + The embeddable Python distribution for Windows includes ``python{VERSION}.zip`` and ``python{VERSION}._pth`` + files. User can move/rename the zip file and edit ``sys.path`` by editing the ``_pth`` file. Here the + ``pattern`` is used only for the default zip file name. + """ - "python{VERSION}.zip" contains compiled *.pyc std lib packages, where - "VERSION" is `py_version_nodot` var from the `sysconfig` module. - :see: https://docs.python.org/3/using/windows.html#the-embeddable-package - :see: `discovery.py_info.PythonInfo` class (interpreter). - :see: `python -m sysconfig` output. - - :note: The embeddable Python distribution for Windows includes - "python{VERSION}.zip" and "python{VERSION}._pth" files. User can - move/rename *zip* file and edit `sys.path` by editing *_pth* file. - Here the `pattern` is used only for the default *zip* file name! - """ # noqa: D205 pattern = f"*python{interpreter.version_nodot}.zip" matches = fnmatch.filter(interpreter.path, pattern) matched_paths = map(Path, matches) diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py b/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py index 29e0c401f..a8863f676 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/mac_os.py @@ -96,8 +96,7 @@ def reload_code(self): def fix_mach_o(exe, current, new, max_size): - """ - https://en.wikipedia.org/wiki/Mach-O. + """https://en.wikipedia.org/wiki/Mach-O. Mach-O, short for Mach object file format, is a file format for executables, object code, shared libraries, dynamically-loaded code, and core dumps. A replacement for the a.out format, Mach-O offers more extensibility and @@ -113,11 +112,12 @@ def fix_mach_o(exe, current, new, max_size): Lisp. With the introduction of Mac OS X 10.6 platform the Mach-O file underwent a significant modification that causes - binaries compiled on a computer running 10.6 or later to be (by default) executable only on computers running Mac - OS X 10.6 or later. The difference stems from load commands that the dynamic linker, in previous Mac OS X versions, + binaries compiled on a computer running 10.6 or later to be (by default) executable only on computers running Mac OS + X 10.6 or later. The difference stems from load commands that the dynamic linker, in previous Mac OS X versions, does not understand. Another significant change to the Mach-O format is the change in how the Link Edit tables (found in the __LINKEDIT section) function. In 10.6 these new Link Edit tables are compressed by removing unused and unneeded bits of information, however Mac OS X 10.5 and earlier cannot read this new Link Edit table format. + """ try: LOGGER.debug("change Mach-O for %s from %s to %s", exe, current, new) @@ -206,10 +206,7 @@ def read_data(file, endian, num=1): return res def mach_o_change(at_path, what, value): # noqa: C901 - """ - Replace a given name (what) in any LC_LOAD_DYLIB command found in the given binary with a new name (value), - provided it's shorter. - """ # noqa: D205 + """Replace a given name (what) in any LC_LOAD_DYLIB command found in the given binary with a new name (value), provided it's shorter.""" def do_macho(file, bits, endian): # Read Mach-O header (the magic number is assumed read by the caller) diff --git a/src/virtualenv/create/via_global_ref/builtin/ref.py b/src/virtualenv/create/via_global_ref/builtin/ref.py index e2fd45ffe..84669d832 100644 --- a/src/virtualenv/create/via_global_ref/builtin/ref.py +++ b/src/virtualenv/create/via_global_ref/builtin/ref.py @@ -1,8 +1,4 @@ -""" -Virtual environments in the traditional sense are built as reference to the host python. This file allows declarative -references to elements on the file system, allowing our system to automatically detect what modes it can support given -the constraints: e.g. can the file system symlink, can the files be read, executed, etc. -""" # noqa: D205 +"""Virtual environments in the traditional sense are built as reference to the host python. This file allows declarative references to elements on the file system, allowing our system to automatically detect what modes it can support given the constraints: e.g. can the file system symlink, can the files be read, executed, etc.""" from __future__ import annotations diff --git a/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py b/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py index fa01a1fb8..6253bf58e 100644 --- a/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py +++ b/src/virtualenv/create/via_global_ref/builtin/via_global_self_do.py @@ -111,10 +111,7 @@ def ensure_directories(self): return {self.dest, self.bin_dir, self.script_dir, self.stdlib, self.include_dir} | set(self.libs) def set_pyenv_cfg(self): - """ - We directly inject the base prefix and base exec prefix to avoid site.py needing to discover these - from home (which usually is done within the interpreter itself). - """ # noqa: D205 + """We directly inject the base prefix and base exec prefix to avoid site.py needing to discover these from home (which usually is done within the interpreter itself).""" super().set_pyenv_cfg() self.pyenv_cfg["base-prefix"] = self.interpreter.system_prefix self.pyenv_cfg["base-exec-prefix"] = self.interpreter.system_exec_prefix diff --git a/src/virtualenv/create/via_global_ref/venv.py b/src/virtualenv/create/via_global_ref/venv.py index 3b909c085..7921c1fc6 100644 --- a/src/virtualenv/create/via_global_ref/venv.py +++ b/src/virtualenv/create/via_global_ref/venv.py @@ -54,11 +54,7 @@ def create(self): self.executables_for_win_pypy_less_v37() def executables_for_win_pypy_less_v37(self): - """ - PyPy <= 3.6 (v7.3.3) for Windows contains only pypy3.exe and pypy3w.exe - Venv does not handle non-existing exe sources, e.g. python.exe, so this - patch does it. - """ # noqa: D205 + """PyPy <= 3.6 (v7.3.3) for Windows contains only pypy3.exe and pypy3w.exe Venv does not handle non-existing exe sources, e.g. python.exe, so this patch does it.""" creator = self.describe if isinstance(creator, Pypy3Windows) and creator.less_v37: for exe in creator.executables(self.interpreter): diff --git a/src/virtualenv/discovery/builtin.py b/src/virtualenv/discovery/builtin.py index e5c04efe5..f73905200 100644 --- a/src/virtualenv/discovery/builtin.py +++ b/src/virtualenv/discovery/builtin.py @@ -195,7 +195,7 @@ def get_paths(env: Mapping[str, str]) -> Generator[Path, None, None]: path = env.get("PATH", None) if path is None: try: - path = os.confstr("CS_PATH") # ty: ignore[unresolved-attribute] + path = os.confstr("CS_PATH") except (AttributeError, ValueError): path = os.defpath if path: diff --git a/src/virtualenv/discovery/cached_py_info.py b/src/virtualenv/discovery/cached_py_info.py index 26e54a8da..7b203d65c 100644 --- a/src/virtualenv/discovery/cached_py_info.py +++ b/src/virtualenv/discovery/cached_py_info.py @@ -1,9 +1,4 @@ -""" - -We acquire the python information by running an interrogation script via subprocess trigger. This operation is not -cheap, especially not on Windows. To not have to pay this hefty cost every time we apply multiple levels of -caching. -""" # noqa: D205 +"""We acquire the python information by running an interrogation script via subprocess trigger. This operation is not cheap, especially not on Windows. To not have to pay this hefty cost every time we apply multiple levels of caching.""" from __future__ import annotations diff --git a/src/virtualenv/discovery/discover.py b/src/virtualenv/discovery/discover.py index 0aaa17c8e..b518833f6 100644 --- a/src/virtualenv/discovery/discover.py +++ b/src/virtualenv/discovery/discover.py @@ -1,42 +1,49 @@ from __future__ import annotations from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from argparse import ArgumentParser + + from virtualenv.config.cli.parser import VirtualEnvOptions + from virtualenv.discovery.py_info import PythonInfo class Discover(ABC): """Discover and provide the requested Python interpreter.""" @classmethod - def add_parser_arguments(cls, parser): - """ - Add CLI arguments for this discovery mechanisms. + def add_parser_arguments(cls, parser: ArgumentParser) -> None: + """Add CLI arguments for this discovery mechanisms. :param parser: the CLI parser + """ raise NotImplementedError - def __init__(self, options) -> None: - """ - Create a new discovery mechanism. + def __init__(self, options: VirtualEnvOptions) -> None: + """Create a new discovery mechanism. :param options: the parsed options as defined within :meth:`add_parser_arguments` + """ self._has_run = False - self._interpreter = None + self._interpreter: PythonInfo | None = None self._env = options.env @abstractmethod - def run(self): - """ - Discovers an interpreter. + def run(self) -> PythonInfo | None: + """Discovers an interpreter. + + :returns: the interpreter ready to use for virtual environment creation - :return: the interpreter ready to use for virtual environment creation """ raise NotImplementedError @property - def interpreter(self): - """:return: the interpreter as returned by :meth:`run`, cached""" + def interpreter(self) -> PythonInfo | None: + """:returns: the interpreter as returned by :meth:`run`, cached""" if self._has_run is False: self._interpreter = self.run() self._has_run = True diff --git a/src/virtualenv/discovery/py_info.py b/src/virtualenv/discovery/py_info.py index 2dedb7a26..823e45a80 100644 --- a/src/virtualenv/discovery/py_info.py +++ b/src/virtualenv/discovery/py_info.py @@ -1,7 +1,7 @@ -""" -The PythonInfo contains information about a concrete instance of a Python interpreter. +"""The PythonInfo contains information about a concrete instance of a Python interpreter. Note: this file is also used to query target interpreters, so can only use standard library methods + """ from __future__ import annotations @@ -17,6 +17,14 @@ import warnings from collections import OrderedDict, namedtuple from string import digits +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Mapping + + from virtualenv.app_data.base import AppData + from virtualenv.discovery.py_spec import PythonSpec + from virtualenv.run.plugin.creators import CreatorSelector VersionInfo = namedtuple("VersionInfo", ["major", "minor", "micro", "releaselevel", "serial"]) # noqa: PYI024 LOGGER = logging.getLogger(__name__) @@ -140,10 +148,10 @@ def abs_path(v): @staticmethod def _get_tcl_tk_libs(): - """ - Detects the tcl and tk libraries using tkinter. + """Detects the tcl and tk libraries using tkinter. This works reliably but spins up tkinter, which is heavy if you don't need it. + """ tcl_lib, tk_lib = None, None try: @@ -218,15 +226,15 @@ def _fast_get_system_executable(self): return self._try_posix_fallback_executable(base_executable) def _try_posix_fallback_executable(self, base_executable): - """ - Try to find a versioned Python binary as fallback for POSIX virtual environments. + """Try to find a versioned Python binary as fallback for POSIX virtual environments. + + Python may return "python" because it was invoked from the POSIX virtual environment however some + installs/distributions do not provide a version-less "python" binary in the system install location (see PEP + 394) so try to fallback to a versioned binary. - Python may return "python" because it was invoked from the POSIX virtual environment - however some installs/distributions do not provide a version-less "python" binary in - the system install location (see PEP 394) so try to fallback to a versioned binary. + Gate this to Python 3.11 as `sys._base_executable` path resolution is now relative to the 'home' key from + pyvenv.cfg which often points to the system install location. - Gate this to Python 3.11 as `sys._base_executable` path resolution is now relative to - the 'home' key from pyvenv.cfg which often points to the system install location. """ major, minor = self.version_info.major, self.version_info.minor if self.os != "posix" or (major, minor) < (3, 11): @@ -245,7 +253,14 @@ def _try_posix_fallback_executable(self, base_executable): return None # in this case we just can't tell easily without poking around FS and calling them, bail - def install_path(self, key): + def install_path(self, key: str) -> str: + """Return the relative installation path for a given installation scheme key. + + :param key: the sysconfig scheme key (e.g. ``purelib``, ``headers``) + + :returns: the relative path for the given key + + """ result = self.distutils_install.get(key) if result is None: # use sysconfig if sysconfig_scheme is set or distutils is unavailable # set prefixes to empty => result is relative from cwd @@ -280,27 +295,41 @@ def _distutils_install(): return {key: (getattr(i, f"install_{key}")[1:]).lstrip(os.sep) for key in SCHEME_KEYS} @property - def version_str(self): + def version_str(self) -> str: + """The full version as ``major.minor.micro`` string (e.g. ``3.13.2``).""" return ".".join(str(i) for i in self.version_info[0:3]) @property - def version_release_str(self): + def version_release_str(self) -> str: + """The release version as ``major.minor`` string (e.g. ``3.13``).""" return ".".join(str(i) for i in self.version_info[0:2]) @property - def python_name(self): + def python_name(self) -> str: + """The python executable name as ``pythonX.Y`` (e.g. ``python3.13``).""" version_info = self.version_info return f"python{version_info.major}.{version_info.minor}" @property - def is_old_virtualenv(self): + def is_old_virtualenv(self) -> bool: + """``True`` if this interpreter runs inside an old-style virtualenv (has ``real_prefix``).""" return self.real_prefix is not None @property - def is_venv(self): + def is_venv(self) -> bool: + """``True`` if this interpreter runs inside a PEP 405 venv (has ``base_prefix``).""" return self.base_prefix is not None - def sysconfig_path(self, key, config_var=None, sep=os.sep): + def sysconfig_path(self, key: str, config_var: dict[str, str] | None = None, sep: str = os.sep) -> str: + """Return the sysconfig install path for a scheme key, optionally substituting config variables. + + :param key: the sysconfig path name (e.g. ``stdlib``, ``purelib``) + :param config_var: override config variables for path expansion, or ``None`` to use defaults + :param sep: path separator to use + + :returns: the expanded path string + + """ pattern = self.sysconfig_paths.get(key) if pattern is None: return "" @@ -312,7 +341,14 @@ def sysconfig_path(self, key, config_var=None, sep=os.sep): config_var = base return pattern.format(**config_var).replace("/", sep) - def creators(self, refresh=False): # noqa: FBT002 + def creators(self, refresh: bool = False) -> CreatorSelector: # noqa: FBT001, FBT002 + """Return the available creators for this interpreter, cached unless ``refresh`` is ``True``. + + :param refresh: if ``True``, re-discover creators instead of using the cache + + :returns: the creator selector for this interpreter + + """ if self._creators is None or refresh is True: from virtualenv.run.plugin.creators import CreatorSelector # noqa: PLC0415 @@ -320,7 +356,8 @@ def creators(self, refresh=False): # noqa: FBT002 return self._creators @property - def system_include(self): + def system_include(self) -> str: + """The path to the system include directory for C headers.""" path = self.sysconfig_path( "include", { @@ -336,11 +373,13 @@ def system_include(self): return path @property - def system_prefix(self): + def system_prefix(self) -> str: + """The prefix of the system Python this interpreter is based on.""" return self.real_prefix or self.base_prefix or self.prefix @property - def system_exec_prefix(self): + def system_exec_prefix(self) -> str: + """The exec prefix of the system Python this interpreter is based on.""" return self.real_prefix or self.base_exec_prefix or self.exec_prefix def __repr__(self) -> str: @@ -378,7 +417,8 @@ def __str__(self) -> str: ) @property - def spec(self): + def spec(self) -> str: + """A specification string identifying this interpreter (e.g. ``CPython3.13.2-64``).""" return "{}{}{}-{}".format( self.implementation, ".".join(str(i) for i in self.version_info), @@ -387,15 +427,26 @@ def spec(self): ) @classmethod - def clear_cache(cls, app_data): - # this method is not used by itself, so here and called functions can import stuff locally + def clear_cache(cls, app_data: AppData) -> None: + """Clear all cached interpreter information from the application data store. + + :param app_data: the application data store to clear + + """ from virtualenv.discovery.cached_py_info import clear # noqa: PLC0415 clear(app_data) cls._cache_exe_discovery.clear() - def satisfies(self, spec, impl_must_match): # noqa: C901, PLR0911, PLR0912 - """Check if a given specification can be satisfied by the this python interpreter instance.""" + def satisfies(self, spec: PythonSpec, impl_must_match: bool) -> bool: # noqa: C901, FBT001, PLR0911, PLR0912 + """Check if a given specification can be satisfied by this python interpreter instance. + + :param spec: the python specification to check against + :param impl_must_match: if ``True``, the implementation (e.g. CPython vs PyPy) must also match + + :returns: ``True`` if this interpreter satisfies the specification + + """ if spec.path: if self.executable == os.path.abspath(spec.path): return True # if the path is a our own executable path we're done @@ -446,23 +497,35 @@ def satisfies(self, spec, impl_must_match): # noqa: C901, PLR0911, PLR0912 _current = None @classmethod - def current(cls, app_data=None): + def current(cls, app_data: AppData | None = None) -> PythonInfo: + """Locate the current host interpreter information. + + This might differ from the running interpreter if the host Python has been upgraded. + + :param app_data: the application data store for caching + + :returns: the current interpreter information + """ - This locates the current host interpreter information. This might be different than what we run into in case - the host python has been upgraded from underneath us. - """ # noqa: D205 if cls._current is None: - cls._current = cls.from_exe(sys.executable, app_data, raise_on_error=True, resolve_to_host=False) + result = cls.from_exe(sys.executable, app_data, raise_on_error=True, resolve_to_host=False) + assert result is not None # raise_on_error=True guarantees non-None # noqa: S101 + cls._current = result return cls._current @classmethod - def current_system(cls, app_data=None) -> PythonInfo: + def current_system(cls, app_data: AppData | None = None) -> PythonInfo: + """Locate the current system interpreter information, resolving through any virtualenv layers. + + :param app_data: the application data store for caching + + :returns: the system interpreter information + """ - This locates the current host interpreter information. This might be different than what we run into in case - the host python has been upgraded from underneath us. - """ # noqa: D205 if cls._current_system is None: - cls._current_system = cls.from_exe(sys.executable, app_data, raise_on_error=True, resolve_to_host=True) + result = cls.from_exe(sys.executable, app_data, raise_on_error=True, resolve_to_host=True) + assert result is not None # raise_on_error=True guarantees non-None # noqa: S101 + cls._current_system = result return cls._current_system def _to_json(self): @@ -479,14 +542,25 @@ def _to_dict(self): @classmethod def from_exe( # noqa: PLR0913 cls, - exe, - app_data=None, - raise_on_error=True, # noqa: FBT002 - ignore_cache=False, # noqa: FBT002 - resolve_to_host=True, # noqa: FBT002 - env=None, - ): - """Given a path to an executable get the python information.""" + exe: str, + app_data: AppData | None = None, + raise_on_error: bool = True, # noqa: FBT001, FBT002 + ignore_cache: bool = False, # noqa: FBT001, FBT002 + resolve_to_host: bool = True, # noqa: FBT001, FBT002 + env: Mapping[str, str] | None = None, + ) -> PythonInfo | None: + """Given a path to an executable, get the python information. + + :param exe: path to the Python executable + :param app_data: the application data store for caching + :param raise_on_error: if ``True``, raise on failure instead of returning ``None`` + :param ignore_cache: if ``True``, bypass the cache and re-query the interpreter + :param resolve_to_host: if ``True``, resolve through virtualenv layers to the host interpreter + :param env: environment variables to use for subprocess calls + + :returns: the interpreter information, or ``None`` if discovery failed and ``raise_on_error`` is ``False`` + + """ # this method is not used by itself, so here and called functions can import stuff locally from virtualenv.discovery.cached_py_info import from_exe # noqa: PLC0415 @@ -536,13 +610,31 @@ def _resolve_to_system(cls, app_data, target): prefixes[prefix] = target target = target.discover_exe(app_data, prefix=prefix, exact=False) if target.executable != target.system_executable: - target = cls.from_exe(target.system_executable, app_data) + resolved = cls.from_exe(target.system_executable, app_data) + if resolved is not None: + target = resolved target.executable = start_executable return target _cache_exe_discovery = {} # noqa: RUF012 - def discover_exe(self, app_data, prefix, exact=True, env=None): # noqa: FBT002 + def discover_exe( + self, + app_data: AppData, + prefix: str, + exact: bool = True, # noqa: FBT001, FBT002 + env: Mapping[str, str] | None = None, + ) -> PythonInfo: + """Discover a matching Python executable under a given prefix directory. + + :param app_data: the application data store for caching + :param prefix: the directory prefix to search under + :param exact: if ``True``, require an exact version match; if ``False``, pick the closest match + :param env: environment variables to use for subprocess calls + + :returns: the discovered interpreter information + + """ key = prefix, exact if key in self._cache_exe_discovery and prefix: LOGGER.debug("discover exe from cache %s - exact %s: %r", prefix, exact, self._cache_exe_discovery[key]) diff --git a/src/virtualenv/discovery/windows/pep514.py b/src/virtualenv/discovery/windows/pep514.py index a75dad36d..14b118003 100644 --- a/src/virtualenv/discovery/windows/pep514.py +++ b/src/virtualenv/discovery/windows/pep514.py @@ -14,7 +14,7 @@ def enum_keys(key): at = 0 while True: try: - yield winreg.EnumKey(key, at) + yield winreg.EnumKey(key, at) # ty: ignore[unresolved-attribute] except OSError: break at += 1 @@ -22,23 +22,23 @@ def enum_keys(key): def get_value(key, value_name): try: - return winreg.QueryValueEx(key, value_name)[0] + return winreg.QueryValueEx(key, value_name)[0] # ty: ignore[unresolved-attribute] except OSError: return None def discover_pythons(): for hive, hive_name, key, flags, default_arch in [ - (winreg.HKEY_CURRENT_USER, "HKEY_CURRENT_USER", r"Software\Python", 0, 64), - (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_64KEY, 64), - (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_32KEY, 32), + (winreg.HKEY_CURRENT_USER, "HKEY_CURRENT_USER", r"Software\Python", 0, 64), # ty: ignore[unresolved-attribute] + (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_64KEY, 64), # ty: ignore[unresolved-attribute] + (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_32KEY, 32), # ty: ignore[unresolved-attribute] ]: yield from process_set(hive, hive_name, key, flags, default_arch) def process_set(hive, hive_name, key, flags, default_arch): try: - with winreg.OpenKeyEx(hive, key, 0, winreg.KEY_READ | flags) as root_key: + with winreg.OpenKeyEx(hive, key, 0, winreg.KEY_READ | flags) as root_key: # ty: ignore[unresolved-attribute] for company in enum_keys(root_key): if company == "PyLauncher": # reserved continue @@ -48,7 +48,7 @@ def process_set(hive, hive_name, key, flags, default_arch): def process_company(hive_name, company, root_key, default_arch): - with winreg.OpenKeyEx(root_key, company) as company_key: + with winreg.OpenKeyEx(root_key, company) as company_key: # ty: ignore[unresolved-attribute] for tag in enum_keys(company_key): spec = process_tag(hive_name, company, company_key, tag, default_arch) if spec is not None: @@ -56,7 +56,7 @@ def process_company(hive_name, company, root_key, default_arch): def process_tag(hive_name, company, company_key, tag, default_arch): - with winreg.OpenKeyEx(company_key, tag) as tag_key: + with winreg.OpenKeyEx(company_key, tag) as tag_key: # ty: ignore[unresolved-attribute] version = load_version_data(hive_name, company, tag, tag_key) if version is not None: # if failed to get version bail major, minor, _ = version @@ -75,7 +75,7 @@ def process_tag(hive_name, company, company_key, tag, default_arch): def load_exe(hive_name, company, company_key, tag): key_path = f"{hive_name}/{company}/{tag}" try: - with winreg.OpenKeyEx(company_key, rf"{tag}\InstallPath") as ip_key, ip_key: + with winreg.OpenKeyEx(company_key, rf"{tag}\InstallPath") as ip_key, ip_key: # ty: ignore[unresolved-attribute] exe = get_value(ip_key, "ExecutablePath") if exe is None: ip = get_value(ip_key, None) diff --git a/src/virtualenv/run/__init__.py b/src/virtualenv/run/__init__.py index 03190502b..5d981e2fd 100644 --- a/src/virtualenv/run/__init__.py +++ b/src/virtualenv/run/__init__.py @@ -3,9 +3,10 @@ import logging import os from functools import partial +from typing import TYPE_CHECKING from virtualenv.app_data import make_app_data -from virtualenv.config.cli.parser import VirtualEnvConfigParser +from virtualenv.config.cli.parser import VirtualEnvConfigParser, VirtualEnvOptions from virtualenv.report import LEVELS, setup_report from virtualenv.run.session import Session from virtualenv.seed.wheels.periodic_update import manual_upgrade @@ -16,16 +17,26 @@ from .plugin.discovery import get_discover from .plugin.seeders import SeederSelector +if TYPE_CHECKING: + from collections.abc import MutableMapping -def cli_run(args, options=None, setup_logging=True, env=None): # noqa: FBT002 - """ - Create a virtual environment given some command line interface arguments. + +def cli_run( + args: list[str], + options: VirtualEnvOptions | None = None, + setup_logging: bool = True, # noqa: FBT001, FBT002 + env: MutableMapping[str, str] | None = None, +) -> Session: + """Create a virtual environment given some command line interface arguments. :param args: the command line arguments :param options: passing in a ``VirtualEnvOptions`` object allows return of the parsed options :param setup_logging: ``True`` if setup logging handlers, ``False`` to use handlers already registered :param env: environment variables to use - :return: the session object of the creation (its structure for now is experimental and might change on short notice) + + :returns: the session object of the creation (its structure for now is experimental and might change on short + notice) + """ env = os.environ if env is None else env of_session = session_via_cli(args, options, setup_logging, env) @@ -34,17 +45,23 @@ def cli_run(args, options=None, setup_logging=True, env=None): # noqa: FBT002 return of_session -def session_via_cli(args, options=None, setup_logging=True, env=None): # noqa: FBT002 - """ - Create a virtualenv session (same as cli_run, but this does not perform the creation). Use this if you just want to - query what the virtual environment would look like, but not actually create it. +def session_via_cli( + args: list[str], + options: VirtualEnvOptions | None = None, + setup_logging: bool = True, # noqa: FBT001, FBT002 + env: MutableMapping[str, str] | None = None, +) -> Session: + """Create a virtualenv session (same as cli_run, but this does not perform the creation). Use this if you just want to query what the virtual environment would look like, but not actually create it. :param args: the command line arguments :param options: passing in a ``VirtualEnvOptions`` object allows return of the parsed options :param setup_logging: ``True`` if setup logging handlers, ``False`` to use handlers already registered :param env: environment variables to use - :return: the session object of the creation (its structure for now is experimental and might change on short notice) - """ # noqa: D205 + + :returns: the session object of the creation (its structure for now is experimental and might change on short + notice) + + """ env = os.environ if env is None else env parser, elements = build_parser(args, options, setup_logging, env) options = parser.parse_args(args) diff --git a/src/virtualenv/run/session.py b/src/virtualenv/run/session.py index def795328..341af5fa4 100644 --- a/src/virtualenv/run/session.py +++ b/src/virtualenv/run/session.py @@ -2,6 +2,18 @@ import json import logging +from typing import TYPE_CHECKING + +from typing_extensions import Self + +if TYPE_CHECKING: + from types import TracebackType + + from virtualenv.activation.activator import Activator + from virtualenv.app_data.base import AppData + from virtualenv.create.creator import Creator + from virtualenv.discovery.py_info import PythonInfo + from virtualenv.seed.seeder import Seeder LOGGER = logging.getLogger(__name__) @@ -9,7 +21,15 @@ class Session: """Represents a virtual environment creation session.""" - def __init__(self, verbosity, app_data, interpreter, creator, seeder, activators) -> None: # noqa: PLR0913 + def __init__( # noqa: PLR0913 + self, + verbosity: int, + app_data: AppData, + interpreter: PythonInfo, + creator: Creator, + seeder: Seeder, + activators: list[Activator], + ) -> None: self._verbosity = verbosity self._app_data = app_data self._interpreter = interpreter @@ -18,58 +38,63 @@ def __init__(self, verbosity, app_data, interpreter, creator, seeder, activators self._activators = activators @property - def verbosity(self): + def verbosity(self) -> int: """The verbosity of the run.""" return self._verbosity @property - def interpreter(self): + def interpreter(self) -> PythonInfo: """Create a virtual environment based on this reference interpreter.""" return self._interpreter @property - def creator(self): + def creator(self) -> Creator: """The creator used to build the virtual environment (must be compatible with the interpreter).""" return self._creator @property - def seeder(self): + def seeder(self) -> Seeder: """The mechanism used to provide the seed packages (pip, setuptools, wheel).""" return self._seeder @property - def activators(self): + def activators(self) -> list[Activator]: """Activators used to generate activations scripts.""" return self._activators - def run(self): + def run(self) -> None: self._create() self._seed() self._activate() self.creator.pyenv_cfg.write() - def _create(self): + def _create(self) -> None: LOGGER.info("create virtual environment via %s", self.creator) self.creator.run() LOGGER.debug(_DEBUG_MARKER) LOGGER.debug("%s", _Debug(self.creator)) - def _seed(self): + def _seed(self) -> None: if self.seeder is not None and self.seeder.enabled: LOGGER.info("add seed packages via %s", self.seeder) self.seeder.run(self.creator) - def _activate(self): + def _activate(self) -> None: if self.activators: active = ", ".join(type(i).__name__.replace("Activator", "") for i in self.activators) LOGGER.info("add activators for %s", active) for activator in self.activators: activator.generate(self.creator) - def __enter__(self): + def __enter__(self) -> Self: return self - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: self._app_data.close() diff --git a/src/virtualenv/seed/seeder.py b/src/virtualenv/seed/seeder.py index 58fd8f416..ee6f0498d 100644 --- a/src/virtualenv/seed/seeder.py +++ b/src/virtualenv/seed/seeder.py @@ -1,39 +1,48 @@ from __future__ import annotations from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from argparse import ArgumentParser + + from virtualenv.app_data.base import AppData + from virtualenv.config.cli.parser import VirtualEnvOptions + from virtualenv.create.creator import Creator + from virtualenv.discovery.py_info import PythonInfo class Seeder(ABC): """A seeder will install some seed packages into a virtual environment.""" - def __init__(self, options, enabled) -> None: - """ - Create. + def __init__(self, options: VirtualEnvOptions, enabled: bool) -> None: # noqa: FBT001 + """Create. :param options: the parsed options as defined within :meth:`add_parser_arguments` :param enabled: a flag weather the seeder is enabled or not + """ self.enabled = enabled self.env = options.env @classmethod - def add_parser_arguments(cls, parser, interpreter, app_data): - """ - Add CLI arguments for this seed mechanisms. + def add_parser_arguments(cls, parser: ArgumentParser, interpreter: PythonInfo, app_data: AppData) -> None: + """Add CLI arguments for this seed mechanisms. :param parser: the CLI parser :param app_data: the CLI parser :param interpreter: the interpreter this virtual environment is based of + """ raise NotImplementedError @abstractmethod - def run(self, creator): - """ - Perform the seed operation. + def run(self, creator: Creator) -> None: + """Perform the seed operation. + + :param creator: the creator (based of :class:`virtualenv.create.creator.Creator`) we used to create this virtual + environment - :param creator: the creator (based of :class:`virtualenv.create.creator.Creator`) we used to create this \ - virtual environment """ raise NotImplementedError diff --git a/src/virtualenv/util/path/_win.py b/src/virtualenv/util/path/_win.py index 6404cda64..a70bd2505 100644 --- a/src/virtualenv/util/path/_win.py +++ b/src/virtualenv/util/path/_win.py @@ -6,7 +6,7 @@ def get_short_path_name(long_name): import ctypes # noqa: PLC0415 from ctypes import wintypes # noqa: PLC0415 - GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW # noqa: N806 + GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW # noqa: N806 # ty: ignore[unresolved-attribute] GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD] GetShortPathNameW.restype = wintypes.DWORD output_buf_size = 0 diff --git a/tests/conftest.py b/tests/conftest.py index e4fc28479..bfac5e2ff 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -198,9 +198,7 @@ def _check_os_environ_stable(): @pytest.fixture(autouse=True) def coverage_env(monkeypatch, link, request): - """ - Enable coverage report collection on the created virtual environments by injecting the coverage project - """ + """Enable coverage report collection on the created virtual environments by injecting the coverage project""" if COVERAGE_RUN and "_no_coverage" not in request.fixturenames: # we inject right after creation, we cannot collect coverage on site.py - used for helper scripts, such as debug from virtualenv import run # noqa: PLC0415 @@ -326,10 +324,7 @@ def session_app_data(tmp_path_factory): @contextmanager def change_env_var(key, value): - """Temporarily change an environment variable. - :param key: the key of the env var - :param value: the value of the env var - """ + """Temporarily change an environment variable. :param key: the key of the env var :param value: the value of the env var""" already_set = key in os.environ prev_value = os.environ.get(key) os.environ[key] = value diff --git a/tests/integration/test_race_condition_simulation.py b/tests/integration/test_race_condition_simulation.py index 857de9aa5..305a009fb 100644 --- a/tests/integration/test_race_condition_simulation.py +++ b/tests/integration/test_race_condition_simulation.py @@ -9,12 +9,12 @@ def test_race_condition_simulation(tmp_path): """Test that simulates the race condition described in the issue. - This test creates a temporary directory with _virtualenv.py and _virtualenv.pth, - then simulates the scenario where: - - One process imports and uses the _virtualenv module (simulating marimo) - - Another process overwrites the _virtualenv.py file (simulating uv venv) + This test creates a temporary directory with _virtualenv.py and _virtualenv.pth, then simulates the scenario where: + - One process imports and uses the _virtualenv module (simulating marimo) - Another process overwrites the + _virtualenv.py file (simulating uv venv) The test verifies that no NameError is raised for _DISTUTILS_PATCH. + """ # Create the _virtualenv.py file virtualenv_file = tmp_path / "_virtualenv.py" diff --git a/tests/unit/activation/test_batch.py b/tests/unit/activation/test_batch.py index 468bcff91..aae22b0be 100644 --- a/tests/unit/activation/test_batch.py +++ b/tests/unit/activation/test_batch.py @@ -137,12 +137,7 @@ def __init__(self, session) -> None: self.unix_line_ending = False def _get_test_lines(self, activate_script): - """ - Build intermediary script which will be then called. - In the script just activate environment, call echo to get current - echo setting, and then deactivate. This ensures that echo setting - is preserved and no unwanted output appears. - """ + """Build intermediary script which will be then called. In the script just activate environment, call echo to get current echo setting, and then deactivate. This ensures that echo setting is preserved and no unwanted output appears.""" intermediary_script_path = str(tmp_path / "intermediary.bat") activate_script_quoted = self.quote(str(activate_script)) return [ diff --git a/tests/unit/activation/test_powershell.py b/tests/unit/activation/test_powershell.py index c6a4c687c..fc26751a8 100644 --- a/tests/unit/activation/test_powershell.py +++ b/tests/unit/activation/test_powershell.py @@ -118,11 +118,7 @@ def print_prompt(self): return "prompt" def quote(self, s): - """ - Tester will pass strings to native commands on Windows so extra - parsing rules are used. Check `PowerShellActivator.quote` for more - details. - """ + """Tester will pass strings to native commands on Windows so extra parsing rules are used. Check `PowerShellActivator.quote` for more details.""" text = PowerShellActivator.quote(s) return text.replace('"', '""') if sys.platform == "win32" else text diff --git a/tests/unit/create/conftest.py b/tests/unit/create/conftest.py index 58d390c5c..adb810077 100644 --- a/tests/unit/create/conftest.py +++ b/tests/unit/create/conftest.py @@ -1,10 +1,10 @@ -""" -It's possible to use multiple types of host pythons to create virtual environments and all should work: +"""It's possible to use multiple types of host pythons to create virtual environments and all should work: - host installation - invoking from a venv (if Python 3.3+) - invoking from an old style virtualenv (<17.0.0) - invoking from our own venv + """ from __future__ import annotations diff --git a/tests/unit/create/via_global_ref/builtin/testing/path.py b/tests/unit/create/via_global_ref/builtin/testing/path.py index 06ba921c5..d55e2c37e 100644 --- a/tests/unit/create/via_global_ref/builtin/testing/path.py +++ b/tests/unit/create/via_global_ref/builtin/testing/path.py @@ -76,9 +76,7 @@ def iterdir(self): def MetaPathMock(filelist): # noqa: N802 - """ - Metaclass that creates a `PathMock` class with the `filelist` defined. - """ + """Metaclass that creates a `PathMock` class with the `filelist` defined.""" return type("PathMock", (PathMockABC,), {"filelist": filelist}) diff --git a/tests/unit/discovery/py_info/test_py_info.py b/tests/unit/discovery/py_info/test_py_info.py index 680b7f559..02636dab5 100644 --- a/tests/unit/discovery/py_info/test_py_info.py +++ b/tests/unit/discovery/py_info/test_py_info.py @@ -374,10 +374,7 @@ def test_py_info_to_system_raises(session_app_data, mocker, caplog): def _stringify_schemes_dict(schemes_dict): - """ - Since this file has from __future__ import unicode_literals, we manually cast all values of mocked install_schemes - to str() as the original schemes are not unicode on Python 2. - """ + """Since this file has from __future__ import unicode_literals, we manually cast all values of mocked install_schemes to str() as the original schemes are not unicode on Python 2.""" return {str(n): {str(k): str(v) for k, v in s.items()} for n, s in schemes_dict.items()} diff --git a/tests/unit/discovery/test_discovery.py b/tests/unit/discovery/test_discovery.py index ce70a69a9..e1c7075d3 100644 --- a/tests/unit/discovery/test_discovery.py +++ b/tests/unit/discovery/test_discovery.py @@ -245,9 +245,7 @@ def test_discovery_via_path_with_file(tmp_path, monkeypatch): def test_absolute_path_does_not_exist(tmp_path): - """ - Test that virtualenv does not fail when an absolute path that does not exist is provided. - """ + """Test that virtualenv does not fail when an absolute path that does not exist is provided.""" # Create a command that uses an absolute path that does not exist # and a valid python executable. command = [ @@ -275,9 +273,7 @@ def test_absolute_path_does_not_exist(tmp_path): def test_absolute_path_does_not_exist_fails(tmp_path): - """ - Test that virtualenv fails when a single absolute path that does not exist is provided. - """ + """Test that virtualenv fails when a single absolute path that does not exist is provided.""" # Create a command that uses an absolute path that does not exist command = [ sys.executable, diff --git a/tests/unit/discovery/windows/test_windows_pep514.py b/tests/unit/discovery/windows/test_windows_pep514.py index 0498352aa..3f296991c 100644 --- a/tests/unit/discovery/windows/test_windows_pep514.py +++ b/tests/unit/discovery/windows/test_windows_pep514.py @@ -105,7 +105,7 @@ def test_pep514_run(capsys, caplog): ('PythonCore', 3, 9, 64, False, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe', None) ('PythonCore', 3, 9, 64, False, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe', None) ('PythonCore', 3, 9, 64, False, 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python39\\python.exe', None) - """, # noqa: E501 + """, ).strip() assert out.strip() == expected assert not err diff --git a/tests/unit/test_file_limit.py b/tests/unit/test_file_limit.py index e0ab4f826..b6c953a63 100644 --- a/tests/unit/test_file_limit.py +++ b/tests/unit/test_file_limit.py @@ -12,9 +12,7 @@ @pytest.mark.skipif(sys.platform == "win32", reason="resource module not available on Windows") def test_too_many_open_files(tmp_path): - """ - Test that we get a specific error when we have too many open files. - """ + """Test that we get a specific error when we have too many open files.""" import resource # noqa: PLC0415 soft_limit, hard_limit = resource.getrlimit(resource.RLIMIT_NOFILE) diff --git a/tox.ini b/tox.ini index 141111f8e..c2e1357ee 100644 --- a/tox.ini +++ b/tox.ini @@ -76,7 +76,11 @@ commands = description = build documentation extras = docs +set_env = + PYTHONUTF8 = 1 commands = + pre-commit run docstrfmt --all-files + proselint check docs sphinx-build -d "{envtmpdir}/doctree" docs "{toxworkdir}/docs_out" --color -b html {posargs:-W} python -c 'import pathlib; print("documentation available under file://\{0\}".format(pathlib.Path(r"{toxworkdir}") / "docs_out" / "index.html"))'