diff options
author | Bernát Gábor <gaborjbernat@gmail.com> | 2023-04-19 16:05:21 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-19 16:05:21 -0700 |
commit | 04af5026d8eff9ab34cd6f4a47e2f9de4f10a25c (patch) | |
tree | 7a7beb20e885285b6838f75db5c647a4d20317fc | |
parent | cdd7eb129e5a31b5ff6779f00bc7621908962626 (diff) | |
download | virtualenv-04af5026d8eff9ab34cd6f4a47e2f9de4f10a25c.tar.gz |
Drop Python 2 support (#2548)
150 files changed, 415 insertions, 1017 deletions
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 3bddd4a..69470c6 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -27,7 +27,7 @@ jobs: - pypy-3.8 - pypy-3.7 os: - - ubuntu-20.04 + - ubuntu-22.04 - macos-12 - windows-2022 include: @@ -60,10 +60,6 @@ jobs: fetch-depth: 0 - name: Use local virtualenv for tox run: python -m pip install . - - name: Install Python 2 for cross test - uses: actions/setup-python@v4 - with: - python-version: "2.7" - name: Setup brew python for test ${{ matrix.py }} if: startsWith(matrix.py,'brew@') run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 84a7048..80bb905 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,12 +21,6 @@ repos: hooks: - id: pyupgrade args: ["--py37-plus"] - exclude: "^(src/virtualenv/create/via_global_ref/_virtualenv.py|src/virtualenv/create/via_global_ref/builtin/python2/site.py|src/virtualenv/discovery/py_info.py|tasks/__main__zipapp.py)$" - - repo: https://github.com/asottile/pyupgrade - rev: v2.38.4 - hooks: - - id: pyupgrade - files: "^(src/virtualenv/create/via_global_ref/_virtualenv.py|src/virtualenv/create/via_global_ref/builtin/python2/site.py|src/virtualenv/discovery/py_info.py|tasks/__main__zipapp.py)$" - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: diff --git a/docs/changelog/2496.docs.rst b/docs/changelog/2496.docs.rst deleted file mode 100644 index a624e57..0000000 --- a/docs/changelog/2496.docs.rst +++ /dev/null @@ -1 +0,0 @@ -Use ``furo`` theme - by :user:`gaborbernat`. diff --git a/docs/changelog/2548.feature.rst b/docs/changelog/2548.feature.rst new file mode 100644 index 0000000..33d0209 --- /dev/null +++ b/docs/changelog/2548.feature.rst @@ -0,0 +1,2 @@ +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`. diff --git a/docs/conf.py b/docs/conf.py index da7fb16..ca53448 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import subprocess import sys from datetime import date, datetime diff --git a/docs/render_cli.py b/docs/render_cli.py index ca75e41..5e76611 100644 --- a/docs/render_cli.py +++ b/docs/render_cli.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from argparse import SUPPRESS from collections import namedtuple from contextlib import contextmanager diff --git a/pyproject.toml b/pyproject.toml index 8833f6c..92f4b18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,9 +37,9 @@ dynamic = [ ] dependencies = [ "distlib<1,>=0.3.6", - "filelock<4,>=3.4.1", - 'importlib-metadata>=4.8.3; python_version < "3.8"', - "platformdirs<4,>=2.4", + "filelock<4,>=3.11", + 'importlib-metadata>=6.4.1; python_version < "3.8"', + "platformdirs<4,>=3.2", ] optional-dependencies.docs = [ "furo>=2023.3.27", @@ -76,15 +76,9 @@ nushell = "virtualenv.activation.nushell:NushellActivator" powershell = "virtualenv.activation.powershell:PowerShellActivator" python = "virtualenv.activation.python:PythonActivator" [project.entry-points."virtualenv.create"] -cpython2-mac-arm-framework = "virtualenv.create.via_global_ref.builtin.cpython.mac_os:CPython2macOsArmFramework" -cpython2-mac-framework = "virtualenv.create.via_global_ref.builtin.cpython.mac_os:CPython2macOsFramework" -cpython2-posix = "virtualenv.create.via_global_ref.builtin.cpython.cpython2:CPython2Posix" -cpython2-win = "virtualenv.create.via_global_ref.builtin.cpython.cpython2:CPython2Windows" cpython3-mac-framework = "virtualenv.create.via_global_ref.builtin.cpython.mac_os:CPython3macOsFramework" cpython3-posix = "virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Posix" cpython3-win = "virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Windows" -pypy2-posix = "virtualenv.create.via_global_ref.builtin.pypy.pypy2:PyPy2Posix" -pypy2-win = "virtualenv.create.via_global_ref.builtin.pypy.pypy2:Pypy2Windows" pypy3-posix = "virtualenv.create.via_global_ref.builtin.pypy.pypy3:PyPy3Posix" pypy3-win = "virtualenv.create.via_global_ref.builtin.pypy.pypy3:Pypy3Windows" venv = "virtualenv.create.via_global_ref.venv:Venv" @@ -105,6 +99,7 @@ line-length = 120 [tool.isort] profile = "black" known_first_party = ["virtualenv"] +add_imports = ["from __future__ import annotations"] [tool.flake8] max-complexity = 22 diff --git a/src/virtualenv/__init__.py b/src/virtualenv/__init__.py index e40e8b7..cc11e7f 100644 --- a/src/virtualenv/__init__.py +++ b/src/virtualenv/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from .run import cli_run, session_via_cli from .version import __version__ diff --git a/src/virtualenv/__main__.py b/src/virtualenv/__main__.py index c3c5adf..0ff40d8 100644 --- a/src/virtualenv/__main__.py +++ b/src/virtualenv/__main__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import sys diff --git a/src/virtualenv/activation/__init__.py b/src/virtualenv/activation/__init__.py index 99984bc..f6cf756 100644 --- a/src/virtualenv/activation/__init__.py +++ b/src/virtualenv/activation/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from .bash import BashActivator from .batch import BatchActivator from .cshell import CShellActivator diff --git a/src/virtualenv/activation/activator.py b/src/virtualenv/activation/activator.py index 10b1cf7..ef35b17 100644 --- a/src/virtualenv/activation/activator.py +++ b/src/virtualenv/activation/activator.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from abc import ABCMeta, abstractmethod diff --git a/src/virtualenv/activation/bash/__init__.py b/src/virtualenv/activation/bash/__init__.py index 9860b72..0267fe4 100644 --- a/src/virtualenv/activation/bash/__init__.py +++ b/src/virtualenv/activation/bash/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pathlib import Path from ..via_template import ViaTemplateActivator diff --git a/src/virtualenv/activation/batch/__init__.py b/src/virtualenv/activation/batch/__init__.py index 13ba097..e445005 100644 --- a/src/virtualenv/activation/batch/__init__.py +++ b/src/virtualenv/activation/batch/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from ..via_template import ViaTemplateActivator diff --git a/src/virtualenv/activation/cshell/__init__.py b/src/virtualenv/activation/cshell/__init__.py index 20a7a18..9065d99 100644 --- a/src/virtualenv/activation/cshell/__init__.py +++ b/src/virtualenv/activation/cshell/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from ..via_template import ViaTemplateActivator diff --git a/src/virtualenv/activation/fish/__init__.py b/src/virtualenv/activation/fish/__init__.py index 49b5e14..b630450 100644 --- a/src/virtualenv/activation/fish/__init__.py +++ b/src/virtualenv/activation/fish/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from ..via_template import ViaTemplateActivator diff --git a/src/virtualenv/activation/nushell/__init__.py b/src/virtualenv/activation/nushell/__init__.py index 4e2ea77..2691f70 100644 --- a/src/virtualenv/activation/nushell/__init__.py +++ b/src/virtualenv/activation/nushell/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from ..via_template import ViaTemplateActivator diff --git a/src/virtualenv/activation/powershell/__init__.py b/src/virtualenv/activation/powershell/__init__.py index 4e74ecb..f8de975 100644 --- a/src/virtualenv/activation/powershell/__init__.py +++ b/src/virtualenv/activation/powershell/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from ..via_template import ViaTemplateActivator diff --git a/src/virtualenv/activation/python/__init__.py b/src/virtualenv/activation/python/__init__.py index eb83504..28861f9 100644 --- a/src/virtualenv/activation/python/__init__.py +++ b/src/virtualenv/activation/python/__init__.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import os -import sys from collections import OrderedDict from ..via_template import ViaTemplateActivator @@ -12,23 +13,14 @@ class PythonActivator(ViaTemplateActivator): def replacements(self, creator, dest_folder): replacements = super().replacements(creator, dest_folder) lib_folders = OrderedDict((os.path.relpath(str(i), str(dest_folder)), None) for i in creator.libs) - win_py2 = creator.interpreter.platform == "win32" and creator.interpreter.version_info.major == 2 replacements.update( { "__LIB_FOLDERS__": os.pathsep.join(lib_folders.keys()), - "__DECODE_PATH__": ("yes" if win_py2 else ""), + "__DECODE_PATH__": "", }, ) return replacements - @staticmethod - def _repr_unicode(creator, value): - py2 = creator.interpreter.version_info.major == 2 - if py2: # on Python 2 we need to encode this into explicit utf-8, py3 supports unicode literals - start = 2 if sys.version_info[0] == 3 else 1 - value = repr(value.encode("utf-8"))[start:-1] - return value - __all__ = [ "PythonActivator", diff --git a/src/virtualenv/activation/python/activate_this.py b/src/virtualenv/activation/python/activate_this.py index e8eeb84..192c796 100644 --- a/src/virtualenv/activation/python/activate_this.py +++ b/src/virtualenv/activation/python/activate_this.py @@ -4,6 +4,8 @@ Use exec(open(this_file).read(), {'__file__': this_file}). This can be used when you must use an existing Python interpreter, not the virtualenv bin/python. """ +from __future__ import annotations + import os import site import sys diff --git a/src/virtualenv/activation/via_template.py b/src/virtualenv/activation/via_template.py index 069d52e..e8ec164 100644 --- a/src/virtualenv/activation/via_template.py +++ b/src/virtualenv/activation/via_template.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import sys from abc import ABCMeta, abstractmethod diff --git a/src/virtualenv/app_data/__init__.py b/src/virtualenv/app_data/__init__.py index 262ac07..ebff2f3 100644 --- a/src/virtualenv/app_data/__init__.py +++ b/src/virtualenv/app_data/__init__.py @@ -2,6 +2,8 @@ Application data stored by virtualenv. """ +from __future__ import annotations + import logging import os diff --git a/src/virtualenv/app_data/base.py b/src/virtualenv/app_data/base.py index bc28b23..db08931 100644 --- a/src/virtualenv/app_data/base.py +++ b/src/virtualenv/app_data/base.py @@ -2,6 +2,8 @@ Application data stored by virtualenv. """ +from __future__ import annotations + from abc import ABCMeta, abstractmethod from contextlib import contextmanager diff --git a/src/virtualenv/app_data/na.py b/src/virtualenv/app_data/na.py index 784c76b..9b80ef5 100644 --- a/src/virtualenv/app_data/na.py +++ b/src/virtualenv/app_data/na.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from contextlib import contextmanager from .base import AppData, ContentStore diff --git a/src/virtualenv/app_data/read_only.py b/src/virtualenv/app_data/read_only.py index b11f4a6..3cec79a 100644 --- a/src/virtualenv/app_data/read_only.py +++ b/src/virtualenv/app_data/read_only.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os.path from virtualenv.util.lock import NoOpFileLock diff --git a/src/virtualenv/app_data/via_disk_folder.py b/src/virtualenv/app_data/via_disk_folder.py index 6c7f558..7abdd1e 100644 --- a/src/virtualenv/app_data/via_disk_folder.py +++ b/src/virtualenv/app_data/via_disk_folder.py @@ -22,6 +22,8 @@ virtualenv-app-data └── _virtualenv.py """ +from __future__ import annotations + import json import logging from abc import ABCMeta diff --git a/src/virtualenv/app_data/via_tempdir.py b/src/virtualenv/app_data/via_tempdir.py index 7854642..8ae86ec 100644 --- a/src/virtualenv/app_data/via_tempdir.py +++ b/src/virtualenv/app_data/via_tempdir.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging from tempfile import mkdtemp diff --git a/src/virtualenv/config/cli/parser.py b/src/virtualenv/config/cli/parser.py index f68e56e..915257b 100644 --- a/src/virtualenv/config/cli/parser.py +++ b/src/virtualenv/config/cli/parser.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from argparse import SUPPRESS, ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace from collections import OrderedDict diff --git a/src/virtualenv/config/convert.py b/src/virtualenv/config/convert.py index 38d3551..e5f1ae8 100644 --- a/src/virtualenv/config/convert.py +++ b/src/virtualenv/config/convert.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os diff --git a/src/virtualenv/config/env_var.py b/src/virtualenv/config/env_var.py index 5dc0c1d..c6549e2 100644 --- a/src/virtualenv/config/env_var.py +++ b/src/virtualenv/config/env_var.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from .convert import convert diff --git a/src/virtualenv/config/ini.py b/src/virtualenv/config/ini.py index f977fc0..c56665c 100644 --- a/src/virtualenv/config/ini.py +++ b/src/virtualenv/config/ini.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os from configparser import ConfigParser diff --git a/src/virtualenv/create/creator.py b/src/virtualenv/create/creator.py index e561a3f..12d7424 100644 --- a/src/virtualenv/create/creator.py +++ b/src/virtualenv/create/creator.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import logging import os diff --git a/src/virtualenv/create/debug.py b/src/virtualenv/create/debug.py index 583d999..a922fb1 100644 --- a/src/virtualenv/create/debug.py +++ b/src/virtualenv/create/debug.py @@ -1,7 +1,7 @@ """Inspect a target Python interpreter virtual environment wise""" -import sys # built-in +from __future__ import annotations -PYPY2_WIN = hasattr(sys, "pypy_version_info") and sys.platform != "win32" and sys.version_info[0] == 2 +import sys # built-in def encode_path(value): @@ -12,7 +12,7 @@ def encode_path(value): value = repr(value) else: value = repr(type(value)) - if isinstance(value, bytes) and not PYPY2_WIN: + if isinstance(value, bytes): value = value.decode(sys.getfilesystemencoding()) return value diff --git a/src/virtualenv/create/describe.py b/src/virtualenv/create/describe.py index 8575267..e39afe6 100644 --- a/src/virtualenv/create/describe.py +++ b/src/virtualenv/create/describe.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABCMeta from collections import OrderedDict from pathlib import Path @@ -58,7 +60,7 @@ class Describe(metaclass=ABCMeta): def _calc_config_vars(self, to): sys_vars = self.interpreter.sysconfig_vars - return {k: (to if v.startswith(self.interpreter.prefix) else v) for k, v in sys_vars.items()} + return {k: (to if v is not None and v.startswith(self.interpreter.prefix) else v) for k, v in sys_vars.items()} @classmethod def can_describe(cls, interpreter): # noqa: U100 @@ -82,12 +84,6 @@ class Describe(metaclass=ABCMeta): return self.script_dir / f"{name}{self.suffix}" -class Python2Supports(Describe, metaclass=ABCMeta): - @classmethod - def can_describe(cls, interpreter): - return interpreter.version_info.major == 2 and super().can_describe(interpreter) - - class Python3Supports(Describe, metaclass=ABCMeta): @classmethod def can_describe(cls, interpreter): @@ -108,7 +104,6 @@ class WindowsSupports(Describe, metaclass=ABCMeta): __all__ = [ "Describe", - "Python2Supports", "Python3Supports", "PosixSupports", "WindowsSupports", diff --git a/src/virtualenv/create/pyenv_cfg.py b/src/virtualenv/create/pyenv_cfg.py index 9193a28..44919ab 100644 --- a/src/virtualenv/create/pyenv_cfg.py +++ b/src/virtualenv/create/pyenv_cfg.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging from collections import OrderedDict diff --git a/src/virtualenv/create/via_global_ref/_virtualenv.py b/src/virtualenv/create/via_global_ref/_virtualenv.py index faee64c..90c32df 100644 --- a/src/virtualenv/create/via_global_ref/_virtualenv.py +++ b/src/virtualenv/create/via_global_ref/_virtualenv.py @@ -1,5 +1,6 @@ """Patches that are applied at runtime to the virtual environment""" -# -*- coding: utf-8 -*- + +from __future__ import annotations import os import sys @@ -24,7 +25,7 @@ def patch_dist(dist): if "prefix" in install: # the prefix governs where to install the libraries install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix) for base in ("purelib", "platlib", "headers", "scripts", "data"): - key = "install_{}".format(base) + key = f"install_{base}" if key in install: # do not allow global configs to hijack venv paths install.pop(key, None) return result @@ -35,96 +36,67 @@ def patch_dist(dist): # Import hook that patches some modules to ignore configuration values that break package installation in case # of virtual environments. _DISTUTILS_PATCH = "distutils.dist", "setuptools.dist" -if sys.version_info > (3, 4): - # https://docs.python.org/3/library/importlib.html#setting-up-an-importer - - class _Finder: - """A meta path finder that allows patching the imported distutils modules""" - - fullname = None - - # lock[0] is threading.Lock(), but initialized lazily to avoid importing threading very early at startup, - # because there are gevent-based applications that need to be first to import threading by themselves. - # See https://github.com/pypa/virtualenv/issues/1895 for details. - lock = [] - - def find_spec(self, fullname, path, target=None): # noqa: U100 - if fullname in _DISTUTILS_PATCH and self.fullname is None: - # initialize lock[0] lazily - if len(self.lock) == 0: - import threading - - lock = threading.Lock() - # there is possibility that two threads T1 and T2 are simultaneously running into find_spec, - # observing .lock as empty, and further going into hereby initialization. However due to the GIL, - # list.append() operation is atomic and this way only one of the threads will "win" to put the lock - # - that every thread will use - into .lock[0]. - # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe - self.lock.append(lock) - - from functools import partial - from importlib.util import find_spec - - with self.lock[0]: - self.fullname = fullname - try: - spec = find_spec(fullname, path) - if spec is not None: - # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work - is_new_api = hasattr(spec.loader, "exec_module") - func_name = "exec_module" if is_new_api else "load_module" - old = getattr(spec.loader, func_name) - func = self.exec_module if is_new_api else self.load_module - if old is not func: - try: - setattr(spec.loader, func_name, partial(func, old)) - except AttributeError: - pass # C-Extension loaders are r/o such as zipimporter with <python 3.7 - return spec - finally: - self.fullname = None - - @staticmethod - def exec_module(old, module): - old(module) - if module.__name__ in _DISTUTILS_PATCH: - patch_dist(module) - - @staticmethod - def load_module(old, name): - module = old(name) - if module.__name__ in _DISTUTILS_PATCH: - patch_dist(module) - return module - - sys.meta_path.insert(0, _Finder()) -else: - # https://www.python.org/dev/peps/pep-0302/ - from imp import find_module - from pkgutil import ImpImporter, ImpLoader - - class _VirtualenvImporter(object, ImpImporter): - def __init__(self, path=None): - object.__init__(self) - ImpImporter.__init__(self, path) - - def find_module(self, fullname, path=None): - if fullname in _DISTUTILS_PATCH: +# https://docs.python.org/3/library/importlib.html#setting-up-an-importer + + +class _Finder: + """A meta path finder that allows patching the imported distutils modules""" + + fullname = None + + # lock[0] is threading.Lock(), but initialized lazily to avoid importing threading very early at startup, + # because there are gevent-based applications that need to be first to import threading by themselves. + # See https://github.com/pypa/virtualenv/issues/1895 for details. + lock = [] + + def find_spec(self, fullname, path, target=None): # noqa: U100 + if fullname in _DISTUTILS_PATCH and self.fullname is None: + # initialize lock[0] lazily + if len(self.lock) == 0: + import threading + + lock = threading.Lock() + # there is possibility that two threads T1 and T2 are simultaneously running into find_spec, + # observing .lock as empty, and further going into hereby initialization. However due to the GIL, + # list.append() operation is atomic and this way only one of the threads will "win" to put the lock + # - that every thread will use - into .lock[0]. + # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe + self.lock.append(lock) + + from functools import partial + from importlib.util import find_spec + + with self.lock[0]: + self.fullname = fullname try: - return _VirtualenvLoader(fullname, *find_module(fullname.split(".")[-1], path)) - except ImportError: - pass - return None - - class _VirtualenvLoader(object, ImpLoader): - def __init__(self, fullname, file, filename, etc): - object.__init__(self) - ImpLoader.__init__(self, fullname, file, filename, etc) - - def load_module(self, fullname): - module = super(_VirtualenvLoader, self).load_module(fullname) + spec = find_spec(fullname, path) + if spec is not None: + # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work + is_new_api = hasattr(spec.loader, "exec_module") + func_name = "exec_module" if is_new_api else "load_module" + old = getattr(spec.loader, func_name) + func = self.exec_module if is_new_api else self.load_module + if old is not func: + try: + setattr(spec.loader, func_name, partial(func, old)) + except AttributeError: + pass # C-Extension loaders are r/o such as zipimporter with <python 3.7 + return spec + finally: + self.fullname = None + + @staticmethod + def exec_module(old, module): + old(module) + if module.__name__ in _DISTUTILS_PATCH: patch_dist(module) - module.__loader__ = None # distlib fallback - return module - sys.meta_path.append(_VirtualenvImporter()) + @staticmethod + def load_module(old, name): + module = old(name) + if module.__name__ in _DISTUTILS_PATCH: + patch_dist(module) + return module + + +sys.meta_path.insert(0, _Finder()) diff --git a/src/virtualenv/create/via_global_ref/api.py b/src/virtualenv/create/via_global_ref/api.py index 884452e..79d707c 100644 --- a/src/virtualenv/create/via_global_ref/api.py +++ b/src/virtualenv/create/via_global_ref/api.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os from abc import ABCMeta diff --git a/src/virtualenv/create/via_global_ref/builtin/builtin_way.py b/src/virtualenv/create/via_global_ref/builtin/builtin_way.py index e321593..5705a7b 100644 --- a/src/virtualenv/create/via_global_ref/builtin/builtin_way.py +++ b/src/virtualenv/create/via_global_ref/builtin/builtin_way.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABCMeta from virtualenv.create.creator import Creator diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/common.py b/src/virtualenv/create/via_global_ref/builtin/cpython/common.py index b2f7944..f704621 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/common.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/common.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABCMeta from collections import OrderedDict from pathlib import Path @@ -26,8 +28,7 @@ class CPythonPosix(CPython, PosixSupports, metaclass=ABCMeta): host_exe = Path(interpreter.system_executable) major, minor = interpreter.version_info.major, interpreter.version_info.minor targets = OrderedDict((i, None) for i in ["python", f"python{major}", f"python{major}.{minor}", host_exe.name]) - must = RefMust.COPY if interpreter.version_info.major == 2 else RefMust.NA - yield host_exe, list(targets.keys()), must, RefWhen.ANY + yield host_exe, list(targets.keys()), RefMust.NA, RefWhen.ANY class CPythonWindows(CPython, WindowsSupports, metaclass=ABCMeta): @@ -50,9 +51,7 @@ class CPythonWindows(CPython, WindowsSupports, metaclass=ABCMeta): def is_mac_os_framework(interpreter): if interpreter.platform == "darwin": - framework_var = interpreter.sysconfig_vars.get("PYTHONFRAMEWORK") - value = "Python3" if interpreter.version_info.major == 3 else "Python" - return framework_var == value + return interpreter.sysconfig_vars.get("PYTHONFRAMEWORK") == "Python3" return False diff --git a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py deleted file mode 100644 index 9e29234..0000000 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython2.py +++ /dev/null @@ -1,98 +0,0 @@ -import abc -import logging -from pathlib import Path - -from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest - -from ..python2.python2 import Python2 -from .common import CPython, CPythonPosix, CPythonWindows, is_mac_os_framework - - -class CPython2(CPython, Python2, metaclass=abc.ABCMeta): - """Create a CPython version 2 virtual environment""" - - @classmethod - def sources(cls, interpreter): - yield from super().sources(interpreter) - # include folder needed on Python 2 as we don't have pyenv.cfg - host_include_marker = cls.host_include_marker(interpreter) - if host_include_marker.exists(): - yield PathRefToDest(host_include_marker.parent, dest=lambda self, _: self.include) # noqa: U101 - - @classmethod - def needs_stdlib_py_module(cls): - return False - - @classmethod - def host_include_marker(cls, interpreter): - return Path(interpreter.system_include) / "Python.h" - - @property - def include(self): - # the pattern include the distribution name too at the end, remove that via the parent call - return (self.dest / self.interpreter.install_path("headers")).parent - - @classmethod - def modules(cls): - return ["os"] # landmark to set sys.prefix - - def ensure_directories(self): - dirs = super().ensure_directories() - host_include_marker = self.host_include_marker(self.interpreter) - if host_include_marker.exists(): - dirs.add(self.include.parent) - else: - logging.debug("no include folders as can't find include marker %s", host_include_marker) - return dirs - - -class CPython2PosixBase(CPython2, CPythonPosix, metaclass=abc.ABCMeta): - """common to macOs framework builds and other posix CPython2""" - - @classmethod - def sources(cls, interpreter): - yield from super().sources(interpreter) - - # check if the makefile exists and if so make it available under the virtual environment - make_file = Path(interpreter.sysconfig["makefile_filename"]) - if make_file.exists() and str(make_file).startswith(interpreter.prefix): - under_prefix = make_file.relative_to(Path(interpreter.prefix)) - yield PathRefToDest(make_file, dest=lambda self, s: self.dest / under_prefix) # noqa: U100 - - -class CPython2Posix(CPython2PosixBase): - """CPython 2 on POSIX (excluding macOs framework builds)""" - - @classmethod - def can_describe(cls, interpreter): - return is_mac_os_framework(interpreter) is False and super().can_describe(interpreter) - - @classmethod - def sources(cls, interpreter): - yield from super().sources(interpreter) - # landmark for exec_prefix - exec_marker_file, to_path, _ = cls.from_stdlib(cls.mappings(interpreter), "lib-dynload") - yield PathRefToDest(exec_marker_file, dest=to_path) - - -class CPython2Windows(CPython2, CPythonWindows): - """CPython 2 on Windows""" - - @classmethod - def sources(cls, interpreter): - yield from super().sources(interpreter) - py27_dll = Path(interpreter.system_executable).parent / "python27.dll" - if py27_dll.exists(): # this might be global in the Windows folder in which case it's alright to be missing - yield PathRefToDest(py27_dll, dest=cls.to_bin) - - libs = Path(interpreter.system_prefix) / "libs" - if libs.exists(): - yield PathRefToDest(libs, dest=lambda self, s: self.dest / s.name) - - -__all__ = [ - "CPython2", - "CPython2PosixBase", - "CPython2Posix", - "CPython2Windows", -] 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 433dc4a..3d9a5c4 100644 --- a/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py +++ b/src/virtualenv/create/via_global_ref/builtin/cpython/cpython3.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import abc import fnmatch from itertools import chain 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 d23f53b..8fa09aa 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 @@ -1,4 +1,6 @@ """The Apple Framework builds require their own customization""" +from __future__ import annotations + import logging import os import struct @@ -12,10 +14,8 @@ from virtualenv.create.via_global_ref.builtin.ref import ( PathRefToDest, RefMust, ) -from virtualenv.info import IS_MAC_ARM64 from .common import CPython, CPythonPosix, is_mac_os_framework -from .cpython2 import CPython2PosixBase from .cpython3 import CPython3 @@ -57,89 +57,6 @@ class CPythonmacOsFramework(CPython, metaclass=ABCMeta): raise NotImplementedError -class CPython2macOsFramework(CPythonmacOsFramework, CPython2PosixBase): - @classmethod - def can_create(cls, interpreter): - if not IS_MAC_ARM64 and super().can_describe(interpreter): - return super().can_create(interpreter) - return False - - def current_mach_o_image_path(self): - return os.path.join(self.interpreter.prefix, "Python") - - def desired_mach_o_image_path(self): - return "@executable_path/../Python" - - @classmethod - def sources(cls, interpreter): - yield from super().sources(interpreter) - # landmark for exec_prefix - exec_marker_file, to_path, _ = cls.from_stdlib(cls.mappings(interpreter), "lib-dynload") - yield PathRefToDest(exec_marker_file, dest=to_path) - - # add a copy of the host python image - exe = Path(interpreter.prefix) / "Python" - yield PathRefToDest(exe, dest=lambda self, _: self.dest / "Python", must=RefMust.COPY) # noqa: U101 - - # add a symlink to the Resources dir - resources = Path(interpreter.prefix) / "Resources" - yield PathRefToDest(resources, dest=lambda self, _: self.dest / "Resources") # noqa: U101 - - @property - def reload_code(self): - result = super().reload_code - result = dedent( - f""" - # the bundled site.py always adds the global site package if we're on python framework build, escape this - import sysconfig - config = sysconfig.get_config_vars() - before = config["PYTHONFRAMEWORK"] - try: - config["PYTHONFRAMEWORK"] = "" - {result} - finally: - config["PYTHONFRAMEWORK"] = before - """, - ) - return result - - -class CPython2macOsArmFramework(CPython2macOsFramework, CPythonmacOsFramework, CPython2PosixBase): - @classmethod - def can_create(cls, interpreter): - if IS_MAC_ARM64 and super(CPythonmacOsFramework, cls).can_describe(interpreter): - return super(CPythonmacOsFramework, cls).can_create(interpreter) - return False - - def create(self): - super(CPython2macOsFramework, self).create() - self.fix_signature() - - def fix_signature(self): - """ - On Apple M1 machines (arm64 chips), rewriting the python executable invalidates its signature. - In python2 this results in a unusable python exe which just dies. - As a temporary workaround we can codesign the python exe during the creation process. - """ - exe = self.exe - try: - logging.debug("Changing signature of copied python exe %s", exe) - bak_dir = exe.parent / "bk" - # Reset the signing on Darwin since the exe has been modified. - # Note codesign fails on the original exe, it needs to be copied and moved back. - bak_dir.mkdir(parents=True, exist_ok=True) - subprocess.check_call(["cp", str(exe), str(bak_dir)]) - subprocess.check_call(["mv", str(bak_dir / exe.name), str(exe)]) - bak_dir.rmdir() - metadata = "--preserve-metadata=identifier,entitlements,flags,runtime" - cmd = ["codesign", "-s", "-", metadata, "-f", str(exe)] - logging.debug("Changing Signature: %s", cmd) - subprocess.check_call(cmd) - except Exception: - logging.fatal("Could not change MacOS code signing on copied python exe at %s", exe) - raise - - class CPython3macOsFramework(CPythonmacOsFramework, CPython3, CPythonPosix): def current_mach_o_image_path(self): return "@executable_path/../../../../Python3" @@ -342,6 +259,5 @@ def _builtin_change_mach_o(maxint): __all__ = [ "CPythonmacOsFramework", - "CPython2macOsFramework", "CPython3macOsFramework", ] diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/common.py b/src/virtualenv/create/via_global_ref/builtin/pypy/common.py index 17cf733..f695d6e 100644 --- a/src/virtualenv/create/via_global_ref/builtin/pypy/common.py +++ b/src/virtualenv/create/via_global_ref/builtin/pypy/common.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import abc from pathlib import Path @@ -15,8 +17,7 @@ class PyPy(ViaGlobalRefVirtualenvBuiltin, metaclass=abc.ABCMeta): def _executables(cls, interpreter): host = Path(interpreter.system_executable) targets = sorted(f"{name}{PyPy.suffix}" for name in cls.exe_names(interpreter)) - must = RefMust.COPY if interpreter.version_info.major == 2 else RefMust.NA - yield host, targets, must, RefWhen.ANY + yield host, targets, RefMust.NA, RefWhen.ANY @classmethod def executables(cls, interpreter): diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy2.py b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy2.py deleted file mode 100644 index 8031339..0000000 --- a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy2.py +++ /dev/null @@ -1,124 +0,0 @@ -import abc -import logging -import os -from pathlib import Path - -from virtualenv.create.describe import PosixSupports, WindowsSupports -from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest - -from ..python2.python2 import Python2 -from .common import PyPy - - -class PyPy2(PyPy, Python2, metaclass=abc.ABCMeta): - """PyPy 2""" - - @classmethod - def exe_stem(cls): - return "pypy" - - @classmethod - def sources(cls, interpreter): - yield from super().sources(interpreter) - # include folder needed on Python 2 as we don't have pyenv.cfg - host_include_marker = cls.host_include_marker(interpreter) - if host_include_marker.exists(): - yield PathRefToDest(host_include_marker.parent, dest=lambda self, _: self.include) # noqa: U101 - - @classmethod - def needs_stdlib_py_module(cls): - return True - - @classmethod - def host_include_marker(cls, interpreter): - return Path(interpreter.system_include) / "PyPy.h" - - @property - def include(self): - return self.dest / self.interpreter.install_path("headers") - - @classmethod - def modules(cls): - # pypy2 uses some modules before the site.py loads, so we need to include these too - return super().modules() + [ - "os", - "copy_reg", - "genericpath", - "linecache", - "stat", - "UserDict", - "warnings", - ] - - @property - def lib_pypy(self): - return self.dest / "lib_pypy" - - def ensure_directories(self): - dirs = super().ensure_directories() - dirs.add(self.lib_pypy) - host_include_marker = self.host_include_marker(self.interpreter) - if host_include_marker.exists(): - dirs.add(self.include.parent) - else: - logging.debug("no include folders as can't find include marker %s", host_include_marker) - return dirs - - @property - def skip_rewrite(self): - """ - PyPy2 built-in imports are handled by this path entry, don't overwrite to not disable it - see: https://github.com/pypa/virtualenv/issues/1652 - """ - return f'or path.endswith("lib_pypy{os.sep}__extensions__") # PyPy2 built-in import marker' - - -class PyPy2Posix(PyPy2, PosixSupports): - """PyPy 2 on POSIX""" - - @classmethod - def modules(cls): - return super().modules() + ["posixpath"] - - @classmethod - def _shared_libs(cls, python_dir): - return python_dir.glob("libpypy*.*") - - @property - def lib(self): - return self.dest / "lib" - - @classmethod - def sources(cls, interpreter): - yield from super().sources(interpreter) - host_lib = Path(interpreter.system_prefix) / "lib" - if host_lib.exists(): - yield PathRefToDest(host_lib, dest=lambda self, _: self.lib) # noqa: U101 - - -class Pypy2Windows(PyPy2, WindowsSupports): - """PyPy 2 on Windows""" - - @classmethod - def modules(cls): - return super().modules() + ["ntpath"] - - @classmethod - def _shared_libs(cls, python_dir): - # No glob in python2 PathLib - for candidate in ["libpypy-c.dll", "libffi-7.dll", "libffi-8.dll"]: - dll = python_dir / candidate - if dll.exists(): - yield dll - - @classmethod - def sources(cls, interpreter): - yield from super().sources(interpreter) - yield PathRefToDest(Path(interpreter.system_prefix) / "libs", dest=lambda self, s: self.dest / s.name) - - -__all__ = [ - "PyPy2", - "PyPy2Posix", - "Pypy2Windows", -] diff --git a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py index 9db36e8..b06ecea 100644 --- a/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py +++ b/src/virtualenv/create/via_global_ref/builtin/pypy/pypy3.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import abc from pathlib import Path diff --git a/src/virtualenv/create/via_global_ref/builtin/python2/__init__.py b/src/virtualenv/create/via_global_ref/builtin/python2/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/src/virtualenv/create/via_global_ref/builtin/python2/__init__.py +++ /dev/null diff --git a/src/virtualenv/create/via_global_ref/builtin/python2/python2.py b/src/virtualenv/create/via_global_ref/builtin/python2/python2.py deleted file mode 100644 index 3452577..0000000 --- a/src/virtualenv/create/via_global_ref/builtin/python2/python2.py +++ /dev/null @@ -1,104 +0,0 @@ -import abc -import json -import os -from pathlib import Path - -from virtualenv.create.describe import Python2Supports -from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest -from virtualenv.info import IS_ZIPAPP -from virtualenv.util.zipapp import read as read_from_zipapp - -from ..via_global_self_do import ViaGlobalRefVirtualenvBuiltin - -HERE = Path(os.path.abspath(__file__)).parent - - -class Python2(ViaGlobalRefVirtualenvBuiltin, Python2Supports, metaclass=abc.ABCMeta): - def create(self): - """Perform operations needed to make the created environment work on Python 2""" - super().create() - # install a patched site-package, the default Python 2 site.py is not smart enough to understand pyvenv.cfg, - # so we inject a small shim that can do this, the location of this depends where it's on host - sys_std_plat = Path(self.interpreter.system_stdlib_platform) - site_py_in = ( - self.stdlib_platform - if ((sys_std_plat / "site.py").exists() or (sys_std_plat / "site.pyc").exists()) - else self.stdlib - ) - site_py = site_py_in / "site.py" - - custom_site = get_custom_site() - if IS_ZIPAPP: - custom_site_text = read_from_zipapp(custom_site) - else: - custom_site_text = custom_site.read_text(encoding="utf-8") - expected = json.dumps([os.path.relpath(str(i), str(site_py)) for i in self.libs]) - - custom_site_text = custom_site_text.replace("___EXPECTED_SITE_PACKAGES___", expected) - - reload_code = os.linesep.join(f" {i}" for i in self.reload_code.splitlines()).lstrip() - custom_site_text = custom_site_text.replace("# ___RELOAD_CODE___", reload_code) - - skip_rewrite = os.linesep.join(f" {i}" for i in self.skip_rewrite.splitlines()).lstrip() - custom_site_text = custom_site_text.replace("# ___SKIP_REWRITE____", skip_rewrite) - - site_py.write_text(custom_site_text, encoding="utf-8") - - @property - def reload_code(self): - return 'reload(sys.modules["site"]) # noqa # call system site.py to setup import libraries' - - @property - def skip_rewrite(self): - return "" - - @classmethod - def sources(cls, interpreter): - yield from super().sources(interpreter) - # install files needed to run site.py, either from stdlib or stdlib_platform, at least pyc, but both if exists - # if neither exists return the module file to trigger failure - mappings, needs_py_module = ( - cls.mappings(interpreter), - cls.needs_stdlib_py_module(), - ) - for req in cls.modules(): - module_file, to_module, module_exists = cls.from_stdlib(mappings, f"{req}.py") - compiled_file, to_compiled, compiled_exists = cls.from_stdlib(mappings, f"{req}.pyc") - if needs_py_module or module_exists or not compiled_exists: - yield PathRefToDest(module_file, dest=to_module) - if compiled_exists: - yield PathRefToDest(compiled_file, dest=to_compiled) - - @staticmethod - def from_stdlib(mappings, name): - for from_std, to_std in mappings: - src = from_std / name - if src.exists(): - return src, to_std, True - # if not exists, fallback to first in list - return mappings[0][0] / name, mappings[0][1], False - - @classmethod - def mappings(cls, interpreter): - mappings = [(Path(interpreter.system_stdlib_platform), cls.to_stdlib_platform)] - if interpreter.system_stdlib_platform != interpreter.system_stdlib: - mappings.append((Path(interpreter.system_stdlib), cls.to_stdlib)) - return mappings - - def to_stdlib(self, src): - return self.stdlib / src.name - - def to_stdlib_platform(self, src): - return self.stdlib_platform / src.name - - @classmethod - def needs_stdlib_py_module(cls): - raise NotImplementedError - - @classmethod - def modules(cls): - return [] - - -def get_custom_site(): - return HERE / "site.py" diff --git a/src/virtualenv/create/via_global_ref/builtin/python2/site.py b/src/virtualenv/create/via_global_ref/builtin/python2/site.py deleted file mode 100644 index 4decd87..0000000 --- a/src/virtualenv/create/via_global_ref/builtin/python2/site.py +++ /dev/null @@ -1,190 +0,0 @@ -# -*- coding: utf-8 -*- -""" -A simple shim module to fix up things on Python 2 only. - -Note: until we setup correctly the paths we can only import built-ins. -""" -import sys - - -def main(): - """Patch what needed, and invoke the original site.py""" - here = __file__ # the distutils.install patterns will be injected relative to this site.py, save it here - config = read_pyvenv() - sys.real_prefix = sys.base_prefix = config["base-prefix"] - sys.base_exec_prefix = config["base-exec-prefix"] - sys.base_executable = config["base-executable"] - global_site_package_enabled = config.get("include-system-site-packages", False) == "true" - rewrite_standard_library_sys_path() - disable_user_site_package() - load_host_site(here) - if global_site_package_enabled: - add_global_site_package() - rewrite_getsitepackages(here) - - -def load_host_site(here): - """trigger reload of site.py - now it will use the standard library instance that will take care of init""" - # we have a duality here, we generate the platform and pure library path based on what distutils.install specifies - # because this is what pip will be using; the host site.py though may contain it's own pattern for where the - # platform and pure library paths should exist - - # notably on Ubuntu there's a patch for getsitepackages to point to - # - prefix + local/lib/pythonx.y/dist-packages - # - prefix + lib/pythonx.y/dist-packages - # while distutils.install.cmd still points both of these to - # - prefix + lib/python2.7/site-packages - - # to facilitate when the two match, or not we first reload the site.py, now triggering the import of host site.py, - # as this will ensure that initialization code within host site.py runs - - # ___RELOAD_CODE___ - - # and then if the distutils site packages are not on the sys.path we add them via add_site_dir; note we must add - # them by invoking add_site_dir to trigger the processing of pth files - - add_site_dir = sys.modules["site"].addsitedir - for path in get_site_packages_dirs(here): - add_site_dir(path) - - -def get_site_packages_dirs(here): - import json - import os - - site_packages = r""" - ___EXPECTED_SITE_PACKAGES___ - """ - - for path in json.loads(site_packages): - yield os.path.abspath(os.path.join(here, path.encode("utf-8"))) - - -sep = "\\" if sys.platform == "win32" else "/" # no os module here yet - poor mans version - - -def read_pyvenv(): - """read pyvenv.cfg""" - config_file = "{}{}pyvenv.cfg".format(sys.prefix, sep) - with open(config_file) as file_handler: - lines = file_handler.readlines() - config = {} - for line in lines: - try: - split_at = line.index("=") - except ValueError: - continue # ignore bad/empty lines - else: - config[line[:split_at].strip()] = line[split_at + 1 :].strip() - return config - - -def rewrite_standard_library_sys_path(): - """Once this site file is loaded the standard library paths have already been set, fix them up""" - exe, prefix, exec_prefix = get_exe_prefixes(base=False) - base_exe, base_prefix, base_exec = get_exe_prefixes(base=True) - exe_dir = exe[: exe.rfind(sep)] - for at, path in enumerate(sys.path): - path = abs_path(path) # replace old sys prefix path starts with new - skip_rewrite = path == exe_dir # don't fix the current executable location, notably on Windows this gets added - skip_rewrite = skip_rewrite # ___SKIP_REWRITE____ - if not skip_rewrite: - sys.path[at] = map_path(path, base_exe, exe_dir, exec_prefix, base_prefix, prefix, base_exec) - - # the rewrite above may have changed elements from PYTHONPATH, revert these if on - if sys.flags.ignore_environment: - return - import os - - python_paths = [] - if "PYTHONPATH" in os.environ and os.environ["PYTHONPATH"]: - for path in os.environ["PYTHONPATH"].split(os.pathsep): - if path not in python_paths: - python_paths.append(path) - sys.path[: len(python_paths)] = python_paths - - -def get_exe_prefixes(base=False): - return tuple(abs_path(getattr(sys, ("base_" if base else "") + i)) for i in ("executable", "prefix", "exec_prefix")) - - -def abs_path(value): - values, keep = value.split(sep), [] - at = len(values) - 1 - while at >= 0: - if values[at] == "..": - at -= 1 - else: - keep.append(values[at]) - at -= 1 - return sep.join(keep[::-1]) - - -def map_path(path, base_executable, exe_dir, exec_prefix, base_prefix, prefix, base_exec_prefix): - if path_starts_with(path, exe_dir): - # content inside the exe folder needs to remap to original executables folder - orig_exe_folder = base_executable[: base_executable.rfind(sep)] - return "{}{}".format(orig_exe_folder, path[len(exe_dir) :]) - elif path_starts_with(path, prefix): - return "{}{}".format(base_prefix, path[len(prefix) :]) - elif path_starts_with(path, exec_prefix): - return "{}{}".format(base_exec_prefix, path[len(exec_prefix) :]) - return path - - -def path_starts_with(directory, value): - return directory.startswith(value if value[-1] == sep else value + sep) - - -def disable_user_site_package(): - """Flip the switch on enable user site package""" - # sys.flags is a c-extension type, so we cannot monkeypatch it, replace it with a python class to flip it - sys.original_flags = sys.flags - - class Flags(object): - def __init__(self): - self.__dict__ = {key: getattr(sys.flags, key) for key in dir(sys.flags) if not key.startswith("_")} - - sys.flags = Flags() - sys.flags.no_user_site = 1 - - -def add_global_site_package(): - """add the global site package""" - import site - - # add user site package - sys.flags = sys.original_flags # restore original - site.ENABLE_USER_SITE = None # reset user site check - # add the global site package to the path - use new prefix and delegate to site.py - orig_prefixes = None - try: - orig_prefixes = site.PREFIXES - site.PREFIXES = [sys.base_prefix, sys.base_exec_prefix] - site.main() - finally: - site.PREFIXES = orig_prefixes + site.PREFIXES - - -# Debian and it's derivatives patch this function. We undo the damage -def rewrite_getsitepackages(here): - site = sys.modules["site"] - - site_package_dirs = get_site_packages_dirs(here) - orig_getsitepackages = site.getsitepackages - - def getsitepackages(): - sitepackages = orig_getsitepackages() - if sys.prefix not in site.PREFIXES or sys.exec_prefix not in site.PREFIXES: - # Someone messed with the prefixes, so we stop patching - return sitepackages - for path in site_package_dirs: - if path not in sitepackages: - sitepackages.insert(0, path) - - return sitepackages - - site.getsitepackages = getsitepackages - - -main() diff --git a/src/virtualenv/create/via_global_ref/builtin/ref.py b/src/virtualenv/create/via_global_ref/builtin/ref.py index fb1cf2e..19210ad 100644 --- a/src/virtualenv/create/via_global_ref/builtin/ref.py +++ b/src/virtualenv/create/via_global_ref/builtin/ref.py @@ -4,6 +4,8 @@ references to elements on the file system, allowing our system to automatically the constraints: e.g. can the file system symlink, can the files be read, executed, etc. """ +from __future__ import annotations + import os from abc import ABCMeta, abstractmethod from collections import OrderedDict 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 d57235c..ccf4289 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 @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABCMeta from virtualenv.create.via_global_ref.builtin.ref import ( diff --git a/src/virtualenv/create/via_global_ref/store.py b/src/virtualenv/create/via_global_ref/store.py index a9c559c..0169f82 100644 --- a/src/virtualenv/create/via_global_ref/store.py +++ b/src/virtualenv/create/via_global_ref/store.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pathlib import Path diff --git a/src/virtualenv/create/via_global_ref/venv.py b/src/virtualenv/create/via_global_ref/venv.py index 071375a..f0ee844 100644 --- a/src/virtualenv/create/via_global_ref/venv.py +++ b/src/virtualenv/create/via_global_ref/venv.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging from copy import copy @@ -26,7 +28,7 @@ class Venv(ViaGlobalRefApi): def can_create(cls, interpreter): if interpreter.has_venv: meta = ViaGlobalRefMeta() - if interpreter.platform == "win32" and interpreter.version_info.major == 3: + if interpreter.platform == "win32": meta = handle_store_python(meta, interpreter) return meta return None diff --git a/src/virtualenv/discovery/builtin.py b/src/virtualenv/discovery/builtin.py index 40320d3..58a912d 100644 --- a/src/virtualenv/discovery/builtin.py +++ b/src/virtualenv/discovery/builtin.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import sys diff --git a/src/virtualenv/discovery/cached_py_info.py b/src/virtualenv/discovery/cached_py_info.py index 3edaca9..e8cc34c 100644 --- a/src/virtualenv/discovery/cached_py_info.py +++ b/src/virtualenv/discovery/cached_py_info.py @@ -5,6 +5,8 @@ cheap, especially not on Windows. To not have to pay this hefty cost every time caching. """ +from __future__ import annotations + import logging import os import random diff --git a/src/virtualenv/discovery/discover.py b/src/virtualenv/discovery/discover.py index d44758c..28e07dd 100644 --- a/src/virtualenv/discovery/discover.py +++ b/src/virtualenv/discovery/discover.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABCMeta, abstractmethod diff --git a/src/virtualenv/discovery/py_info.py b/src/virtualenv/discovery/py_info.py index ffff7aa..a28c518 100644 --- a/src/virtualenv/discovery/py_info.py +++ b/src/virtualenv/discovery/py_info.py @@ -3,7 +3,8 @@ The PythonInfo contains information about a concrete instance of a Python interp Note: this file is also used to query target interpreters, so can only use standard library methods """ -from __future__ import absolute_import, print_function + +from __future__ import annotations import json import logging @@ -27,44 +28,41 @@ EXTENSIONS = _get_path_extensions() _CONF_VAR_RE = re.compile(r"\{\w+\}") -class PythonInfo(object): +class PythonInfo: """Contains information for a Python interpreter""" def __init__(self): - def u(v): - return v.decode("utf-8") if isinstance(v, bytes) else v - def abs_path(v): return None if v is None else os.path.abspath(v) # unroll relative elements from path (e.g. ..) # qualifies the python - self.platform = u(sys.platform) - self.implementation = u(platform.python_implementation()) + self.platform = sys.platform + self.implementation = platform.python_implementation() if self.implementation == "PyPy": - self.pypy_version_info = tuple(u(i) for i in sys.pypy_version_info) + self.pypy_version_info = tuple(sys.pypy_version_info) # this is a tuple in earlier, struct later, unify to our own named tuple - self.version_info = VersionInfo(*[u(i) for i in sys.version_info]) + self.version_info = VersionInfo(*sys.version_info) self.architecture = 64 if sys.maxsize > 2**32 else 32 # Used to determine some file names. # See `CPython3Windows.python_zip()`. self.version_nodot = sysconfig.get_config_var("py_version_nodot") - self.version = u(sys.version) - self.os = u(os.name) + self.version = sys.version + self.os = os.name # information about the prefix - determines python home - self.prefix = u(abs_path(getattr(sys, "prefix", None))) # prefix we think - self.base_prefix = u(abs_path(getattr(sys, "base_prefix", None))) # venv - self.real_prefix = u(abs_path(getattr(sys, "real_prefix", None))) # old virtualenv + self.prefix = abs_path(getattr(sys, "prefix", None)) # prefix we think + self.base_prefix = abs_path(getattr(sys, "base_prefix", None)) # venv + self.real_prefix = abs_path(getattr(sys, "real_prefix", None)) # old virtualenv # information about the exec prefix - dynamic stdlib modules - self.base_exec_prefix = u(abs_path(getattr(sys, "base_exec_prefix", None))) - self.exec_prefix = u(abs_path(getattr(sys, "exec_prefix", None))) + self.base_exec_prefix = abs_path(getattr(sys, "base_exec_prefix", None)) + self.exec_prefix = abs_path(getattr(sys, "exec_prefix", None)) - self.executable = u(abs_path(sys.executable)) # the executable we were invoked via - self.original_executable = u(abs_path(self.executable)) # the executable as known by the interpreter + self.executable = abs_path(sys.executable) # the executable we were invoked via + self.original_executable = abs_path(self.executable) # the executable as known by the interpreter self.system_executable = self._fast_get_system_executable() # the executable we are based of (if available) try: @@ -73,17 +71,16 @@ class PythonInfo(object): except ImportError: has = False self.has_venv = has - self.path = [u(i) for i in sys.path] - self.file_system_encoding = u(sys.getfilesystemencoding()) - self.stdout_encoding = u(getattr(sys.stdout, "encoding", None)) + self.path = sys.path + self.file_system_encoding = sys.getfilesystemencoding() + self.stdout_encoding = getattr(sys.stdout, "encoding", None) scheme_names = sysconfig.get_scheme_names() if "venv" in scheme_names: self.sysconfig_scheme = "venv" self.sysconfig_paths = { - u(i): u(sysconfig.get_path(i, expand=False, scheme=self.sysconfig_scheme)) - for i in sysconfig.get_path_names() + i: sysconfig.get_path(i, expand=False, scheme=self.sysconfig_scheme) for i in sysconfig.get_path_names() } # we cannot use distutils at all if "venv" exists, distutils don't know it self.distutils_install = {} @@ -99,13 +96,13 @@ class PythonInfo(object): self.distutils_install = {} else: self.sysconfig_scheme = None - self.sysconfig_paths = {u(i): u(sysconfig.get_path(i, expand=False)) for i in sysconfig.get_path_names()} - self.distutils_install = {u(k): u(v) for k, v in self._distutils_install().items()} + self.sysconfig_paths = {i: sysconfig.get_path(i, expand=False) for i in sysconfig.get_path_names()} + self.distutils_install = self._distutils_install().copy() # https://bugs.python.org/issue22199 makefile = getattr(sysconfig, "get_makefile_filename", getattr(sysconfig, "_get_makefile_filename", None)) self.sysconfig = { - u(k): u(v) + k: v for k, v in [ # a list of content to store from sysconfig ("makefile_filename", makefile()), @@ -116,14 +113,15 @@ class PythonInfo(object): config_var_keys = set() for element in self.sysconfig_paths.values(): for k in _CONF_VAR_RE.findall(element): - config_var_keys.add(u(k[1:-1])) + config_var_keys.add(k[1:-1]) config_var_keys.add("PYTHONFRAMEWORK") - self.sysconfig_vars = {u(i): u(sysconfig.get_config_var(i) or "") for i in config_var_keys} - if self.implementation == "PyPy" and sys.version_info.major == 2: - self.sysconfig_vars["implementation_lower"] = "python" + self.sysconfig_vars = {i: sysconfig.get_config_var(i or "") for i in config_var_keys} - confs = {k: (self.system_prefix if v.startswith(self.prefix) else v) for k, v in self.sysconfig_vars.items()} + confs = { + k: (self.system_prefix if v is not None and v.startswith(self.prefix) else v) + for k, v in self.sysconfig_vars.items() + } self.system_stdlib = self.sysconfig_path("stdlib", confs) self.system_stdlib_platform = self.sysconfig_path("platstdlib", confs) self.max_size = getattr(sys, "maxsize", getattr(sys, "maxint", None)) @@ -151,8 +149,7 @@ class PythonInfo(object): # search relative to the directory of sys._base_executable base_dir = os.path.dirname(base_executable) for base_executable in [ - os.path.join(base_dir, exe) - for exe in ("python{}".format(major), "python{}.{}".format(major, minor)) + os.path.join(base_dir, exe) for exe in (f"python{major}", f"python{major}.{minor}") ]: if os.path.exists(base_executable): return base_executable @@ -193,7 +190,7 @@ class PythonInfo(object): i.prefix = os.sep # paths generated are relative to prefix that contains the path sep, this makes it relative i.finalize_options() - result = {key: (getattr(i, "install_{}".format(key))[1:]).lstrip(os.sep) for key in SCHEME_KEYS} + result = {key: (getattr(i, f"install_{key}")[1:]).lstrip(os.sep) for key in SCHEME_KEYS} return result @property @@ -207,7 +204,7 @@ class PythonInfo(object): @property def python_name(self): version_info = self.version_info - return "python{}.{}".format(version_info.major, version_info.minor) + return f"python{version_info.major}.{version_info.minor}" @property def is_old_virtualenv(self): @@ -215,7 +212,7 @@ class PythonInfo(object): @property def is_venv(self): - return self.base_prefix is not None and self.version_info.major == 3 + return self.base_prefix is not None def sysconfig_path(self, key, config_var=None, sep=os.sep): pattern = self.sysconfig_paths[key] @@ -238,7 +235,10 @@ class PythonInfo(object): def system_include(self): path = self.sysconfig_path( "include", - {k: (self.system_prefix if v.startswith(self.prefix) else v) for k, v in self.sysconfig_vars.items()}, + { + k: (self.system_prefix if v is not None and v.startswith(self.prefix) else v) + for k, v in self.sysconfig_vars.items() + }, ) if not os.path.exists(path): # some broken packaging don't respect the sysconfig, fallback to distutils path # the pattern include the distribution name too at the end, remove that via the parent call @@ -257,8 +257,6 @@ class PythonInfo(object): def __unicode__(self): content = repr(self) - if sys.version_info == 2: - content = content.decode("utf-8") return content def __repr__(self): @@ -271,7 +269,7 @@ class PythonInfo(object): content = "{}({})".format( self.__class__.__name__, ", ".join( - "{}={}".format(k, v) + f"{k}={v}" for k, v in ( ("spec", self.spec), ( @@ -292,7 +290,7 @@ class PythonInfo(object): ("exe", self.executable), ("platform", self.platform), ("version", repr(self.version)), - ("encoding_fs_io", "{}-{}".format(self.file_system_encoding, self.stdout_encoding)), + ("encoding_fs_io", f"{self.file_system_encoding}-{self.stdout_encoding}"), ) if k is not None ), @@ -512,7 +510,7 @@ class PythonInfo(object): # following path pattern of the current if base.startswith(self.prefix): relative = base[len(self.prefix) :] - candidate_folder["{}{}".format(inside_folder, relative)] = None + candidate_folder[f"{inside_folder}{relative}"] = None # or at root level candidate_folder[inside_folder] = None @@ -523,9 +521,9 @@ class PythonInfo(object): for name in self._possible_base(): for at in (3, 2, 1, 0): version = ".".join(str(i) for i in self.version_info[:at]) - for arch in ["-{}".format(self.architecture), ""]: + for arch in [f"-{self.architecture}", ""]: for ext in EXTENSIONS: - candidate = "{}{}{}{}".format(name, version, arch, ext) + candidate = f"{name}{version}{arch}{ext}" name_candidate[candidate] = None return list(name_candidate.keys()) diff --git a/src/virtualenv/discovery/py_spec.py b/src/virtualenv/discovery/py_spec.py index 103c7ae..04ff645 100644 --- a/src/virtualenv/discovery/py_spec.py +++ b/src/virtualenv/discovery/py_spec.py @@ -1,5 +1,7 @@ """A Python specification is an abstract requirement definition of an interpreter""" +from __future__ import annotations + import os import re from collections import OrderedDict diff --git a/src/virtualenv/discovery/windows/__init__.py b/src/virtualenv/discovery/windows/__init__.py index 068a187..f2ceb4b 100644 --- a/src/virtualenv/discovery/windows/__init__.py +++ b/src/virtualenv/discovery/windows/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from ..py_info import PythonInfo from ..py_spec import PythonSpec from .pep514 import discover_pythons diff --git a/src/virtualenv/discovery/windows/pep514.py b/src/virtualenv/discovery/windows/pep514.py index beb1d81..ccf3ec1 100644 --- a/src/virtualenv/discovery/windows/pep514.py +++ b/src/virtualenv/discovery/windows/pep514.py @@ -1,5 +1,7 @@ """Implement https://www.python.org/dev/peps/pep-0514/ to discover interpreters - Windows only""" +from __future__ import annotations + import os import re import winreg diff --git a/src/virtualenv/info.py b/src/virtualenv/info.py index a4fc4bf..1562ff2 100644 --- a/src/virtualenv/info.py +++ b/src/virtualenv/info.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import platform diff --git a/src/virtualenv/report.py b/src/virtualenv/report.py index 0236f21..be17216 100644 --- a/src/virtualenv/report.py +++ b/src/virtualenv/report.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import sys diff --git a/src/virtualenv/run/__init__.py b/src/virtualenv/run/__init__.py index 6d22b71..ea2a40c 100644 --- a/src/virtualenv/run/__init__.py +++ b/src/virtualenv/run/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os from functools import partial diff --git a/src/virtualenv/run/plugin/activators.py b/src/virtualenv/run/plugin/activators.py index 320cae7..0d4fd8f 100644 --- a/src/virtualenv/run/plugin/activators.py +++ b/src/virtualenv/run/plugin/activators.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from argparse import ArgumentTypeError from collections import OrderedDict diff --git a/src/virtualenv/run/plugin/base.py b/src/virtualenv/run/plugin/base.py index 3eb8ab3..ddb2369 100644 --- a/src/virtualenv/run/plugin/base.py +++ b/src/virtualenv/run/plugin/base.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from collections import OrderedDict diff --git a/src/virtualenv/run/plugin/creators.py b/src/virtualenv/run/plugin/creators.py index 8953064..fd47db2 100644 --- a/src/virtualenv/run/plugin/creators.py +++ b/src/virtualenv/run/plugin/creators.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from collections import OrderedDict, defaultdict, namedtuple from virtualenv.create.describe import Describe diff --git a/src/virtualenv/run/plugin/discovery.py b/src/virtualenv/run/plugin/discovery.py index 13f39ed..caabc95 100644 --- a/src/virtualenv/run/plugin/discovery.py +++ b/src/virtualenv/run/plugin/discovery.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from .base import PluginLoader diff --git a/src/virtualenv/run/plugin/seeders.py b/src/virtualenv/run/plugin/seeders.py index 1a51d2e..f1cd605 100644 --- a/src/virtualenv/run/plugin/seeders.py +++ b/src/virtualenv/run/plugin/seeders.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from .base import ComponentBuilder diff --git a/src/virtualenv/run/session.py b/src/virtualenv/run/session.py index 2c8821c..15ec597 100644 --- a/src/virtualenv/run/session.py +++ b/src/virtualenv/run/session.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import logging diff --git a/src/virtualenv/seed/embed/base_embed.py b/src/virtualenv/seed/embed/base_embed.py index f29110b..65f2b97 100644 --- a/src/virtualenv/seed/embed/base_embed.py +++ b/src/virtualenv/seed/embed/base_embed.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABCMeta from pathlib import Path diff --git a/src/virtualenv/seed/embed/pip_invoke.py b/src/virtualenv/seed/embed/pip_invoke.py index 2ca9438..032f14d 100644 --- a/src/virtualenv/seed/embed/pip_invoke.py +++ b/src/virtualenv/seed/embed/pip_invoke.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging from contextlib import contextmanager from subprocess import Popen diff --git a/src/virtualenv/seed/embed/via_app_data/pip_install/base.py b/src/virtualenv/seed/embed/via_app_data/pip_install/base.py index 4fa3304..5e87b80 100644 --- a/src/virtualenv/seed/embed/via_app_data/pip_install/base.py +++ b/src/virtualenv/seed/embed/via_app_data/pip_install/base.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import re diff --git a/src/virtualenv/seed/embed/via_app_data/pip_install/copy.py b/src/virtualenv/seed/embed/via_app_data/pip_install/copy.py index f5717e1..b5f01aa 100644 --- a/src/virtualenv/seed/embed/via_app_data/pip_install/copy.py +++ b/src/virtualenv/seed/embed/via_app_data/pip_install/copy.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from pathlib import Path diff --git a/src/virtualenv/seed/embed/via_app_data/pip_install/symlink.py b/src/virtualenv/seed/embed/via_app_data/pip_install/symlink.py index 4695de5..7460fff 100644 --- a/src/virtualenv/seed/embed/via_app_data/pip_install/symlink.py +++ b/src/virtualenv/seed/embed/via_app_data/pip_install/symlink.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from stat import S_IREAD, S_IRGRP, S_IROTH from subprocess import PIPE, Popen diff --git a/src/virtualenv/seed/embed/via_app_data/via_app_data.py b/src/virtualenv/seed/embed/via_app_data/via_app_data.py index f31ecf6..0c36250 100644 --- a/src/virtualenv/seed/embed/via_app_data/via_app_data.py +++ b/src/virtualenv/seed/embed/via_app_data/via_app_data.py @@ -1,5 +1,7 @@ """Bootstrap""" +from __future__ import annotations + import logging import sys import traceback diff --git a/src/virtualenv/seed/seeder.py b/src/virtualenv/seed/seeder.py index d06d1a0..a87c2a3 100644 --- a/src/virtualenv/seed/seeder.py +++ b/src/virtualenv/seed/seeder.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABCMeta, abstractmethod diff --git a/src/virtualenv/seed/wheels/__init__.py b/src/virtualenv/seed/wheels/__init__.py index c563181..94b4cc9 100644 --- a/src/virtualenv/seed/wheels/__init__.py +++ b/src/virtualenv/seed/wheels/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from .acquire import get_wheel, pip_wheel_env_run from .util import Version, Wheel diff --git a/src/virtualenv/seed/wheels/acquire.py b/src/virtualenv/seed/wheels/acquire.py index 21fde34..aaa0eb0 100644 --- a/src/virtualenv/seed/wheels/acquire.py +++ b/src/virtualenv/seed/wheels/acquire.py @@ -1,5 +1,7 @@ """Bootstrap""" +from __future__ import annotations + import logging import sys from operator import eq, lt diff --git a/src/virtualenv/seed/wheels/bundle.py b/src/virtualenv/seed/wheels/bundle.py index 66bbe56..e2c6095 100644 --- a/src/virtualenv/seed/wheels/bundle.py +++ b/src/virtualenv/seed/wheels/bundle.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from ..wheels.embed import get_embed_wheel from .periodic_update import periodic_update from .util import Version, Wheel, discover_wheels diff --git a/src/virtualenv/seed/wheels/embed/__init__.py b/src/virtualenv/seed/wheels/embed/__init__.py index 782051a..8f15d16 100644 --- a/src/virtualenv/seed/wheels/embed/__init__.py +++ b/src/virtualenv/seed/wheels/embed/__init__.py @@ -1,56 +1,43 @@ +from __future__ import annotations + from pathlib import Path from virtualenv.seed.wheels.util import Wheel BUNDLE_FOLDER = Path(__file__).absolute().parent BUNDLE_SUPPORT = { - "3.12": { + "3.7": { "pip": "pip-23.1-py3-none-any.whl", "setuptools": "setuptools-67.6.1-py3-none-any.whl", "wheel": "wheel-0.40.0-py3-none-any.whl", }, - "3.11": { + "3.8": { "pip": "pip-23.1-py3-none-any.whl", "setuptools": "setuptools-67.6.1-py3-none-any.whl", "wheel": "wheel-0.40.0-py3-none-any.whl", }, - "3.10": { + "3.9": { "pip": "pip-23.1-py3-none-any.whl", "setuptools": "setuptools-67.6.1-py3-none-any.whl", "wheel": "wheel-0.40.0-py3-none-any.whl", }, - "3.9": { + "3.10": { "pip": "pip-23.1-py3-none-any.whl", "setuptools": "setuptools-67.6.1-py3-none-any.whl", "wheel": "wheel-0.40.0-py3-none-any.whl", }, - "3.8": { + "3.11": { "pip": "pip-23.1-py3-none-any.whl", "setuptools": "setuptools-67.6.1-py3-none-any.whl", "wheel": "wheel-0.40.0-py3-none-any.whl", }, - "3.7": { + "3.12": { "pip": "pip-23.1-py3-none-any.whl", "setuptools": "setuptools-67.6.1-py3-none-any.whl", "wheel": "wheel-0.40.0-py3-none-any.whl", }, - "3.6": { - "pip": "pip-21.3.1-py3-none-any.whl", - "setuptools": "setuptools-59.6.0-py3-none-any.whl", - "wheel": "wheel-0.37.1-py2.py3-none-any.whl", - }, - "3.5": { - "pip": "pip-20.3.4-py2.py3-none-any.whl", - "setuptools": "setuptools-50.3.2-py3-none-any.whl", - "wheel": "wheel-0.37.1-py2.py3-none-any.whl", - }, - "2.7": { - "pip": "pip-20.3.4-py2.py3-none-any.whl", - "setuptools": "setuptools-44.1.1-py2.py3-none-any.whl", - "wheel": "wheel-0.37.1-py2.py3-none-any.whl", - }, } -MAX = "3.12" +MAX = "3.7" def get_embed_wheel(distribution, for_py_version): diff --git a/src/virtualenv/seed/wheels/embed/pip-20.3.4-py2.py3-none-any.whl b/src/virtualenv/seed/wheels/embed/pip-20.3.4-py2.py3-none-any.whl Binary files differdeleted file mode 100644 index 95de4d7..0000000 --- a/src/virtualenv/seed/wheels/embed/pip-20.3.4-py2.py3-none-any.whl +++ /dev/null diff --git a/src/virtualenv/seed/wheels/embed/pip-21.3.1-py3-none-any.whl b/src/virtualenv/seed/wheels/embed/pip-21.3.1-py3-none-any.whl Binary files differdeleted file mode 100644 index 769bae6..0000000 --- a/src/virtualenv/seed/wheels/embed/pip-21.3.1-py3-none-any.whl +++ /dev/null diff --git a/src/virtualenv/seed/wheels/embed/setuptools-44.1.1-py2.py3-none-any.whl b/src/virtualenv/seed/wheels/embed/setuptools-44.1.1-py2.py3-none-any.whl Binary files differdeleted file mode 100644 index bf28513..0000000 --- a/src/virtualenv/seed/wheels/embed/setuptools-44.1.1-py2.py3-none-any.whl +++ /dev/null diff --git a/src/virtualenv/seed/wheels/embed/setuptools-50.3.2-py3-none-any.whl b/src/virtualenv/seed/wheels/embed/setuptools-50.3.2-py3-none-any.whl Binary files differdeleted file mode 100644 index 56d1bf9..0000000 --- a/src/virtualenv/seed/wheels/embed/setuptools-50.3.2-py3-none-any.whl +++ /dev/null diff --git a/src/virtualenv/seed/wheels/embed/setuptools-59.6.0-py3-none-any.whl b/src/virtualenv/seed/wheels/embed/setuptools-59.6.0-py3-none-any.whl Binary files differdeleted file mode 100644 index 08d7e94..0000000 --- a/src/virtualenv/seed/wheels/embed/setuptools-59.6.0-py3-none-any.whl +++ /dev/null diff --git a/src/virtualenv/seed/wheels/embed/wheel-0.37.1-py2.py3-none-any.whl b/src/virtualenv/seed/wheels/embed/wheel-0.37.1-py2.py3-none-any.whl Binary files differdeleted file mode 100644 index e6c4d95..0000000 --- a/src/virtualenv/seed/wheels/embed/wheel-0.37.1-py2.py3-none-any.whl +++ /dev/null diff --git a/src/virtualenv/seed/wheels/periodic_update.py b/src/virtualenv/seed/wheels/periodic_update.py index 2088c9d..d1767ce 100644 --- a/src/virtualenv/seed/wheels/periodic_update.py +++ b/src/virtualenv/seed/wheels/periodic_update.py @@ -3,6 +3,8 @@ Periodically update bundled versions. """ +from __future__ import annotations + import json import logging import os diff --git a/src/virtualenv/seed/wheels/util.py b/src/virtualenv/seed/wheels/util.py index f09d873..5843575 100644 --- a/src/virtualenv/seed/wheels/util.py +++ b/src/virtualenv/seed/wheels/util.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from operator import attrgetter from zipfile import ZipFile diff --git a/src/virtualenv/util/error.py b/src/virtualenv/util/error.py index 945a25e..5862226 100644 --- a/src/virtualenv/util/error.py +++ b/src/virtualenv/util/error.py @@ -1,6 +1,9 @@ """Errors""" +from __future__ import annotations + + class ProcessCallFailed(RuntimeError): """Failed a process call""" diff --git a/src/virtualenv/util/lock.py b/src/virtualenv/util/lock.py index cf026f4..c0774d1 100644 --- a/src/virtualenv/util/lock.py +++ b/src/virtualenv/util/lock.py @@ -1,5 +1,7 @@ """holds locking functionality that works across processes""" +from __future__ import annotations + import logging import os from abc import ABCMeta, abstractmethod diff --git a/src/virtualenv/util/path/__init__.py b/src/virtualenv/util/path/__init__.py index 39a8db7..dc827f3 100644 --- a/src/virtualenv/util/path/__init__.py +++ b/src/virtualenv/util/path/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from ._permission import make_exe, set_tree from ._sync import copy, copytree, ensure_dir, safe_delete, symlink from ._win import get_short_path_name diff --git a/src/virtualenv/util/path/_permission.py b/src/virtualenv/util/path/_permission.py index ca92314..8dcad0c 100644 --- a/src/virtualenv/util/path/_permission.py +++ b/src/virtualenv/util/path/_permission.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from stat import S_IXGRP, S_IXOTH, S_IXUSR diff --git a/src/virtualenv/util/path/_sync.py b/src/virtualenv/util/path/_sync.py index 604379d..f0e0173 100644 --- a/src/virtualenv/util/path/_sync.py +++ b/src/virtualenv/util/path/_sync.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import shutil diff --git a/src/virtualenv/util/path/_win.py b/src/virtualenv/util/path/_win.py index d83eabb..94eaf65 100644 --- a/src/virtualenv/util/path/_win.py +++ b/src/virtualenv/util/path/_win.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + def get_short_path_name(long_name): """ Gets the short path name of a given long path. diff --git a/src/virtualenv/util/subprocess/__init__.py b/src/virtualenv/util/subprocess/__init__.py index efae569..cb71fa7 100644 --- a/src/virtualenv/util/subprocess/__init__.py +++ b/src/virtualenv/util/subprocess/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import subprocess CREATE_NO_WINDOW = 0x80000000 diff --git a/src/virtualenv/util/zipapp.py b/src/virtualenv/util/zipapp.py index e7578c4..5271ebb 100644 --- a/src/virtualenv/util/zipapp.py +++ b/src/virtualenv/util/zipapp.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import zipfile diff --git a/tasks/__main__zipapp.py b/tasks/__main__zipapp.py index 87d4589..e48620b 100644 --- a/tasks/__main__zipapp.py +++ b/tasks/__main__zipapp.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import os import sys @@ -7,7 +9,7 @@ ABS_HERE = os.path.abspath(os.path.dirname(__file__)) NEW_IMPORT_SYSTEM = sys.version_info[0] == 3 -class VersionPlatformSelect(object): +class VersionPlatformSelect: def __init__(self): self.archive = ABS_HERE self._zip_file = zipfile.ZipFile(ABS_HERE, "r") @@ -20,11 +22,11 @@ class VersionPlatformSelect(object): per_version = json.loads(self.get_data(of_file).decode("utf-8")) all_platforms = per_version[version] if version in per_version else per_version["3.9"] content = all_platforms.get("==any", {}) # start will all platforms - not_us = "!={}".format(sys.platform) + not_us = f"!={sys.platform}" for key, value in all_platforms.items(): # now override that with not platform if key.startswith("!=") and key != not_us: content.update(value) - content.update(all_platforms.get("=={}".format(sys.platform), {})) # and finish it off with our platform + content.update(all_platforms.get(f"=={sys.platform}", {})) # and finish it off with our platform return content def __enter__(self): @@ -61,19 +63,19 @@ class VersionPlatformSelect(object): yield result def __repr__(self): - return "{}(path={})".format(self.__class__.__name__, ABS_HERE) + return f"{self.__class__.__name__}(path={ABS_HERE})" def _register_distutils_finder(self): if "distlib" not in self.modules: return - class DistlibFinder(object): + class DistlibFinder: def __init__(self, path, loader): self.path = path self.loader = loader def find(self, name): - class Resource(object): + class Resource: def __init__(self, content): self.bytes = content diff --git a/tasks/make_zipapp.py b/tasks/make_zipapp.py index 67286f6..5303f41 100644 --- a/tasks/make_zipapp.py +++ b/tasks/make_zipapp.py @@ -1,4 +1,6 @@ """https://docs.python.org/3/library/zipapp.html""" +from __future__ import annotations + import argparse import io import json @@ -20,7 +22,7 @@ from packaging.requirements import Requirement HERE = Path(__file__).parent.absolute() -VERSIONS = [f"3.{i}" for i in range(10, 5, -1)] +VERSIONS = [f"3.{i}" for i in range(10, 6, -1)] def main(): @@ -223,7 +225,13 @@ class WheelDownloader: def run_suppress_output(cmd, stop_print_on_fail=False): - process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + encoding="utf-8", + ) out, err = process.communicate() if stop_print_on_fail and process.returncode != 0: print(f"exit with {process.returncode} of {' '.join(quote(i) for i in cmd)}", file=sys.stdout) diff --git a/tasks/release.py b/tasks/release.py index 8724bf2..0d5ac38 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -1,7 +1,8 @@ """Handles creating a release PR""" +from __future__ import annotations + from pathlib import Path from subprocess import check_call -from typing import Tuple from git import Commit, Head, Remote, Repo, TagReference from packaging.version import Version @@ -25,7 +26,7 @@ def main(version_str: str) -> None: print("All done! ✨ 🍰 ✨") -def create_release_branch(repo: Repo, version: Version) -> Tuple[Remote, Head]: +def create_release_branch(repo: Repo, version: Version) -> tuple[Remote, Head]: print("create release branch from upstream main") upstream = get_upstream(repo) upstream.fetch() diff --git a/tasks/update_embedded.py b/tasks/update_embedded.py index 2f44531..3f159f0 100755 --- a/tasks/update_embedded.py +++ b/tasks/update_embedded.py @@ -1,5 +1,7 @@ """Helper script to rebuild virtualenv.py from virtualenv_support""" +from __future__ import annotations + import codecs import os import re diff --git a/tasks/upgrade_wheels.py b/tasks/upgrade_wheels.py index 76750da..beb2df7 100644 --- a/tasks/upgrade_wheels.py +++ b/tasks/upgrade_wheels.py @@ -2,6 +2,8 @@ Helper script to rebuild virtualenv_support. Downloads the wheel files using pip """ +from __future__ import annotations + import os import shutil import subprocess @@ -15,7 +17,7 @@ from threading import Thread STRICT = "UPGRADE_ADVISORY" not in os.environ BUNDLED = ["pip", "setuptools", "wheel"] -SUPPORT = list(reversed([(2, 7)] + [(3, i) for i in range(5, 13)])) +SUPPORT = [(3, i) for i in range(7, 13)] DEST = Path(__file__).resolve().parents[1] / "src" / "virtualenv" / "seed" / "wheels" / "embed" @@ -81,7 +83,7 @@ def run(): lines.append("") changelog = "\n".join(lines) print(changelog) - if len(lines) >= 3: + if len(lines) >= 4: (Path(__file__).parents[1] / "docs" / "changelog" / "u.bugfix.rst").write_text(changelog, encoding="utf-8") support_table = OrderedDict((".".join(str(j) for j in i), []) for i in SUPPORT) for package in sorted(new_batch.keys()): diff --git a/tests/conftest.py b/tests/conftest.py index 68924dc..72db0d5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import shutil @@ -9,7 +11,6 @@ from pathlib import Path import pytest from virtualenv.app_data import AppDataDiskFolder -from virtualenv.discovery.builtin import get_interpreter from virtualenv.discovery.py_info import PythonInfo from virtualenv.info import IS_WIN, fs_supports_symlink from virtualenv.report import LOGGER @@ -54,9 +55,9 @@ def has_symlink_support(tmp_path_factory): # noqa: U100 def link_folder(has_symlink_support): if has_symlink_support: return os.symlink - elif sys.platform == "win32" and sys.version_info[0:2] > (3, 4): + elif sys.platform == "win32": # on Windows junctions may be used instead - import _winapi # Cpython3.5 has builtin implementation for junctions + import _winapi return getattr(_winapi, "CreateJunction", None) else: @@ -358,18 +359,6 @@ def temp_app_data(monkeypatch, tmp_path): @pytest.fixture(scope="session") -def cross_python(is_inside_ci, session_app_data): - spec = str(2 if sys.version_info[0] == 3 else 3) - interpreter = get_interpreter(spec, [], session_app_data) - if interpreter is None: - msg = f"could not find {spec}" - if is_inside_ci: - raise RuntimeError(msg) - pytest.skip(msg=msg) - return interpreter - - -@pytest.fixture(scope="session") def for_py_version(): return f"{sys.version_info.major}.{sys.version_info.minor}" diff --git a/tests/integration/test_run_int.py b/tests/integration/test_run_int.py index 8ca310e..36aaf4f 100644 --- a/tests/integration/test_run_int.py +++ b/tests/integration/test_run_int.py @@ -1,4 +1,4 @@ -import sys +from __future__ import annotations import pytest @@ -9,7 +9,7 @@ from virtualenv.util.subprocess import run_cmd @pytest.mark.skipif(IS_PYPY, reason="setuptools distutils patching does not work") def test_app_data_pinning(tmp_path): - version = "19.1.1" if sys.version_info[0:2] == (3, 4) else "19.3.1" + version = "19.3.1" result = cli_run([str(tmp_path), "--pip", version, "--activators", "", "--seeder", "app-data"]) code, out, _ = run_cmd([str(result.creator.script("pip")), "list", "--disable-pip-version-check"]) assert not code diff --git a/tests/integration/test_zipapp.py b/tests/integration/test_zipapp.py index 2472daa..b2f27a0 100644 --- a/tests/integration/test_zipapp.py +++ b/tests/integration/test_zipapp.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import shutil import subprocess from pathlib import Path diff --git a/tests/unit/activation/conftest.py b/tests/unit/activation/conftest.py index 3a93b15..6a46934 100644 --- a/tests/unit/activation/conftest.py +++ b/tests/unit/activation/conftest.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import re import subprocess diff --git a/tests/unit/activation/test_activate_this.py b/tests/unit/activation/test_activate_this.py deleted file mode 100644 index b7a4d60..0000000 --- a/tests/unit/activation/test_activate_this.py +++ /dev/null @@ -1,26 +0,0 @@ -from virtualenv.activation import PythonActivator -from virtualenv.config.cli.parser import VirtualEnvOptions -from virtualenv.run import session_via_cli - - -def test_python_activator_cross(session_app_data, cross_python, special_name_dir): - options = VirtualEnvOptions() - cli_args = [ - str(special_name_dir), - "-p", - str(cross_python.executable), - "--app-data", - str(session_app_data.lock.path), - "--without-pip", - "--activators", - "", - ] - session = session_via_cli(cli_args, options) - activator = PythonActivator(options) - session.creator.bin_dir.mkdir(parents=True) - results = activator.generate(session.creator) - assert len(results) == 1 - result = results[0] - content = result.read_text(encoding="utf-8") - # check that the repr strings have been correctly stripped - assert "\"'" not in content diff --git a/tests/unit/activation/test_activation_support.py b/tests/unit/activation/test_activation_support.py index 4bca363..e25fc96 100644 --- a/tests/unit/activation/test_activation_support.py +++ b/tests/unit/activation/test_activation_support.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from argparse import Namespace import pytest diff --git a/tests/unit/activation/test_activator.py b/tests/unit/activation/test_activator.py index c8e973a..9b15b0b 100644 --- a/tests/unit/activation/test_activator.py +++ b/tests/unit/activation/test_activator.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from argparse import Namespace from virtualenv.activation.activator import Activator diff --git a/tests/unit/activation/test_bash.py b/tests/unit/activation/test_bash.py index ae56896..4a36767 100644 --- a/tests/unit/activation/test_bash.py +++ b/tests/unit/activation/test_bash.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from virtualenv.activation import BashActivator diff --git a/tests/unit/activation/test_batch.py b/tests/unit/activation/test_batch.py index 9e6d6d6..458278f 100644 --- a/tests/unit/activation/test_batch.py +++ b/tests/unit/activation/test_batch.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from shlex import quote import pytest diff --git a/tests/unit/activation/test_csh.py b/tests/unit/activation/test_csh.py index afdb08f..e76b97b 100644 --- a/tests/unit/activation/test_csh.py +++ b/tests/unit/activation/test_csh.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from virtualenv.activation import CShellActivator diff --git a/tests/unit/activation/test_fish.py b/tests/unit/activation/test_fish.py index 18a6b3c..2a82059 100644 --- a/tests/unit/activation/test_fish.py +++ b/tests/unit/activation/test_fish.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from virtualenv.activation import FishActivator diff --git a/tests/unit/activation/test_nushell.py b/tests/unit/activation/test_nushell.py index 513ebba..e3bd5a3 100644 --- a/tests/unit/activation/test_nushell.py +++ b/tests/unit/activation/test_nushell.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from shutil import which from virtualenv.activation import NushellActivator diff --git a/tests/unit/activation/test_powershell.py b/tests/unit/activation/test_powershell.py index 761237f..887b766 100644 --- a/tests/unit/activation/test_powershell.py +++ b/tests/unit/activation/test_powershell.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from shlex import quote diff --git a/tests/unit/activation/test_python_activator.py b/tests/unit/activation/test_python_activator.py index cd8997e..ee0d1eb 100644 --- a/tests/unit/activation/test_python_activator.py +++ b/tests/unit/activation/test_python_activator.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import sys from ast import literal_eval diff --git a/tests/unit/config/cli/test_parser.py b/tests/unit/config/cli/test_parser.py index a12fb3f..cb656c3 100644 --- a/tests/unit/config/cli/test_parser.py +++ b/tests/unit/config/cli/test_parser.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from contextlib import contextmanager diff --git a/tests/unit/config/test___main__.py b/tests/unit/config/test___main__.py index 62228c9..76c9dfa 100644 --- a/tests/unit/config/test___main__.py +++ b/tests/unit/config/test___main__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import re import sys from subprocess import PIPE, Popen, check_output diff --git a/tests/unit/config/test_env_var.py b/tests/unit/config/test_env_var.py index 2fe6ef4..b8eb656 100644 --- a/tests/unit/config/test_env_var.py +++ b/tests/unit/config/test_env_var.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from pathlib import Path diff --git a/tests/unit/config/test_ini.py b/tests/unit/config/test_ini.py index c7a601e..e7fde82 100644 --- a/tests/unit/config/test_ini.py +++ b/tests/unit/config/test_ini.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from textwrap import dedent import pytest diff --git a/tests/unit/create/conftest.py b/tests/unit/create/conftest.py index 1e028fb..5bf764e 100644 --- a/tests/unit/create/conftest.py +++ b/tests/unit/create/conftest.py @@ -7,16 +7,14 @@ It's possible to use multiple types of host pythons to create virtual environmen - invoking from our own venv """ -import subprocess +from __future__ import annotations + import sys -from pathlib import Path from subprocess import Popen import pytest from virtualenv.discovery.py_info import PythonInfo -from virtualenv.info import IS_WIN -from virtualenv.run import cli_run CURRENT = PythonInfo.current_system() @@ -29,7 +27,7 @@ def root(tmp_path_factory, session_app_data): # noqa: U100 def venv(tmp_path_factory, session_app_data): if CURRENT.is_venv: return sys.executable - elif CURRENT.version_info.major == 3: + else: root_python = root(tmp_path_factory, session_app_data) dest = tmp_path_factory.mktemp("venv") process = Popen([str(root_python), "-m", "venv", "--without-pip", str(dest)]) @@ -40,56 +38,9 @@ def venv(tmp_path_factory, session_app_data): return exe_path -def old_virtualenv(tmp_path_factory, session_app_data): - if CURRENT.is_old_virtualenv: - return CURRENT.executable - else: - env_for_old_virtualenv = tmp_path_factory.mktemp("env-for-old-virtualenv") - result = cli_run(["--no-download", "--activators", "", str(env_for_old_virtualenv), "--no-periodic-update"]) - # noinspection PyBroadException - try: - process = Popen( - [ - str(result.creator.script("pip")), - "install", - "--no-index", - "--disable-pip-version-check", - str(Path(__file__).resolve().parent / "virtualenv-16.7.9-py2.py3-none-any.whl"), - "-v", - ], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - _, __ = process.communicate() - assert not process.returncode - except Exception: - return RuntimeError("failed to install old virtualenv") - # noinspection PyBroadException - try: - old_virtualenv_at = tmp_path_factory.mktemp("old-virtualenv") - cmd = [ - str(result.creator.script("virtualenv")), - str(old_virtualenv_at), - "--no-pip", - "--no-setuptools", - "--no-wheel", - ] - process = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - _, __ = process.communicate() - assert not process.returncode - if result.creator.interpreter.implementation == "PyPy" and IS_WIN: - # old virtualenv creates pypy paths wrong on windows, so have to hardcode it - return str(old_virtualenv_at / "bin" / "pypy.exe") - exe_path = CURRENT.discover_exe(session_app_data, prefix=str(old_virtualenv_at)).original_executable - return exe_path - except Exception as exception: - return RuntimeError(f"failed to create old virtualenv {exception}") - - PYTHON = { "root": root, "venv": venv, - "old_virtualenv": old_virtualenv, } diff --git a/tests/unit/create/console_app/demo/__init__.py b/tests/unit/create/console_app/demo/__init__.py index a7e1f5a..f68e2c7 100644 --- a/tests/unit/create/console_app/demo/__init__.py +++ b/tests/unit/create/console_app/demo/__init__.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + def run(): print("magic") diff --git a/tests/unit/create/console_app/demo/__main__.py b/tests/unit/create/console_app/demo/__main__.py index a7e1f5a..f68e2c7 100644 --- a/tests/unit/create/console_app/demo/__main__.py +++ b/tests/unit/create/console_app/demo/__main__.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + def run(): print("magic") diff --git a/tests/unit/create/console_app/setup.py b/tests/unit/create/console_app/setup.py index 6068493..a03590f 100644 --- a/tests/unit/create/console_app/setup.py +++ b/tests/unit/create/console_app/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from setuptools import setup setup() diff --git a/tests/unit/create/test_creator.py b/tests/unit/create/test_creator.py index ea61ed0..1f3750f 100644 --- a/tests/unit/create/test_creator.py +++ b/tests/unit/create/test_creator.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import ast import difflib import gc @@ -22,9 +24,7 @@ import pytest from virtualenv.__main__ import run, run_with_catch from virtualenv.create.creator import DEBUG_SCRIPT, Creator, get_env_debug_info from virtualenv.create.pyenv_cfg import PyEnvCfg -from virtualenv.create.via_global_ref.builtin.cpython.cpython2 import CPython2PosixBase from virtualenv.create.via_global_ref.builtin.cpython.cpython3 import CPython3Posix -from virtualenv.create.via_global_ref.builtin.python2.python2 import Python2 from virtualenv.discovery.py_info import PythonInfo from virtualenv.info import IS_PYPY, IS_WIN, fs_is_case_sensitive from virtualenv.run import cli_run, session_via_cli @@ -101,29 +101,11 @@ for k, v in CURRENT.creators().key_to_meta.items(): CREATE_METHODS.append((k, "copies")) if v.can_symlink: CREATE_METHODS.append((k, "symlinks")) -_VENV_BUG_ON = ( - IS_PYPY - and CURRENT.version_info[0:3] == (3, 6, 9) - and CURRENT.pypy_version_info[0:2] == [7, 3, 0] - and CURRENT.platform == "linux" -) @pytest.mark.parametrize( ("creator", "isolated"), - [ - pytest.param( - *i, - marks=pytest.mark.xfail( - reason="https://bitbucket.org/pypy/pypy/issues/3159/pypy36-730-venv-fails-with-copies-on-linux", - strict=True, - ), - ) - if _VENV_BUG_ON and i[0][0] == "venv" and i[0][1] == "copies" - else i - for i in product(CREATE_METHODS, ["isolated", "global"]) - ], - ids=lambda i: "-".join(i) if isinstance(i, tuple) else i, + [pytest.param(*i, id=f"{'-'.join(i[0])}-{i[1]}") for i in product(CREATE_METHODS, ["isolated", "global"])], ) def test_create_no_seed(python, creator, isolated, system, coverage_env, special_name_dir): dest = special_name_dir @@ -226,10 +208,6 @@ def test_create_no_seed(python, creator, isolated, system, coverage_env, special ).strip() assert result == "None" - if isinstance(creator, CPython2PosixBase): - make_file = debug["makefile_filename"] - assert os.path.exists(make_file) - git_ignore = (dest / ".gitignore").read_text(encoding="utf-8") assert git_ignore.splitlines() == ["# created by virtualenv automatically", "*"] @@ -340,32 +318,6 @@ def test_home_path_is_exe_parent(tmp_path, creator): assert any(os.path.exists(os.path.join(cfg["home"], exe)) for exe in exes) -@pytest.mark.slow() -@pytest.mark.usefixtures("current_fastest") -def test_cross_major(cross_python, coverage_env, tmp_path, session_app_data): - cmd = [ - "-p", - cross_python.executable, - str(tmp_path), - "--no-setuptools", - "--no-wheel", - "--activators", - "", - ] - result = cli_run(cmd) - pip_scripts = {i.name.replace(".exe", "") for i in result.creator.script_dir.iterdir() if i.name.startswith("pip")} - major, minor = cross_python.version_info[0:2] - assert pip_scripts == { - "pip", - f"pip{major}", - f"pip-{major}.{minor}", - f"pip{major}.{minor}", - } - coverage_env() - env = PythonInfo.from_exe(str(result.creator.exe), session_app_data) - assert env.version_info.major != CURRENT.version_info.major - - @pytest.mark.usefixtures("temp_app_data") def test_create_parallel(tmp_path): def create(count): @@ -659,34 +611,3 @@ def test_python_path(monkeypatch, tmp_path, python_path_on): assert non_python_path == [i for i in base if i not in extra_as_python_path] else: assert base == extra_all - - -@pytest.mark.parametrize( - ("py", "pyc"), - product( - [True, False] if Python2.from_stdlib(Python2.mappings(CURRENT), "os.py")[2] else [False], - [True, False] if Python2.from_stdlib(Python2.mappings(CURRENT), "os.pyc")[2] else [False], - ), -) -@pytest.mark.usefixtures("session_app_data") -def test_py_pyc_missing(tmp_path, mocker, py, pyc): - """Ensure that creation can succeed if os.pyc exists (even if os.py has been deleted)""" - previous = Python2.from_stdlib - - def from_stdlib(mappings, name): - path, to, exists = previous(mappings, name) - if name.endswith("py"): - exists = py - elif name.endswith("pyc"): - exists = pyc - return path, to, exists - - mocker.patch.object(Python2, "from_stdlib", side_effect=from_stdlib) - - result = cli_run([str(tmp_path), "--without-pip", "--activators", "", "-vv", "-p", "2"]) - py_at = Python2.from_stdlib(Python2.mappings(CURRENT), "os.py")[1](result.creator, Path("os.py")) - py = pyc is False or py # if pyc is False we fallback to serve the py, which will exist (as we only mock the check) - assert py_at.exists() is py - - pyc_at = Python2.from_stdlib(Python2.mappings(CURRENT), "osc.py")[1](result.creator, Path("os.pyc")) - assert pyc_at.exists() is pyc diff --git a/tests/unit/create/test_interpreters.py b/tests/unit/create/test_interpreters.py index 5d36b23..b6b58df 100644 --- a/tests/unit/create/test_interpreters.py +++ b/tests/unit/create/test_interpreters.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from uuid import uuid4 diff --git a/tests/unit/create/via_global_ref/builtin/conftest.py b/tests/unit/create/via_global_ref/builtin/conftest.py index 7119fbe..bb505db 100644 --- a/tests/unit/create/via_global_ref/builtin/conftest.py +++ b/tests/unit/create/via_global_ref/builtin/conftest.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from pathlib import Path diff --git a/tests/unit/create/via_global_ref/builtin/cpython/test_cpython3_win.py b/tests/unit/create/via_global_ref/builtin/cpython/test_cpython3_win.py index 90fdb38..f831de1 100644 --- a/tests/unit/create/via_global_ref/builtin/cpython/test_cpython3_win.py +++ b/tests/unit/create/via_global_ref/builtin/cpython/test_cpython3_win.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from testing.helpers import contains_exe, contains_ref from testing.path import join as path diff --git a/tests/unit/create/via_global_ref/builtin/pypy/test_pypy3.py b/tests/unit/create/via_global_ref/builtin/pypy/test_pypy3.py index 4903801..3ae905f 100644 --- a/tests/unit/create/via_global_ref/builtin/pypy/test_pypy3.py +++ b/tests/unit/create/via_global_ref/builtin/pypy/test_pypy3.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from testing.helpers import contains_exe, contains_ref from testing.path import join as path diff --git a/tests/unit/create/via_global_ref/builtin/testing/helpers.py b/tests/unit/create/via_global_ref/builtin/testing/helpers.py index 5ae1df8..e55c8d0 100644 --- a/tests/unit/create/via_global_ref/builtin/testing/helpers.py +++ b/tests/unit/create/via_global_ref/builtin/testing/helpers.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from functools import reduce from pathlib import Path 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 b2e1b85..96c5e3b 100644 --- a/tests/unit/create/via_global_ref/builtin/testing/path.py +++ b/tests/unit/create/via_global_ref/builtin/testing/path.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from abc import ABCMeta, abstractmethod from itertools import chain from operator import attrgetter as attr diff --git a/tests/unit/create/via_global_ref/builtin/testing/py_info.py b/tests/unit/create/via_global_ref/builtin/testing/py_info.py index d7909b2..f43b45d 100644 --- a/tests/unit/create/via_global_ref/builtin/testing/py_info.py +++ b/tests/unit/create/via_global_ref/builtin/testing/py_info.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pathlib import Path from virtualenv.discovery.py_info import PythonInfo diff --git a/tests/unit/create/via_global_ref/greet/setup.py b/tests/unit/create/via_global_ref/greet/setup.py index 7206137..2965915 100644 --- a/tests/unit/create/via_global_ref/greet/setup.py +++ b/tests/unit/create/via_global_ref/greet/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from setuptools import Extension, setup diff --git a/tests/unit/create/via_global_ref/test_api.py b/tests/unit/create/via_global_ref/test_api.py index ade64d4..a863b0e 100644 --- a/tests/unit/create/via_global_ref/test_api.py +++ b/tests/unit/create/via_global_ref/test_api.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from virtualenv.create.via_global_ref import api diff --git a/tests/unit/create/via_global_ref/test_build_c_ext.py b/tests/unit/create/via_global_ref/test_build_c_ext.py index 0de14d5..a2442d1 100644 --- a/tests/unit/create/via_global_ref/test_build_c_ext.py +++ b/tests/unit/create/via_global_ref/test_build_c_ext.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import shutil import subprocess diff --git a/tests/unit/discovery/py_info/test_py_info.py b/tests/unit/discovery/py_info/test_py_info.py index 24b129c..639fbaa 100644 --- a/tests/unit/discovery/py_info/test_py_info.py +++ b/tests/unit/discovery/py_info/test_py_info.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import copy import functools import itertools @@ -343,9 +345,6 @@ def test_custom_venv_install_scheme_is_prefered(mocker): # define the prefix as sysconfig.get_preferred_scheme did before 3.11 sysconfig_install_schemes["nt" if os.name == "nt" else "posix_prefix"] = default_scheme - if sys.version_info[0] == 2: - sysconfig_install_schemes = _stringify_schemes_dict(sysconfig_install_schemes) - # On Python < 3.10, the distutils schemes are not derived from sysconfig schemes # So we mock them as well to assert the custom "venv" install scheme has priority distutils_scheme = { @@ -360,9 +359,6 @@ def test_custom_venv_install_scheme_is_prefered(mocker): "nt": distutils_scheme, } - if sys.version_info[0] == 2: - distutils_schemes = _stringify_schemes_dict(distutils_schemes) - # We need to mock distutils first, so they don't see the mocked sysconfig, # if imported for the first time. # That can happen if the actual interpreter has the "venv" INSTALL_SCHEME diff --git a/tests/unit/discovery/py_info/test_py_info_exe_based_of.py b/tests/unit/discovery/py_info/test_py_info_exe_based_of.py index 2b025eb..562424e 100644 --- a/tests/unit/discovery/py_info/test_py_info_exe_based_of.py +++ b/tests/unit/discovery/py_info/test_py_info_exe_based_of.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os from pathlib import Path diff --git a/tests/unit/discovery/test_discovery.py b/tests/unit/discovery/test_discovery.py index c4a2cc7..b8c820a 100644 --- a/tests/unit/discovery/test_discovery.py +++ b/tests/unit/discovery/test_discovery.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import sys diff --git a/tests/unit/discovery/test_py_spec.py b/tests/unit/discovery/test_py_spec.py index 641429e..7656866 100644 --- a/tests/unit/discovery/test_py_spec.py +++ b/tests/unit/discovery/test_py_spec.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from copy import copy diff --git a/tests/unit/discovery/windows/conftest.py b/tests/unit/discovery/windows/conftest.py index 58da626..21ed61b 100644 --- a/tests/unit/discovery/windows/conftest.py +++ b/tests/unit/discovery/windows/conftest.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from contextlib import contextmanager from pathlib import Path diff --git a/tests/unit/discovery/windows/test_windows.py b/tests/unit/discovery/windows/test_windows.py index 65ff9ca..38dd546 100644 --- a/tests/unit/discovery/windows/test_windows.py +++ b/tests/unit/discovery/windows/test_windows.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys import pytest diff --git a/tests/unit/discovery/windows/test_windows_pep514.py b/tests/unit/discovery/windows/test_windows_pep514.py index c02db38..0b392d6 100644 --- a/tests/unit/discovery/windows/test_windows_pep514.py +++ b/tests/unit/discovery/windows/test_windows_pep514.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys import textwrap diff --git a/tests/unit/discovery/windows/winreg-mock-values.py b/tests/unit/discovery/windows/winreg-mock-values.py index 11ec0b4..da76c56 100644 --- a/tests/unit/discovery/windows/winreg-mock-values.py +++ b/tests/unit/discovery/windows/winreg-mock-values.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import winreg hive_open = { diff --git a/tests/unit/seed/embed/test_base_embed.py b/tests/unit/seed/embed/test_base_embed.py index 3344c74..e9d61f3 100644 --- a/tests/unit/seed/embed/test_base_embed.py +++ b/tests/unit/seed/embed/test_base_embed.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from virtualenv.run import session_via_cli diff --git a/tests/unit/seed/embed/test_bootstrap_link_via_app_data.py b/tests/unit/seed/embed/test_bootstrap_link_via_app_data.py index 2c8c3e8..610aaa2 100644 --- a/tests/unit/seed/embed/test_bootstrap_link_via_app_data.py +++ b/tests/unit/seed/embed/test_bootstrap_link_via_app_data.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import contextlib import os import sys @@ -113,9 +115,6 @@ def test_seed_link_via_app_data(tmp_path, coverage_env, current_fastest, copies) post_run = set(site_package.iterdir()) - patch_files assert not post_run, "\n".join(str(i) for i in post_run) - if sys.version_info[0:2] == (3, 4) and os.environ.get("PIP_REQ_TRACKER"): - os.environ.pop("PIP_REQ_TRACKER") - @contextlib.contextmanager def read_only_dir(d): diff --git a/tests/unit/seed/embed/test_pip_invoke.py b/tests/unit/seed/embed/test_pip_invoke.py index f045886..9deda26 100644 --- a/tests/unit/seed/embed/test_pip_invoke.py +++ b/tests/unit/seed/embed/test_pip_invoke.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import itertools import sys from shutil import copy2 @@ -23,7 +25,7 @@ def test_base_bootstrap_via_pip_invoke(tmp_path, coverage_env, mocker, current_f def _load_embed_wheel(app_data, distribution, for_py_version, version): # noqa: U100 return load_embed_wheel(app_data, distribution, old_ver, version) - old_ver = "2.7" + old_ver = "3.7" old = BUNDLE_SUPPORT[old_ver] mocker.patch("virtualenv.seed.wheels.bundle.load_embed_wheel", side_effect=_load_embed_wheel) diff --git a/tests/unit/seed/wheels/test_acquire.py b/tests/unit/seed/wheels/test_acquire.py index d028239..7f88f6a 100644 --- a/tests/unit/seed/wheels/test_acquire.py +++ b/tests/unit/seed/wheels/test_acquire.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import sys from datetime import datetime diff --git a/tests/unit/seed/wheels/test_acquire_find_wheel.py b/tests/unit/seed/wheels/test_acquire_find_wheel.py index 6ca1f22..7822849 100644 --- a/tests/unit/seed/wheels/test_acquire_find_wheel.py +++ b/tests/unit/seed/wheels/test_acquire_find_wheel.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from virtualenv.seed.wheels.acquire import find_compatible_in_house @@ -22,13 +24,6 @@ def test_find_exact(for_py_version): assert result.path == expected.path -def test_find_less_than(): - latest = get_embed_wheel("setuptools", MAX) - result = find_compatible_in_house("setuptools", f"<{latest.version}", MAX, BUNDLE_FOLDER) - assert result is not None - assert result.path != latest.path - - def test_find_bad_spec(): with pytest.raises(ValueError, match="bad"): find_compatible_in_house("setuptools", "bad", MAX, BUNDLE_FOLDER) diff --git a/tests/unit/seed/wheels/test_bundle.py b/tests/unit/seed/wheels/test_bundle.py index 38be96c..fbd34f9 100644 --- a/tests/unit/seed/wheels/test_bundle.py +++ b/tests/unit/seed/wheels/test_bundle.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os from datetime import datetime from pathlib import Path diff --git a/tests/unit/seed/wheels/test_periodic_update.py b/tests/unit/seed/wheels/test_periodic_update.py index e7794f5..82c86e3 100644 --- a/tests/unit/seed/wheels/test_periodic_update.py +++ b/tests/unit/seed/wheels/test_periodic_update.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import os import subprocess diff --git a/tests/unit/seed/wheels/test_wheels_util.py b/tests/unit/seed/wheels/test_wheels_util.py index 7c3c4a9..00afa04 100644 --- a/tests/unit/seed/wheels/test_wheels_util.py +++ b/tests/unit/seed/wheels/test_wheels_util.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest from virtualenv.seed.wheels.embed import MAX, get_embed_wheel diff --git a/tests/unit/test_run.py b/tests/unit/test_run.py index 41223e2..a048e60 100644 --- a/tests/unit/test_run.py +++ b/tests/unit/test_run.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import pytest diff --git a/tests/unit/test_util.py b/tests/unit/test_util.py index 81f8ff7..1959c5f 100644 --- a/tests/unit/test_util.py +++ b/tests/unit/test_util.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import concurrent.futures import traceback |