diff --git a/.gitignore b/.gitignore index b5dc459..d5e2400 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/virtualenv-20.20.0.tar.gz +SOURCES/virtualenv-20.21.1.tar.gz diff --git a/.python-virtualenv.metadata b/.python-virtualenv.metadata index 57aa073..b1fdf0c 100644 --- a/.python-virtualenv.metadata +++ b/.python-virtualenv.metadata @@ -1 +1 @@ -9a444e4de87a9944222a1bde643f0a4ae8bd3e57 SOURCES/virtualenv-20.20.0.tar.gz +ee10bef35332c6d7a9c9c82e11c5e5d081ddbdbd SOURCES/virtualenv-20.21.1.tar.gz diff --git a/SOURCES/3.12-support-and-no-setuptools-wheel-on-3.12-2558.patch b/SOURCES/3.12-support-and-no-setuptools-wheel-on-3.12-2558.patch new file mode 100644 index 0000000..f70a536 --- /dev/null +++ b/SOURCES/3.12-support-and-no-setuptools-wheel-on-3.12-2558.patch @@ -0,0 +1,339 @@ +From 42e0698087f061d1d7db6fcb9469302bda5d44ca Mon Sep 17 00:00:00 2001 +From: chrysle +Date: Fri, 28 Apr 2023 01:36:03 +0200 +Subject: [PATCH] 3.12 support and no setuptools/wheel on 3.12+ (#2558) + +Cherry-picked from fd93dd79be89b21e6e9d43ca2dd1b02b811f6d6f +--- + docs/changelog/2487.feature.rst | 6 +++++ + docs/changelog/2558.feature.rst | 1 + + docs/render_cli.py | 10 +------ + docs/user_guide.rst | 5 ++-- + src/virtualenv/activation/python/__init__.py | 3 ++- + src/virtualenv/seed/embed/base_embed.py | 11 +++++--- + src/virtualenv/util/path/_sync.py | 4 ++- + tests/unit/config/test___main__.py | 2 +- + tests/unit/create/test_creator.py | 26 ++++++++++++++++--- + tests/unit/discovery/py_info/test_py_info.py | 2 +- + tests/unit/discovery/windows/conftest.py | 2 +- + tests/unit/seed/embed/test_base_embed.py | 12 +++++++++ + .../embed/test_bootstrap_link_via_app_data.py | 4 +-- + .../unit/seed/wheels/test_periodic_update.py | 17 ++++++++++-- + 14 files changed, 79 insertions(+), 26 deletions(-) + create mode 100644 docs/changelog/2487.feature.rst + create mode 100644 docs/changelog/2558.feature.rst + +diff --git a/docs/changelog/2487.feature.rst b/docs/changelog/2487.feature.rst +new file mode 100644 +index 0000000..12cc896 +--- /dev/null ++++ b/docs/changelog/2487.feature.rst +@@ -0,0 +1,6 @@ ++Do not install ``wheel`` and ``setuptools`` seed packages for Python 3.12+. To restore the old behaviour use: ++ ++- for ``wheel`` use ``VIRTUALENV_WHEEL=bundle`` environment variable or ``--wheel=bundle`` CLI flag, ++- for ``setuptools`` use ``VIRTUALENV_SETUPTOOLS=bundle`` environment variable or ``--setuptools=bundle`` CLI flag. ++ ++By :user:`chrysle`. +diff --git a/docs/changelog/2558.feature.rst b/docs/changelog/2558.feature.rst +new file mode 100644 +index 0000000..58b627a +--- /dev/null ++++ b/docs/changelog/2558.feature.rst +@@ -0,0 +1 @@ ++3.12 support - by :user:`gaborbernat`. +diff --git a/src/virtualenv/activation/python/__init__.py b/src/virtualenv/activation/python/__init__.py +index eb83504..a49444b 100644 +--- a/src/virtualenv/activation/python/__init__.py ++++ b/src/virtualenv/activation/python/__init__.py +@@ -12,10 +12,11 @@ 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) ++ lib_folders = os.pathsep.join(lib_folders.keys()).replace("\\", "\\\\") # escape Windows path characters + win_py2 = creator.interpreter.platform == "win32" and creator.interpreter.version_info.major == 2 + replacements.update( + { +- "__LIB_FOLDERS__": os.pathsep.join(lib_folders.keys()), ++ "__LIB_FOLDERS__": lib_folders, + "__DECODE_PATH__": ("yes" if win_py2 else ""), + }, + ) +diff --git a/src/virtualenv/seed/embed/base_embed.py b/src/virtualenv/seed/embed/base_embed.py +index f29110b..6782d6f 100644 +--- a/src/virtualenv/seed/embed/base_embed.py ++++ b/src/virtualenv/seed/embed/base_embed.py +@@ -39,7 +39,7 @@ class BaseEmbed(Seeder, metaclass=ABCMeta): + return { + distribution: getattr(self, f"{distribution}_version") + for distribution in self.distributions() +- if getattr(self, f"no_{distribution}") is False ++ if getattr(self, f"no_{distribution}") is False and getattr(self, f"{distribution}_version") != "none" + } + + @classmethod +@@ -69,11 +69,13 @@ class BaseEmbed(Seeder, metaclass=ABCMeta): + default=[], + ) + for distribution, default in cls.distributions().items(): ++ if interpreter.version_info[:2] >= (3, 12) and distribution in {"wheel", "setuptools"}: ++ default = "none" + parser.add_argument( + f"--{distribution}", + dest=distribution, + metavar="version", +- help=f"version of {distribution} to install as seed: embed, bundle or exact version", ++ help=f"version of {distribution} to install as seed: embed, bundle, none or exact version", + default=default, + ) + for distribution in cls.distributions(): +@@ -101,7 +103,10 @@ class BaseEmbed(Seeder, metaclass=ABCMeta): + for distribution in self.distributions(): + if getattr(self, f"no_{distribution}"): + continue +- ver = f"={getattr(self, f'{distribution}_version', None) or 'latest'}" ++ version = getattr(self, f"{distribution}_version", None) ++ if version == "none": ++ continue ++ ver = f"={version or 'latest'}" + result += f" {distribution}{ver}," + return result[:-1] + ")" + +diff --git a/src/virtualenv/util/path/_sync.py b/src/virtualenv/util/path/_sync.py +index 604379d..b0af1eb 100644 +--- a/src/virtualenv/util/path/_sync.py ++++ b/src/virtualenv/util/path/_sync.py +@@ -1,6 +1,7 @@ + import logging + import os + import shutil ++import sys + from stat import S_IWUSR + + +@@ -56,7 +57,8 @@ def safe_delete(dest): + else: + raise + +- shutil.rmtree(str(dest), ignore_errors=True, onerror=onerror) ++ kwargs = {"onexc" if sys.version_info >= (3, 12) else "onerror": onerror} ++ shutil.rmtree(str(dest), ignore_errors=True, **kwargs) + + + class _Debug: +diff --git a/tests/unit/config/test___main__.py b/tests/unit/config/test___main__.py +index 62228c9..d22ef7e 100644 +--- a/tests/unit/config/test___main__.py ++++ b/tests/unit/config/test___main__.py +@@ -58,7 +58,7 @@ def test_fail_with_traceback(raise_on_session_done, tmp_path, capsys): + + @pytest.mark.usefixtures("session_app_data") + def test_session_report_full(tmp_path, capsys): +- run_with_catch([str(tmp_path)]) ++ run_with_catch([str(tmp_path), "--setuptools", "bundle", "--wheel", "bundle"]) + out, err = capsys.readouterr() + assert err == "" + lines = out.splitlines() +diff --git a/tests/unit/create/test_creator.py b/tests/unit/create/test_creator.py +index 0ec6d62..8b9d688 100644 +--- a/tests/unit/create/test_creator.py ++++ b/tests/unit/create/test_creator.py +@@ -412,7 +412,19 @@ def test_create_long_path(tmp_path): + @pytest.mark.parametrize("creator", sorted(set(PythonInfo.current_system().creators().key_to_class) - {"builtin"})) + @pytest.mark.usefixtures("session_app_data") + def test_create_distutils_cfg(creator, tmp_path, monkeypatch): +- result = cli_run([str(tmp_path / "venv"), "--activators", "", "--creator", creator]) ++ result = cli_run( ++ [ ++ str(tmp_path / "venv"), ++ "--activators", ++ "", ++ "--creator", ++ creator, ++ "--setuptools", ++ "bundle", ++ "--wheel", ++ "bundle", ++ ], ++ ) + + app = Path(__file__).parent / "console_app" + dest = tmp_path / "console_app" +@@ -465,7 +477,9 @@ def list_files(path): + + def test_zip_importer_can_import_setuptools(tmp_path): + """We're patching the loaders so might fail on r/o loaders, such as zipimporter on CPython<3.8""" +- result = cli_run([str(tmp_path / "venv"), "--activators", "", "--no-pip", "--no-wheel", "--copies"]) ++ result = cli_run( ++ [str(tmp_path / "venv"), "--activators", "", "--no-pip", "--no-wheel", "--copies", "--setuptools", "bundle"], ++ ) + zip_path = tmp_path / "site-packages.zip" + with zipfile.ZipFile(str(zip_path), "w", zipfile.ZIP_DEFLATED) as zip_handler: + lib = str(result.creator.purelib) +@@ -499,6 +513,7 @@ def test_no_preimport_threading(tmp_path): + out = subprocess.check_output( + [str(session.creator.exe), "-c", r"import sys; print('\n'.join(sorted(sys.modules)))"], + text=True, ++ encoding="utf-8", + ) + imported = set(out.splitlines()) + assert "threading" not in imported +@@ -515,6 +530,7 @@ def test_pth_in_site_vs_python_path(tmp_path): + out = subprocess.check_output( + [str(session.creator.exe), "-c", r"import sys; print(sys.testpth)"], + text=True, ++ encoding="utf-8", + ) + assert out == "ok\n" + # same with $PYTHONPATH pointing to site_packages +@@ -527,6 +543,7 @@ def test_pth_in_site_vs_python_path(tmp_path): + [str(session.creator.exe), "-c", r"import sys; print(sys.testpth)"], + text=True, + env=env, ++ encoding="utf-8", + ) + assert out == "ok\n" + +@@ -540,6 +557,7 @@ def test_getsitepackages_system_site(tmp_path): + out = subprocess.check_output( + [str(session.creator.exe), "-c", r"import site; print(site.getsitepackages())"], + text=True, ++ encoding="utf-8", + ) + site_packages = ast.literal_eval(out) + +@@ -554,6 +572,7 @@ def test_getsitepackages_system_site(tmp_path): + out = subprocess.check_output( + [str(session.creator.exe), "-c", r"import site; print(site.getsitepackages())"], + text=True, ++ encoding="utf-8", + ) + site_packages = [str(Path(i).resolve()) for i in ast.literal_eval(out)] + +@@ -579,6 +598,7 @@ def test_get_site_packages(tmp_path): + out = subprocess.check_output( + [str(session.creator.exe), "-c", r"import site; print(site.getsitepackages())"], + text=True, ++ encoding="utf-8", + ) + site_packages = ast.literal_eval(out) + +@@ -617,7 +637,7 @@ def test_python_path(monkeypatch, tmp_path, python_path_on): + if flag: + cmd.append(flag) + cmd.extend(["-c", "import json; import sys; print(json.dumps(sys.path))"]) +- return [i if case_sensitive else i.lower() for i in json.loads(subprocess.check_output(cmd))] ++ return [i if case_sensitive else i.lower() for i in json.loads(subprocess.check_output(cmd, encoding="utf-8"))] + + monkeypatch.delenv("PYTHONPATH", raising=False) + base = _get_sys_path() +diff --git a/tests/unit/discovery/py_info/test_py_info.py b/tests/unit/discovery/py_info/test_py_info.py +index 24b129c..f3fdb7e 100644 +--- a/tests/unit/discovery/py_info/test_py_info.py ++++ b/tests/unit/discovery/py_info/test_py_info.py +@@ -289,7 +289,7 @@ def test_discover_exe_on_path_non_spec_name_not_match(mocker): + assert CURRENT.satisfies(spec, impl_must_match=True) is False + + +-@pytest.mark.skipif(IS_PYPY, reason="setuptools distutil1s patching does not work") ++@pytest.mark.skipif(IS_PYPY, reason="setuptools distutils patching does not work") + def test_py_info_setuptools(): + from setuptools.dist import Distribution + +diff --git a/tests/unit/discovery/windows/conftest.py b/tests/unit/discovery/windows/conftest.py +index 58da626..94f14da 100644 +--- a/tests/unit/discovery/windows/conftest.py ++++ b/tests/unit/discovery/windows/conftest.py +@@ -9,7 +9,7 @@ def _mock_registry(mocker): + from virtualenv.discovery.windows.pep514 import winreg + + loc, glob = {}, {} +- mock_value_str = (Path(__file__).parent / "winreg-mock-values.py").read_text() ++ mock_value_str = (Path(__file__).parent / "winreg-mock-values.py").read_text(encoding="utf-8") + exec(mock_value_str, glob, loc) + enum_collect = loc["enum_collect"] + value_collect = loc["value_collect"] +diff --git a/tests/unit/seed/embed/test_base_embed.py b/tests/unit/seed/embed/test_base_embed.py +index 3344c74..ef2f829 100644 +--- a/tests/unit/seed/embed/test_base_embed.py ++++ b/tests/unit/seed/embed/test_base_embed.py +@@ -1,3 +1,5 @@ ++import sys ++ + import pytest + + from virtualenv.run import session_via_cli +@@ -10,3 +12,13 @@ from virtualenv.run import session_via_cli + def test_download_cli_flag(args, download, tmp_path): + session = session_via_cli(args + [str(tmp_path)]) + assert session.seeder.download is download ++ ++ ++def test_embed_wheel_versions(tmp_path): ++ session = session_via_cli([str(tmp_path)]) ++ expected = ( ++ {"pip": "bundle"} ++ if sys.version_info[:2] >= (3, 12) ++ else {"pip": "bundle", "setuptools": "bundle", "wheel": "bundle"} ++ ) ++ assert session.seeder.distribution_to_versions() == expected +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..015686d 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 +@@ -203,7 +203,7 @@ def test_populated_read_only_cache_and_copied_app_data(tmp_path, current_fastest + @pytest.mark.parametrize("pkg", ["pip", "setuptools", "wheel"]) + @pytest.mark.usefixtures("session_app_data", "current_fastest", "coverage_env") + def test_base_bootstrap_link_via_app_data_no(tmp_path, pkg): +- create_cmd = [str(tmp_path), "--seeder", "app-data", f"--no-{pkg}"] ++ create_cmd = [str(tmp_path), "--seeder", "app-data", f"--no-{pkg}", "--wheel", "bundle", "--setuptools", "bundle"] + result = cli_run(create_cmd) + assert not (result.creator.purelib / pkg).exists() + for key in {"pip", "setuptools", "wheel"} - {pkg}: +@@ -231,7 +231,7 @@ def _run_parallel_threads(tmp_path): + + def _run(name): + try: +- cli_run(["--seeder", "app-data", str(tmp_path / name), "--no-pip", "--no-setuptools"]) ++ cli_run(["--seeder", "app-data", str(tmp_path / name), "--no-pip", "--no-setuptools", "--wheel", "bundle"]) + except Exception as exception: + as_str = str(exception) + exceptions.append(as_str) +diff --git a/tests/unit/seed/wheels/test_periodic_update.py b/tests/unit/seed/wheels/test_periodic_update.py +index e7794f5..c36a983 100644 +--- a/tests/unit/seed/wheels/test_periodic_update.py ++++ b/tests/unit/seed/wheels/test_periodic_update.py +@@ -66,7 +66,7 @@ def test_manual_upgrade(session_app_data, caplog, mocker, for_py_version): + + @pytest.mark.usefixtures("session_app_data") + def test_pick_periodic_update(tmp_path, mocker, for_py_version): +- embed, current = get_embed_wheel("setuptools", "3.5"), get_embed_wheel("setuptools", for_py_version) ++ embed, current = get_embed_wheel("setuptools", "3.6"), get_embed_wheel("setuptools", for_py_version) + mocker.patch("virtualenv.seed.wheels.bundle.load_embed_wheel", return_value=embed) + completed = datetime.now() - timedelta(days=29) + u_log = UpdateLog( +@@ -77,7 +77,20 @@ def test_pick_periodic_update(tmp_path, mocker, for_py_version): + ) + read_dict = mocker.patch("virtualenv.app_data.via_disk_folder.JSONStoreDisk.read", return_value=u_log.to_dict()) + +- result = cli_run([str(tmp_path), "--activators", "", "--no-periodic-update", "--no-wheel", "--no-pip"]) ++ result = cli_run( ++ [ ++ str(tmp_path), ++ "--activators", ++ "", ++ "--no-periodic-update", ++ "--no-wheel", ++ "--no-pip", ++ "--setuptools", ++ "bundle", ++ "--wheel", ++ "bundle", ++ ], ++ ) + + assert read_dict.call_count == 1 + installed = [i.name for i in result.creator.purelib.iterdir() if i.suffix == ".dist-info"] +-- +2.40.0 + diff --git a/SOURCES/prevent-PermissionError-when-using-venv-creator-on-s.patch b/SOURCES/prevent-PermissionError-when-using-venv-creator-on-s.patch new file mode 100644 index 0000000..421c2b2 --- /dev/null +++ b/SOURCES/prevent-PermissionError-when-using-venv-creator-on-s.patch @@ -0,0 +1,70 @@ +From fc8e412fa8fe524ab3f112f02e865aa42608388b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= +Date: Thu, 27 Apr 2023 18:52:38 +0200 +Subject: [PATCH] prevent PermissionError when using venv creator on some + systems (#2543) + +Cherry-picked from 0597a2f8ab32705b86a25dbd1d42fd30c3033061 +--- + docs/changelog/2543.bugfix.rst | 2 ++ + src/virtualenv/activation/via_template.py | 4 ++++ + tests/unit/create/test_creator.py | 22 ++++++++++++++++++++++ + 3 files changed, 28 insertions(+) + create mode 100644 docs/changelog/2543.bugfix.rst + +diff --git a/docs/changelog/2543.bugfix.rst b/docs/changelog/2543.bugfix.rst +new file mode 100644 +index 0000000..5f0d6ca +--- /dev/null ++++ b/docs/changelog/2543.bugfix.rst +@@ -0,0 +1,2 @@ ++Prevent ``PermissionError`` when using venv creator on systems that deliver files without user write ++permission - by :user:`kulikjak`. +diff --git a/src/virtualenv/activation/via_template.py b/src/virtualenv/activation/via_template.py +index 069d52e..cc9dbda 100644 +--- a/src/virtualenv/activation/via_template.py ++++ b/src/virtualenv/activation/via_template.py +@@ -41,6 +41,10 @@ class ViaTemplateActivator(Activator, metaclass=ABCMeta): + for template in templates: + text = self.instantiate_template(replacements, template, creator) + dest = to_folder / self.as_name(template) ++ # remove the file if it already exists - this prevents permission ++ # errors when the dest is not writable ++ if dest.exists(): ++ dest.unlink() + # use write_bytes to avoid platform specific line normalization (\n -> \r\n) + dest.write_bytes(text.encode("utf-8")) + generated.append(dest) +diff --git a/tests/unit/create/test_creator.py b/tests/unit/create/test_creator.py +index ea61ed0..0ec6d62 100644 +--- a/tests/unit/create/test_creator.py ++++ b/tests/unit/create/test_creator.py +@@ -690,3 +690,25 @@ def test_py_pyc_missing(tmp_path, mocker, py, pyc): + + pyc_at = Python2.from_stdlib(Python2.mappings(CURRENT), "osc.py")[1](result.creator, Path("os.pyc")) + assert pyc_at.exists() is pyc ++ ++ ++# Make sure that the venv creator works on systems where vendor-delivered files ++# (specifically venv scripts delivered with Python itself) are not writable. ++# ++# https://github.com/pypa/virtualenv/issues/2419 ++@pytest.mark.skipif("venv" not in CURRENT_CREATORS, reason="test needs venv creator") ++def test_venv_creator_without_write_perms(tmp_path, mocker): ++ from virtualenv.run.session import Session ++ ++ prev = Session._create ++ ++ def func(self): ++ prev(self) ++ scripts_dir = self.creator.dest / "bin" ++ for script in scripts_dir.glob("*ctivate*"): ++ script.chmod(stat.S_IREAD | stat.S_IRGRP | stat.S_IROTH) ++ ++ mocker.patch("virtualenv.run.session.Session._create", side_effect=func, autospec=True) ++ ++ cmd = [str(tmp_path), "--seeder", "app-data", "--without-pip", "--creator", "venv"] ++ cli_run(cmd) +-- +2.40.0 + diff --git a/SPECS/python-virtualenv.spec b/SPECS/python-virtualenv.spec index 4f878c4..8e549dc 100644 --- a/SPECS/python-virtualenv.spec +++ b/SPECS/python-virtualenv.spec @@ -1,6 +1,6 @@ Name: python-virtualenv -Version: 20.20.0 -Release: 2%{?dist} +Version: 20.21.1 +Release: 1%{?dist} Summary: Tool to create isolated Python environments License: MIT @@ -10,6 +10,18 @@ Source0: %{pypi_source virtualenv} # Add /usr/share/python-wheels to extra_search_dir Patch1: rpm-wheels.patch +## Backports from virtualenv 20.22+ +## We cannot update yet as we want to preserve support for Python 2.7 and 3.6 environments +## Patches in https://github.com/fedora-python/virtualenv/commits/20.21.x +# (20.23.0) prevent PermissionError when using venv creator on some systems +# https://github.com/pypa/virtualenv/pull/2543 +Patch2: prevent-PermissionError-when-using-venv-creator-on-s.patch +# (20.23.0) 3.12 support and no setuptools/wheel on 3.12+ +# freezgun and typing changes stripped +# files missing in sdist removed from the path file +# https://github.com/pypa/virtualenv/pull/2558 +Patch3: 3.12-support-and-no-setuptools-wheel-on-3.12-2558.patch + BuildArch: noarch BuildRequires: python3-devel @@ -66,6 +78,12 @@ Requires: (python3.11-pip-wheel if python3.11) Requires: (python3.11-setuptools-wheel if python3.11) Requires: (python3.11-wheel-wheel if python3.11) +# For Python 3.12 +# (setuptools and wheel is not installed by default, but still possible with --wheel/setuptools=bundle) +Requires: (python3.12-pip-wheel if python3.12) +Requires: (python3.12-setuptools-wheel if python3.12) +Requires: (python3.12-wheel-wheel if python3.12) + %description -n python3-virtualenv virtualenv is a tool to create isolated Python environments. virtualenv is a successor to workingenv, and an extension of virtual-python. It is @@ -108,6 +126,7 @@ echo '__version__, __version_tuple__ = version, version_tuple' >> %{buildroot}%{ # Skip tests which requires internet or some extra dependencies # Requires internet: # - test_download_* +# - test_can_build_c_extensions (on Python 3.12+) # Uses disabled functionalities around bundled wheels: # - test_wheel_* # - test_seed_link_via_app_data @@ -124,6 +143,9 @@ PIP_CERT=/etc/pki/tls/certs/ca-bundle.crt \ not test_periodic_update and \ not test_wheel_ and \ not test_download_ and \ +%if v"%{python3_version}" >= v"3.12" + not test_can_build_c_extensions and \ +%endif not test_base_bootstrap_via_pip_invoke and \ not test_seed_link_via_app_data and \ not test_py_pyc_missing" @@ -134,6 +156,11 @@ PIP_CERT=/etc/pki/tls/certs/ca-bundle.crt \ %{_bindir}/virtualenv %changelog +* Wed Jan 03 2024 Miro HronĨok - 20.21.1-1 +- Update to 20.21.1 +- Backport from 20.23.0: Don't install setuptools and wheel to Python 3.12+ environments +- Add RPM Requires needed for Python 3.12 + * Thu Aug 24 2023 Sergey Cherevko - 20.20.0-2 - Rebuilt for MSVSphere 9.2