Compare commits

..

No commits in common. 'c8-stream-2.7' and 'epel9' have entirely different histories.

5
.gitignore vendored

@ -1 +1,4 @@
SOURCES/virtualenv-15.1.0.tar.gz
/virtualenv-*.tar.gz
/virtualenv-*/
/results_python-virtualenv/
*.rpm

@ -1 +0,0 @@
995ce0fa007210ac2f10258999d06813ecdd6eeb SOURCES/virtualenv-15.1.0.tar.gz

@ -0,0 +1,339 @@
From 42e0698087f061d1d7db6fcb9469302bda5d44ca Mon Sep 17 00:00:00 2001
From: chrysle <fritzihab@posteo.de>
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

@ -1,16 +0,0 @@
diff --git a/virtualenv.py b/virtualenv.py
index c4e3bd5..89b8863 100755
--- a/virtualenv.py
+++ b/virtualenv.py
@@ -1181,8 +1181,9 @@ def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear, sy
exec_dir = join(sys.exec_prefix, 'Lib')
else:
exec_dir = join(sys.exec_prefix, 'lib', py_version)
- for fn in os.listdir(exec_dir):
- copyfile(join(exec_dir, fn), join(lib_dir, fn), symlink)
+ if os.path.isdir(exec_dir):
+ for fn in os.listdir(exec_dir):
+ copyfile(join(exec_dir, fn), join(lib_dir, fn), symlink)
if is_jython:
# Jython has either jython-dev.jar and javalib/ dir, or just

@ -1,102 +0,0 @@
diff --git a/tests/test_virtualenv.py b/tests/test_virtualenv.py
index ce45ede..7946a16 100644
--- a/tests/test_virtualenv.py
+++ b/tests/test_virtualenv.py
@@ -4,11 +4,16 @@ import os
import shutil
import sys
import tempfile
+import zipfile
import pytest
import platform # noqa
from mock import patch, Mock
+try:
+ from pathlib import Path
+except ImportError:
+ from pathlib2 import Path
def test_version():
"""Should have a version string"""
@@ -139,3 +144,44 @@ def test_always_copy_option():
" symlink (to %s)" % (full_name, os.readlink(full_name))
finally:
shutil.rmtree(tmp_virtualenv)
+
+
+def test_missing_certifi_pem(tmp_path):
+ """Make sure that we can still create virtual environment if pip is
+ patched to not use certifi's cacert.pem and the file is removed.
+ This can happen if pip is packaged by Linux distributions."""
+ proj_dir = Path(__file__).parent.parent
+ support_original = proj_dir / "virtualenv_support"
+ pip_wheel = sorted(support_original.glob("pip*whl"))[0]
+ whl_name = pip_wheel.name
+
+ wheeldir = tmp_path / "wheels"
+ wheeldir.mkdir()
+ tmpcert = tmp_path / "tmpcert.pem"
+ cacert = "pip/_vendor/requests/cacert.pem"
+ certifi = "pip/_vendor/requests/certs.py"
+ oldpath = b"os.path.join(os.path.dirname(__file__), 'cacert.pem')"
+ newpath = "r'{}'".format(tmpcert).encode()
+ removed = False
+ replaced = False
+
+ with zipfile.ZipFile(str(pip_wheel), "r") as whlin:
+ with zipfile.ZipFile(str(wheeldir / whl_name), "w") as whlout:
+ for item in whlin.infolist():
+ buff = whlin.read(item.filename)
+ if item.filename == cacert:
+ tmpcert.write_bytes(buff)
+ removed = True
+ continue
+ if item.filename == certifi:
+ nbuff = buff.replace(oldpath, newpath)
+ assert nbuff != buff
+ buff = nbuff
+ replaced = True
+ whlout.writestr(item, buff)
+
+ assert removed and replaced
+
+ venvdir = tmp_path / "venv"
+ search_dirs = [str(wheeldir), str(support_original)]
+ virtualenv.create_environment(str(venvdir), search_dirs=search_dirs)
diff --git a/virtualenv.egg-info/PKG-INFO b/virtualenv.egg-info/PKG-INFO
index 11f5c75..501e81a 100644
--- a/virtualenv.egg-info/PKG-INFO
+++ b/virtualenv.egg-info/PKG-INFO
@@ -1,10 +1,12 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
Name: virtualenv
Version: 15.1.0
Summary: Virtual Python Environment builder
Home-page: https://virtualenv.pypa.io/
-Author: Jannis Leidel, Carl Meyer and Brian Rosner
-Author-email: python-virtualenv@groups.google.com
+Author: Ian Bicking
+Author-email: ianb@colorstudy.com
+Maintainer: Jannis Leidel, Carl Meyer and Brian Rosner
+Maintainer-email: python-virtualenv@groups.google.com
License: MIT
Description: Virtualenv
==========
diff --git a/virtualenv.py b/virtualenv.py
index a174b8a..5699998 100755
--- a/virtualenv.py
+++ b/virtualenv.py
@@ -861,7 +861,10 @@ def install_wheel(project_names, py_executable, search_dirs=None,
import pip
- cert_data = pkgutil.get_data("pip._vendor.requests", "cacert.pem")
+ try:
+ cert_data = pkgutil.get_data("pip._vendor.requests", "cacert.pem")
+ except IOError:
+ cert_data = None
if cert_data is not None:
cert_file = tempfile.NamedTemporaryFile(delete=False)
cert_file.write(cert_data)

@ -1,230 +0,0 @@
diff --git a/virtualenv.py b/virtualenv.py
index 9854324..e5d0883 100755
--- a/virtualenv.py
+++ b/virtualenv.py
@@ -49,7 +49,8 @@ try:
except NameError:
basestring = str
-py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1])
+version = '%s.%s' % (sys.version_info[0], sys.version_info[1])
+py_version = 'python%s' % version
is_jython = sys.platform.startswith('java')
is_pypy = hasattr(sys, 'pypy_version_info')
@@ -1012,13 +1013,13 @@ def change_prefix(filename, dst_prefix):
if is_darwin:
prefixes.extend((
- os.path.join("/Library/Python", sys.version[:3], "site-packages"),
+ os.path.join("/Library/Python", version, "site-packages"),
os.path.join(sys.prefix, "Extras", "lib", "python"),
- os.path.join("~", "Library", "Python", sys.version[:3], "site-packages"),
+ os.path.join("~", "Library", "Python", version, "site-packages"),
# Python 2.6 no-frameworks
- os.path.join("~", ".local", "lib","python", sys.version[:3], "site-packages"),
+ os.path.join("~", ".local", "lib","python", version, "site-packages"),
# System Python 2.7 on OSX Mountain Lion
- os.path.join("~", "Library", "Python", sys.version[:3], "lib", "python", "site-packages")))
+ os.path.join("~", "Library", "Python", version, "lib", "python", "site-packages")))
if hasattr(sys, 'real_prefix'):
prefixes.append(sys.real_prefix)
@@ -1099,7 +1100,7 @@ def copy_required_modules(dst_prefix, symlink):
# special-case custom readline.so on OS X, but not for pypy:
if modname == 'readline' and sys.platform == 'darwin' and not (
is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))):
- dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so')
+ dst_filename = join(dst_prefix, 'lib', py_version, 'readline.so')
elif modname == 'readline' and sys.platform == 'win32':
# special-case for Windows, where readline is not a
# standard module, though it may have been installed in
@@ -1398,8 +1399,7 @@ def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear, sy
if not is_win:
# Ensure that 'python', 'pythonX' and 'pythonX.Y' all exist
py_exe_version_major = 'python%s' % sys.version_info[0]
- py_exe_version_major_minor = 'python%s.%s' % (
- sys.version_info[0], sys.version_info[1])
+ py_exe_version_major_minor = py_version
py_exe_no_version = 'python'
required_symlinks = [ py_exe_no_version, py_exe_version_major,
py_exe_version_major_minor ]
@@ -1547,7 +1547,8 @@ def fix_local_scheme(home_dir, symlink=True):
except ImportError:
pass
else:
- if sysconfig._get_default_scheme() == 'posix_local':
+ get_scheme = getattr(sysconfig, 'get_default_scheme', None) or sysconfig._get_default_scheme
+ if get_scheme() == 'posix_local':
local_path = os.path.join(home_dir, 'local')
if not os.path.exists(local_path):
os.mkdir(local_path)
@@ -1575,7 +1576,7 @@ def fix_lib64(lib_dir, symlink=True):
logger.debug('This system uses lib64; symlinking lib64 to lib')
- assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], (
+ assert os.path.basename(lib_dir) == py_version, (
"Unexpected python lib dir: %r" % lib_dir)
lib_parent = os.path.dirname(lib_dir)
top_level = os.path.dirname(lib_parent)
@@ -1637,7 +1638,7 @@ def make_environment_relocatable(home_dir):
fixup_pth_and_egg_link(home_dir)
## FIXME: need to fix up distutils.cfg
-OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3],
+OK_ABS_SCRIPTS = ['python', py_version,
'activate', 'activate.bat', 'activate_this.py',
'activate.fish', 'activate.csh']
@@ -1647,7 +1648,7 @@ def fixup_scripts(home_dir, bin_dir):
'%s /c' % os.path.normcase(os.environ.get('COMSPEC', 'cmd.exe')),
'', '.exe')
else:
- new_shebang_args = ('/usr/bin/env', sys.version[:3], '')
+ new_shebang_args = ('/usr/bin/env', version, '')
# This is what we expect at the top of scripts:
shebang = '#!%s' % os.path.normcase(os.path.join(
diff --git a/virtualenv_embedded/activate_this.py b/virtualenv_embedded/activate_this.py
index f18193b..8272888 100644
--- a/virtualenv_embedded/activate_this.py
+++ b/virtualenv_embedded/activate_this.py
@@ -19,7 +19,7 @@ base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if sys.platform == 'win32':
site_packages = os.path.join(base, 'Lib', 'site-packages')
else:
- site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages')
+ site_packages = os.path.join(base, 'lib', 'python%s.%s' % (sys.version_info[0], sys.version_info[1]), 'site-packages')
prev_sys_path = list(sys.path)
import site
site.addsitedir(site_packages)
diff --git a/virtualenv_embedded/site.py b/virtualenv_embedded/site.py
index 7969769..b3603ff 100644
--- a/virtualenv_embedded/site.py
+++ b/virtualenv_embedded/site.py
@@ -134,7 +134,7 @@ def addbuilddir():
"""Append ./build/lib.<platform> in case we're running in the build dir
(especially for Guido :-)"""
from distutils.util import get_platform
- s = "build/lib.%s-%.3s" % (get_platform(), sys.version)
+ s = "build/lib.%s-%s.%s" % (get_platform(), sys.version_info[0], sys.version_info[1])
if hasattr(sys, 'gettotalrefcount'):
s += '-pydebug'
s = os.path.join(os.path.dirname(sys.path[-1]), s)
@@ -162,7 +162,7 @@ def addpackage(sitedir, name, known_paths):
reset = 0
fullname = os.path.join(sitedir, name)
try:
- f = open(fullname, "rU")
+ f = open(fullname, "r")
except IOError:
return
try:
@@ -222,21 +222,21 @@ def addsitepackages(known_paths, sys_prefix=sys.prefix, exec_prefix=sys.exec_pre
if prefix.startswith("/System/Library/Frameworks/"): # Apple's Python
- sitedirs = [os.path.join("/Library/Python", sys.version[:3], "site-packages"),
+ sitedirs = [os.path.join("/Library/Python", "%s.%s" % (sys.version_info[0], sys.version_info[1]), "site-packages"),
os.path.join(prefix, "Extras", "lib", "python")]
else: # any other Python distros on OSX work this way
sitedirs = [os.path.join(prefix, "lib",
- "python" + sys.version[:3], "site-packages")]
+ "python" + "%s.%s" % (sys.version_info[0], sys.version_info[1]), "site-packages")]
elif os.sep == '/':
sitedirs = [os.path.join(prefix,
"lib",
- "python" + sys.version[:3],
+ "python%s.%s" % (sys.version_info[0], sys.version_info[1]),
"site-packages"),
os.path.join(prefix, "lib", "site-python"),
- os.path.join(prefix, "python" + sys.version[:3], "lib-dynload")]
- lib64_dir = os.path.join(prefix, "lib64", "python" + sys.version[:3], "site-packages")
+ os.path.join(prefix, "python%s.%s" % (sys.version_info[0], sys.version_info[1]), "lib-dynload")]
+ lib64_dir = os.path.join(prefix, "lib64", "python%s.%s" % (sys.version_info[0], sys.version_info[1]), "site-packages")
if (os.path.exists(lib64_dir) and
os.path.realpath(lib64_dir) not in [os.path.realpath(p) for p in sitedirs]):
if _is_64bit:
@@ -251,11 +251,11 @@ def addsitepackages(known_paths, sys_prefix=sys.prefix, exec_prefix=sys.exec_pre
pass
# Debian-specific dist-packages directories:
sitedirs.append(os.path.join(prefix, "local/lib",
- "python" + sys.version[:3],
+ "python%s.%s" % (sys.version_info[0], sys.version_info[1]),
"dist-packages"))
if sys.version[0] == '2':
sitedirs.append(os.path.join(prefix, "lib",
- "python" + sys.version[:3],
+ "python%s.%s" % (sys.version_info[0], sys.version_info[1]),
"dist-packages"))
else:
sitedirs.append(os.path.join(prefix, "lib",
@@ -275,7 +275,7 @@ def addsitepackages(known_paths, sys_prefix=sys.prefix, exec_prefix=sys.exec_pre
os.path.join(home,
'Library',
'Python',
- sys.version[:3],
+ '%s.%s' % (sys.version_info[0], sys.version_info[1]),
'site-packages'))
for sitedir in sitedirs:
if os.path.isdir(sitedir):
@@ -335,7 +335,7 @@ def addusersitepackages(known_paths):
else:
USER_BASE = joinuser(base, "Python")
USER_SITE = os.path.join(USER_BASE,
- "Python" + sys.version[0] + sys.version[2],
+ "Python%s%s" % (sys.version_info[0], sys.version_info[1]),
"site-packages")
else:
if env_base:
@@ -343,7 +343,7 @@ def addusersitepackages(known_paths):
else:
USER_BASE = joinuser("~", ".local")
USER_SITE = os.path.join(USER_BASE, "lib",
- "python" + sys.version[:3],
+ "python%s.%s" % (sys.version_info[0], sys.version_info[1]),
"site-packages")
if ENABLE_USER_SITE and os.path.isdir(USER_SITE):
@@ -351,7 +351,7 @@ def addusersitepackages(known_paths):
if ENABLE_USER_SITE:
for dist_libdir in ("lib", "local/lib"):
user_site = os.path.join(USER_BASE, dist_libdir,
- "python" + sys.version[:3],
+ "python%s.%s" % (sys.version_info[0], sys.version_info[1]),
"dist-packages")
if os.path.isdir(user_site):
addsitedir(user_site, known_paths)
@@ -426,7 +426,7 @@ class _Printer(object):
for filename in self.__files:
filename = os.path.join(dir, filename)
try:
- fp = open(filename, "rU")
+ fp = open(filename, "r")
data = fp.read()
fp.close()
break
@@ -581,9 +581,9 @@ def virtual_install_main_packages():
elif sys.platform == 'win32':
paths = [os.path.join(sys.real_prefix, 'Lib'), os.path.join(sys.real_prefix, 'DLLs')]
else:
- paths = [os.path.join(sys.real_prefix, 'lib', 'python'+sys.version[:3])]
+ paths = [os.path.join(sys.real_prefix, 'lib', 'python%s.%s' % (sys.version_info[0], sys.version_info[1]))]
hardcoded_relative_dirs = paths[:] # for the special 'darwin' case below
- lib64_path = os.path.join(sys.real_prefix, 'lib64', 'python'+sys.version[:3])
+ lib64_path = os.path.join(sys.real_prefix, 'lib64', 'python%s.%s' % (sys.version_info[0], sys.version_info[1]))
if os.path.exists(lib64_path):
if _is_64bit:
paths.insert(0, lib64_path)
@@ -600,7 +600,7 @@ def virtual_install_main_packages():
# This is a non-multiarch aware Python. Fallback to the old way.
arch = sys.platform
plat_path = os.path.join(sys.real_prefix, 'lib',
- 'python'+sys.version[:3],
+ 'python%s.%s' % (sys.version_info[0], sys.version_info[1]),
'plat-%s' % arch)
if os.path.exists(plat_path):
paths.append(plat_path)

@ -1,20 +0,0 @@
diff --git a/virtualenv.py b/virtualenv.py
index e5d0883..34d2160 100755
--- a/virtualenv.py
+++ b/virtualenv.py
@@ -4,6 +4,15 @@
import os
import sys
+# The way virtualenv < 20 creates virtual environments
+# is not compatible with Python 3.11+ "frozen standard library modules"
+# https://docs.python.org/3.11/whatsnew/3.11.html#frozen-imports-static-code-objects
+if sys.version_info >= (3, 11):
+ venv_cmd = 'python{0.major}.{0.minor} -m venv'.format(sys.version_info)
+ sys.exit('ERROR: Virtual environments created by virtualenv < 20 '
+ 'are not compatible with Python 3.11.\n'
+ 'ERROR: Use `{}` instead.'.format(venv_cmd))
+
# If we are running in a new interpreter to create a virtualenv,
# we do NOT want paths from our existing location interfering with anything,
# So we remove this file's directory from sys.path - most likely to be

@ -1,125 +0,0 @@
From b7b8a713d9f1ebac6430fd0fc10175ed37b834ee Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Thu, 18 Mar 2021 13:08:52 +0100
Subject: [PATCH] rpm
---
virtualenv.py | 66 ++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 55 insertions(+), 11 deletions(-)
diff --git a/virtualenv.py b/virtualenv.py
index 5699998..9854324 100755
--- a/virtualenv.py
+++ b/virtualenv.py
@@ -39,9 +39,9 @@ except ImportError:
__version__ = "15.1.0"
virtualenv_version = __version__ # legacy
-if sys.version_info < (2, 6):
+if sys.version_info < (2, 7):
print('ERROR: %s' % sys.exc_info()[1])
- print('ERROR: this script requires Python 2.6 or greater.')
+ print('ERROR: this script requires Python 2.7 or greater.')
sys.exit(101)
try:
@@ -399,6 +399,8 @@ def _find_file(filename, dirs):
def file_search_dirs():
here = os.path.dirname(os.path.abspath(__file__))
dirs = [here, join(here, 'virtualenv_support')]
+ dirs.insert(1, '/usr/share/python{}-wheels'.format(sys.version_info[0]))
+ dirs.insert(1, '/usr/share/python{}{}-wheels'.format(*sys.version_info[:2]))
if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv':
# Probably some boot script; just in case virtualenv is installed...
try:
@@ -859,7 +861,12 @@ def install_wheel(project_names, py_executable, search_dirs=None,
import tempfile
import os
- import pip
+ try:
+ from pip._internal import main as _main
+ if type(_main) is type(sys): # <type 'module'>
+ _main = _main.main # nested starting in Pip 19.3
+ except ImportError:
+ from pip import main as _main
try:
cert_data = pkgutil.get_data("pip._vendor.requests", "cacert.pem")
@@ -878,7 +885,7 @@ def install_wheel(project_names, py_executable, search_dirs=None,
args += ["--cert", cert_file.name]
args += sys.argv[1:]
- sys.exit(pip.main(args))
+ sys.exit(_main(args))
finally:
if cert_file is not None:
os.remove(cert_file.name)
@@ -1038,20 +1045,57 @@ def change_prefix(filename, dst_prefix):
assert False, "Filename %s does not start with any of these prefixes: %s" % \
(filename, prefixes)
-def copy_required_modules(dst_prefix, symlink):
- import imp
+def find_module_filename(modname):
+ if sys.version_info < (3, 4):
+ # noinspection PyDeprecation
+ import imp
+
+ try:
+ file_handler, filepath, _ = imp.find_module(modname)
+ except ImportError:
+ return None
+ else:
+ if file_handler is not None:
+ file_handler.close()
+ return filepath
+ else:
+ import importlib.util
+ if sys.version_info < (3, 5):
+
+ def find_spec(modname):
+ # noinspection PyDeprecation
+ loader = importlib.find_loader(modname)
+ if loader is None:
+ return None
+ else:
+ return importlib.util.spec_from_loader(modname, loader)
+
+ else:
+ find_spec = importlib.util.find_spec
+
+ spec = find_spec(modname)
+ if spec is None:
+ return None
+ if not os.path.exists(spec.origin):
+ # https://bitbucket.org/pypy/pypy/issues/2944/origin-for-several-builtin-modules
+ # on pypy3, some builtin modules have a bogus build-time file path, ignore them
+ return None
+ filepath = spec.origin
+ # https://www.python.org/dev/peps/pep-3147/#file guarantee to be non-cached
+ if os.path.basename(filepath) == "__init__.py":
+ filepath = os.path.dirname(filepath)
+ return filepath
+
+def copy_required_modules(dst_prefix, symlink):
for modname in REQUIRED_MODULES:
if modname in sys.builtin_module_names:
logger.info("Ignoring built-in bootstrap module: %s" % modname)
continue
- try:
- f, filename, _ = imp.find_module(modname)
- except ImportError:
+ filename = find_module_filename(modname)
+ if filename is None:
logger.info("Cannot import bootstrap module: %s" % modname)
else:
- if f is not None:
- f.close()
# special-case custom readline.so on OS X, but not for pypy:
if modname == 'readline' and sys.platform == 'darwin' and not (
is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))):
--
2.30.2

@ -1,516 +0,0 @@
# python2X and python3X are built form the same module, so we need a conditional
# for python[23] bits the state of the conditional is not important in the spec,
# it is set in modulemd
%bcond_without python2
%bcond_without python3
%bcond_with python36_module
Name: python-virtualenv
Version: 15.1.0
Release: 22%{?dist}
Summary: Tool to create isolated Python environments
Group: Development/Languages
License: MIT
URL: http://pypi.python.org/pypi/virtualenv
Source0: http://pypi.python.org/packages/source/v/virtualenv/virtualenv-%{version}.tar.gz
# virtualenv -p "/usr/bin/python3" venv fails if there are not packages installed
# under /usr/local/lib/pythonX.Y/site-packages. Check if exec_dir exists before
# listing it's content.
Patch0: check-exec_dir.patch
# Don't fail on missing certifi's cert
# https://github.com/pypa/virtualenv/pull/1252
Patch1: dont-fail-on-missing-certifi-cert.patch
# Changes related to RPM wheels:
# 1. Drop support for Python 2.6 because we don't have it in RHEL 8 and we don't want to
# bundle prehistoric wheels
# 2. Use wheels from /usr/share/python{2,3,38,39,...}-wheels
# 3. Add support for pip 19.3-ish by importing pip.main() from different locations
# 4. Use the importlib module rather than deprecated imp on Python 3
Patch2: rpm-wheels.patch
# Fixes for Python 3.10+
# Backports from upstream virtualenv 16.7:
#
# Adjusts the code to accept Python minor versions with two (or more) digits
# https://github.com/pypa/virtualenv/commit/311a909c10
#
# Use sysconfig.get_default_scheme() where available (added in Python 3.10)
# https://github.com/pypa/virtualenv/commit/b4aef0a53b
#
# Remove universal newline flag (removed in Python 3.11)
# https://github.com/pypa/virtualenv/commit/8b23c8296f
#
# Backports re-created from scratch, due to changes in formatting.
#
# Run bin/rebuild-script.py in %%build to regenerate the base64 embedded files!
Patch3: python3.10.patch
# The way virtualenv < 20 creates virtual environments
# is not compatible with Python 3.11+ "frozen standard library modules"
# https://docs.python.org/3.11/whatsnew/3.11.html#frozen-imports-static-code-objects
#
# This patch makes virtualenv explicitly error and suggest venv instead.
# See https://bugzilla.redhat.com/show_bug.cgi?id=2165702
Patch4: python3.11-error.patch
BuildArch: noarch
%if %{with python2}
BuildRequires: python2-devel
BuildRequires: python2-setuptools
# RPM installed wheels
BuildRequires: python2-pip-wheel
BuildRequires: python2-setuptools-wheel
BuildRequires: python2-wheel-wheel
%endif # with python2
%if %{with python3}
BuildRequires: python3-sphinx
BuildRequires: python3-setuptools
# RPM installed wheels
BuildRequires: python3-pip-wheel
BuildRequires: python3-setuptools-wheel
BuildRequires: python3-wheel-wheel
%if %{with python36_module}
BuildRequires: python36-devel
BuildRequires: python36-rpm-macros
%else
BuildRequires: python3-devel
%endif
%endif # with python3
%description
virtualenv is a tool to create isolated Python environments. virtualenv
is a successor to workingenv, and an extension of virtual-python. It is
written by Ian Bicking, and sponsored by the Open Planning Project. It is
licensed under an MIT-style permissive license.
%if %{with python2}
%package -n python2-virtualenv
Summary: Tool to create isolated Python environments
Requires: python2-setuptools
Requires: python2-devel
# RPM installed wheels
Requires: python2-pip-wheel
Requires: python2-setuptools-wheel
Requires: python2-wheel-wheel
Requires: (python3-wheel-wheel if python36)
Requires: (python38-wheel-wheel if python38)
Requires: (python39-wheel-wheel if python39)
%{?python_provide:%python_provide python2-virtualenv}
%description -n python2-virtualenv
virtualenv is a tool to create isolated Python environments. virtualenv
is a successor to workingenv, and an extension of virtual-python. It is
written by Ian Bicking, and sponsored by the Open Planning Project. It is
licensed under an MIT-style permissive license
%endif
%if %{with python3}
%package -n python-virtualenv-doc
Summary: Documentation for python virtualenv
%description -n python-virtualenv-doc
Documentation for python virtualenv.
%package -n python3-virtualenv
Summary: Tool to create isolated Python environments
Requires: python3-setuptools
# RPM installed wheels
Requires: python3-pip-wheel
Requires: python3-setuptools-wheel
Requires: python3-wheel-wheel
Requires: (python2-wheel-wheel if python2)
Requires: (python38-wheel-wheel if python38)
Requires: (python39-wheel-wheel if python39)
# Require alternatives version that implements the --keep-foreign flag
Requires(postun): alternatives >= 1.19.1-1
# For alternatives
Requires: python36
Requires(post): python36
Requires(postun): python36
%if %{with python36_module}
Requires: python36-devel
%else
Requires: python3-devel
%endif
%{?python_provide:%python_provide python3-virtualenv}
Provides: virtualenv = %{version}-%{release}
%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
written by Ian Bicking, and sponsored by the Open Planning Project. It is
licensed under an MIT-style permissive license
%endif # with python3
%prep
%setup -q -n virtualenv-%{version}
%{__sed} -i -e "1s|#!/usr/bin/env python||" virtualenv.py
%patch0 -p1
%patch1 -p1
%patch2 -p1
%patch3 -p1
%patch4 -p1
# Remove the wheels provided by RPM packages and argparse as it's only required for python 2.6
rm virtualenv_support/pip-*
rm virtualenv_support/setuptools-*
rm virtualenv_support/wheel-*
rm virtualenv_support/argparse-*
%build
# Regenerate base64 embedded files
%{?with_python2:%{__python2} bin/rebuild-script.py}
%{?with_python3:%{__python3} bin/rebuild-script.py}
# Build code
%{?with_python2:%{py2_build}}
# Build docs on Fedora
%if %{with python3}
%{__python3} setup.py build_sphinx
rm -f build/sphinx/html/.buildinfo
%{py3_build}
# Build docs on Fedora
%{__python3} setup.py build_sphinx
rm -f build/sphinx/html/.buildinfo
%endif # with python3
%install
%if %{with python3}
%{py3_install}
# rename binaries to use python3
mv %{buildroot}/%{_bindir}/virtualenv %{buildroot}/%{_bindir}/py3-virtualenv
# The versioned 3.x script was removed from upstream. Add it back.
cp %{buildroot}/%{_bindir}/py3-virtualenv %{buildroot}/%{_bindir}/virtualenv-%{python3_version}
# For alternatives
touch %{buildroot}/%{_bindir}/virtualenv-3
touch %{buildroot}/%{_bindir}/virtualenv
%endif # with python3
%if %{with python2}
%{py2_install}
# The versioned 2.x script was removed from upstream. Add it back.
cp %{buildroot}/%{_bindir}/virtualenv %{buildroot}/%{_bindir}/virtualenv-%{python2_version}
mv %{buildroot}/%{_bindir}/virtualenv %{buildroot}/%{_bindir}/virtualenv-2
%endif
%if %{with python3}
%post -n python3-virtualenv
alternatives --add-slave python3 %{_bindir}/python%{python3_version} \
%{_bindir}/virtualenv-3 \
virtualenv-3 \
%{_bindir}/virtualenv-%{python3_version}
alternatives --add-slave python3 %{_bindir}/python%{python3_version} \
%{_bindir}/virtualenv \
virtualenv \
%{_bindir}/virtualenv-3
%postun -n python3-virtualenv
# Do this only during uninstall process (not during update)
if [ $1 -eq 0 ]; then
alternatives --keep-foreign --remove-slave python3 %{_bindir}/python%{python3_version} \
virtualenv-3
alternatives --keep-foreign --remove-slave python3 %{_bindir}/python%{python3_version} \
virtualenv
fi
%endif
%if %{with python2}
%files -n python2-virtualenv
%license LICENSE.txt
%doc docs/*rst PKG-INFO AUTHORS.txt
%{python2_sitelib}/*
%{_bindir}/virtualenv-2
%{_bindir}/virtualenv-%{python2_version}
%endif
%if %{with python3}
# Include sphinx docs on Fedora
%files -n python-virtualenv-doc
%doc build/sphinx/*
%files -n python3-virtualenv
%license LICENSE.txt
%doc docs/*rst PKG-INFO AUTHORS.txt
%{_bindir}/py3-virtualenv
%ghost %{_bindir}/virtualenv
%ghost %{_bindir}/virtualenv-3
%{_bindir}/virtualenv-%{python3_version}
%{python3_sitelib}/virtualenv.py
%{python3_sitelib}/virtualenv_support/
%{python3_sitelib}/virtualenv-*.egg-info/
%{python3_sitelib}/__pycache__/*
%endif # with python3
%changelog
* Wed Feb 01 2023 Miro Hrončok <mhroncok@redhat.com> - 15.1.0-22
- Add a custom error message when users attempt to create Python 3.11+ virtual environments
- Resolves: rhbz#2165702
* Wed Jul 28 2021 Tomas Orsava <torsava@redhat.com> - 15.1.0-21
- Adjusted the postun scriptlets to enable upgrading to RHEL 9
- Resolves: rhbz#1933055
* Thu Mar 18 2021 Lumír Balhar <lbalhar@redhat.com> - 15.1.0-20
- Use python-version-specific wheels from Python modules
Resolves: rhbz#1917971
* Fri Jun 21 2019 Miro Hrončok <mhroncok@redhat.com> - 15.1.0-19
- Use wheels from RPM packages (rhbz#1659550) (rhbz#1659551)
- Fail with a warning on Python versions < 2.7
* Thu Apr 25 2019 Tomas Orsava <torsava@redhat.com> - 15.1.0-18
- Bumping due to problems with modular RPM upgrade path
- Resolves: rhbz#1695587
* Fri Dec 14 2018 Miro Hrončok <mhroncok@redhat.com> - 15.1.0-17
- Don't fail on missing certifi's cert bundle
- Resolves: rhbz#1659440
* Wed Nov 28 2018 Tomas Orsava <torsava@redhat.com> - 15.1.0-16
- Provide the package name `virtualenv` and the /usr/bin/virtualenv binary
- Resolves: rhbz#1649958
* Thu Oct 04 2018 Lumír Balhar <lbalhar@redhat.com> - 15.1.0-15
- Fix alternatives - post and postun sections only with python3
- Resolves: rhbz#1633534
* Mon Oct 01 2018 Lumír Balhar <lbalhar@redhat.com> - 15.1.0-14
- Add alternatives for virtualenv-3
- Resolves: rhbz#1633534
* Wed Aug 15 2018 Lumír Balhar <lbalhar@redhat.com> - 15.1.0-13
- Remove virtualenv-3 executable. This will be provided by python3 module.
- Resolves: rhbz#1615727
* Wed Aug 08 2018 Lumír Balhar <lbalhar@redhat.com> - 15.1.0-12
- Remove unversioned binaries from python2 subpackage
- Resolves: rhbz#1613343
* Sat Aug 04 2018 Lumír Balhar <lbalhar@redhat.com> - 15.1.0-11
- Fixed conditions
* Tue Jul 31 2018 Lumír Balhar <lbalhar@redhat.com> - 15.1.0-10
- Switch python3 coditions to bcond
* Tue Jul 17 2018 Tomas Orsava <torsava@redhat.com> - 15.1.0-9
- BuildRequire also python36-rpm-macros as part of the python36 module build
* Wed Jul 04 2018 Miro Hrončok <mhroncok@redhat.com> - 15.1.0-8
- Add a bcond for python2
* Thu Jun 14 2018 Tomas Orsava <torsava@redhat.com> - 15.1.0-7
- Switch to using Python 3 version of sphinx
* Mon Apr 30 2018 Tomas Orsava <torsava@redhat.com> - 15.1.0-6
- Require the python36-devel package when building for the python36 module
* Wed Feb 28 2018 Iryna Shcherbina <ishcherb@redhat.com> - 15.1.0-5
- Update Python 2 dependency declarations to new packaging standards
(See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3)
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 15.1.0-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
* Fri Sep 29 2017 Troy Dawson <tdawson@redhat.com> - 15.1.0-3
- Cleanup spec file conditionals
* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 15.1.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
* Mon Jun 12 2017 Steve Milner <smilner@redhat.com> - 15.1.0-1
- Update to 15.1.0 per https://bugzilla.redhat.com/show_bug.cgi?id=1454962
* Fri Feb 17 2017 Michal Cyprian <mcyprian@redhat.com> - 15.0.3-6
- Check if exec_dir exists before listing it's content during venv create process
* Sat Feb 11 2017 Fedora Release Engineering <releng@fedoraproject.org> - 15.0.3-5
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
* Wed Jan 4 2017 Steve Milner <smilner@redhat.com> - 15.0.3-4
- Updated version binaries per discussion at bz#1385240.
* Tue Dec 13 2016 Stratakis Charalampos <cstratak@redhat.com> - 15.0.3-3
- Rebuild for Python 3.6
* Mon Oct 17 2016 Steve Milner <smilner@redhat.com> - 15.0.3-2
- Added MAJOR symlinks per bz#1385240.
* Mon Aug 8 2016 Steve Milner <smilner@redhat.com> - 15.0.3-1
- Update for upstream release.
* Tue Jul 19 2016 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 14.0.6-2
- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages
* Sun Feb 21 2016 Orion Poplawski <orion@cora.nwra.com> - 14.0.6-1
- Update to 14.0.6
* Tue Feb 2 2016 Orion Poplawski <orion@cora.nwra.com> - 13.1.2-4
- Modernize spec
- Fix python3 package file ownership
* Wed Dec 2 2015 Orion Poplawski <orion@cora.nwra.com> - 13.1.2-3
- Move documentation to separate package (bug #1219139)
* Wed Oct 14 2015 Robert Kuska <rkuska@redhat.com> - 13.1.2-2
- Rebuilt for Python3.5 rebuild
* Mon Aug 24 2015 Steve Milner <smilner@redhat.com> - 13.1.2-1
- Update for upstream release.
* Thu Jun 18 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 12.0.7-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
* Mon Mar 16 2015 Matej Stuchlik <mstuchli@redhat.com> - 12.0.7-1
- Update to 12.0.7
* Thu Jan 15 2015 Matthias Runge <mrunge@redhat.com> - 1.11.6-2
- add a python3-package, thanks to Matej Stuchlik (rhbz#1179150)
* Wed Jul 09 2014 Matthias Runge <mrunge@redhat.com> - 1.11.6-1
- update to 1.11.6:
Upstream updated setuptools to 3.6, updated pip to 1.5.6
* Sun Jun 08 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.10.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
* Thu Aug 15 2013 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.10.1-1
- Upstream upgraded pip to v1.4.1
- Upstream upgraded setuptools to v0.9.8 (fixes CVE-2013-1633)
* Sun Aug 04 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.9.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
* Tue May 14 2013 Toshio Kuratomi <toshio@fedoraproject.org> - 1.9.1-1
- Update to upstream 1.9.1 because of security issues with the bundled
python-pip in older releases. This is just a quick fix until a
python-virtualenv maintainer can unbundle the python-pip package
see: https://bugzilla.redhat.com/show_bug.cgi?id=749378
* Thu Feb 14 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7.2-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
* Tue Aug 14 2012 Steve Milner <me@stevemilner.org> - 1.7.2-1
- Update for upstream bug fixes.
- Added path for versioned binary.
- Patch no longer required.
* Sat Jul 21 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7.1.2-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
* Wed Mar 14 2012 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.7.1.2-1
- Update for upstream bug fixes.
- Added patch for sphinx building
* Sat Jan 14 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
* Tue Dec 20 2011 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.7-1
- Update for https://bugzilla.redhat.com/show_bug.cgi?id=769067
* Wed Feb 09 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.5.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
* Sat Oct 16 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.5.1-1
- Added _weakrefset requirement for Python 2.7.1.
- Add support for PyPy.
- Uses a proper temporary dir when installing environment requirements.
- Add --prompt option to be able to override the default prompt prefix.
- Add fish and csh activate scripts.
* Thu Jul 22 2010 David Malcolm <dmalcolm@redhat.com> - 1.4.8-4
- Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild
* Wed Jul 7 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.8-3
- Fixed EPEL installation issue from BZ#611536
* Wed Jun 9 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.8-2
- Only replace the python shebang on the first line (Robert Buchholz)
* Wed Apr 28 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.8-1
- update pip to 0.7
- move regen-docs into bin/
- Fix #31, make activate_this.py work on Windows (use Lib/site-packages)
unset PYTHONHOME envioronment variable -- first step towards fixing the PYTHONHOME issue; see e.g. https://bugs.launchpad.net/virtualenv/+bug/290844
- unset PYTHONHOME in the (Unix) activate script (and reset it in deactivate())
- use the activate.sh in virtualenv.py via running bin/rebuild-script.py
- add warning message if PYTHONHOME is set
* Fri Apr 2 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.6-1
- allow script creation without setuptools
- fix problem with --relocate when bin/ has subdirs (fixes #12)
- Allow more flexible .pth file fixup
- make nt a required module, along with posix. it may not be a builtin module on jython
- don't mess with PEP 302-supplied __file__, from CPython, and merge in a small startup optimization for Jython, from Jython
* Tue Dec 22 2009 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.3-1
- Updated for upstream release.
* Thu Nov 12 2009 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.2-1
- Updated for upstream release.
* Sun Jul 26 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.3-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
* Tue Apr 28 2009 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3.3-1
- Updated for upstream release.
* Thu Feb 26 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.2-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
* Thu Dec 25 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3.2-1
- Updated for upstream release.
* Thu Dec 04 2008 Ignacio Vazquez-Abrams <ivazqueznet+rpm@gmail.com> - 1.3.1-4
- Rebuild for Python 2.6
* Mon Dec 1 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3.1-3
- Added missing dependencies.
* Sat Nov 29 2008 Ignacio Vazquez-Abrams <ivazqueznet+rpm@gmail.com> - 1.3.1-2
- Rebuild for Python 2.6
* Fri Nov 28 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3.1-1
- Updated for upstream release
* Sun Sep 28 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3-1
- Updated for upstream release
* Sat Aug 30 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.2-1
- Updated for upstream release
* Fri Aug 29 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.1-3
- Updated from review notes
* Thu Aug 28 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.1-2
- Updated from review notes
* Tue Aug 26 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.1-1
- Initial Version

@ -0,0 +1,390 @@
* Wed Jan 03 2024 Miro Hrončok <mhroncok@redhat.com> - 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 Jul 27 2023 Benjamin A. Beasley <code@musicinmybrain.net> - 20.20.0-2
- Remove workarounds for old hatchling
* Wed Mar 01 2023 Lumír Balhar <lbalhar@redhat.com> - 20.20.0-1
- Update to 20.20.0 (rhbz#2174221)
* Thu Feb 09 2023 Lumír Balhar <lbalhar@redhat.com> - 20.19.0-1
- Update to 20.19.0 (rhbz#2167499)
* Mon Jan 30 2023 Miro Hrončok <mhroncok@redhat.com> - 20.17.1-2
- Use wheels from /usr/share/python3.11-wheels when creating Python 3.11 virtual environments
- Require Python 3.11 wheels when Python 3.11 is installed
* Wed Dec 07 2022 Lumír Balhar <lbalhar@redhat.com> - 20.17.1-1
- Update to 20.17.1 (rhbz#2151044)
* Thu Dec 01 2022 Lumír Balhar <lbalhar@redhat.com> - 20.17.0-1
- Update to 20.17.0 (rhbz#2148907)
* Mon Nov 14 2022 Lumír Balhar <lbalhar@redhat.com> - 20.16.7-1
- Update to 20.16.7 (#2142311)
* Thu Oct 27 2022 Lumír Balhar <lbalhar@redhat.com> - 20.16.6-1
- Update to 20.16.6
Resolves: rhbz#2137713
* Wed Jun 29 2022 Lumír Balhar <lbalhar@redhat.com> - 20.15.1-1
- Update to 20.15.1
Resolves: rhbz#2101975
* Sun Jun 26 2022 Lumír Balhar <lbalhar@redhat.com> - 20.15.0-1
- Update to 20.15.0
Resolves: rhbz#2101126
* Mon Mar 21 2022 Lumír Balhar <lbalhar@redhat.com> - 20.13.4-1
- Update to 20.13.4
Resolves: rhbz#2065839
* Mon Mar 14 2022 Benjamin A. Beasley <code@musicinmybrain.net> - 20.13.3-3
- BR tcsh so we can test csh activation scripts
* Mon Mar 14 2022 Lumír Balhar <lbalhar@redhat.com> - 20.13.3-2
- Add explicit error when embed version of wheels is requested
Resolves: rhbz#2053948
* Tue Mar 08 2022 Lumír Balhar <lbalhar@redhat.com> - 20.13.3-1
- Update to 20.13.3
Resolves: rhbz#2061449
* Fri Feb 25 2022 Lumír Balhar <lbalhar@redhat.com> - 20.13.2-1
- Update to 20.13.2
Resolves: rhbz#2058146
* Sun Feb 06 2022 Lumír Balhar <lbalhar@redhat.com> - 20.13.1-1
- Update to 20.13.1
Resolves: rhbz#2051025
* Mon Jan 03 2022 Lumír Balhar <lbalhar@redhat.com> - 20.13.0-1
- Update to 20.13.0
Resolves: rhbz#2035895
* Mon Nov 08 2021 Lumír Balhar <lbalhar@redhat.com> - 20.10.0-2
- Remove hack for local/ prefixes
* Tue Nov 02 2021 Lumír Balhar <lbalhar@redhat.com> - 20.10.0-1
- Update to 20.10.0
Resolves: rhbz#2019116
* Mon Oct 25 2021 Lumír Balhar <lbalhar@redhat.com> - 20.9.0-1
- Update to 20.9.0
Resolves: rhbz#2016758
* Wed Oct 06 2021 Lumír Balhar <lbalhar@redhat.com> - 20.8.1-1
- Update to 20.8.1
Resoves: rhbz#2007595
* Wed Oct 06 2021 Miro Hrončok <mhroncok@redhat.com> - 20.7.2-2
- Remove /local/ part from virtualenv paths
Resolves: rhbz#2011455
* Mon Aug 16 2021 Lumír Balhar <lbalhar@redhat.com> - 20.7.2-1
- Update to 20.7.2
Resolves: rhbz#1991618
* Sun Aug 01 2021 Lumír Balhar <lbalhar@redhat.com> - 20.7.0-1
- Update to 20.7.0
Resolves: rhbz#1988721
* Wed Jul 21 2021 Lumír Balhar <lbalhar@redhat.com> - 20.6.0-1
- Update to 20.6.0
Resolves: rhbz#1981792
* Fri Jun 04 2021 Python Maint <python-maint@redhat.com> - 20.4.7-3
- Rebuilt for Python 3.10
* Wed Jun 02 2021 Python Maint <python-maint@redhat.com> - 20.4.7-2
- Bootstrap for Python 3.10
* Tue May 25 2021 Lumír Balhar <lbalhar@redhat.com> - 20.4.7-1
- Update to 20.4.7
Resolves: rhbz#1964115
* Wed Apr 21 2021 Lumír Balhar <lbalhar@redhat.com> - 20.4.4-1
- Update to 20.4.4
Resolves: rhbz#1951515
* Wed Mar 17 2021 Lumír Balhar <lbalhar@redhat.com> - 20.4.3-1
- Update to 20.4.3
Resolves: rhbz#1939428
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 20.4.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Tue Jan 19 2021 Lumír Balhar <lbalhar@redhat.com> - 20.4.0-1
- Update to 20.4.0 (#1915682)
* Tue Jan 12 2021 Lumír Balhar <lbalhar@redhat.com> - 20.3.0-1
- Update to 20.3.0 (#1914641)
* Mon Nov 23 2020 Lumír Balhar <lbalhar@redhat.com> - 20.2.1-1
- Update to 20.2.1 (#1900253)
* Wed Nov 18 2020 Lumír Balhar <lbalhar@redhat.com> - 20.1.0-1
- Update to 20.1.0 (#1891297)
* Fri Oct 02 2020 Lumír Balhar <lbalhar@redhat.com> - 20.0.32-1
- Update to 20.0.32 (#1884449)
* Thu Sep 03 2020 Lumír Balhar <lbalhar@redhat.com> - 20.0.31-1
- Update to 20.0.31 (#1869352)
* Thu Aug 06 2020 Lumír Balhar <lbalhar@redhat.com> - 20.0.30-1
- Update to 20.0.30 (#1862562)
* Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 20.0.28-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Fri Jul 24 2020 Miro Hrončok <mhroncok@redhat.com> - 20.0.28-1
- Update to 20.0.28
- Fixes rhbz#1860272
* Thu Jul 23 2020 Lumír Balhar <lbalhar@redhat.com> - 20.0.27-1
- Update to 20.0.27 (#1854551)
* Tue Jun 23 2020 Lumír Balhar <lbalhar@redhat.com> - 20.0.25-1
- Update to 20.0.25
* Mon Jun 15 2020 Lumír Balhar <lbalhar@redhat.com> - 20.0.23-1
- Update to 20.0.23 (#1742034)
* Sat May 23 2020 Miro Hrončok <mhroncok@redhat.com> - 16.7.10-2
- Rebuilt for Python 3.9
* Tue Feb 25 2020 Miro Hrončok <mhroncok@redhat.com> - 16.7.10-1
- Update to 16.7.10
- Explicitly require setuptools < 44 with Python 3.4
* Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 16.7.3-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Tue Sep 03 2019 Miro Hrončok <mhroncok@redhat.com> - 16.7.3-2
- Prefer wheels bundled in Python's ensurepip module over the RPM built ones
- This allows continuing support for Python 3.4 in Fedora 32+
* Wed Aug 21 2019 Charalampos Stratakis <cstratak@redhat.com> - 16.7.3-1
- Update to 16.7.3 (#1742034)
* Fri Aug 16 2019 Miro Hrončok <mhroncok@redhat.com> - 16.6.1-3
- Rebuilt for Python 3.8
* Mon Jul 29 2019 Miro Hrončok <mhroncok@redhat.com> - 16.6.1-2
- Drop python2-virtualenv
* Thu Jul 11 2019 Miro Hrončok <mhroncok@redhat.com> - 16.6.1-1
- Update to 16.6.1 (#1699031)
- No more Python 2.6 or Jython support
- Drop runtime dependency on pythonX-devel
* Sat Feb 02 2019 Fedora Release Engineering <releng@fedoraproject.org> - 16.0.0-7
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Thu Dec 13 2018 Miro Hrončok <mhroncok@redhat.com> - 16.0.0-6
- Don't fail on missing certifi's cert bundle (#1655253)
* Wed Aug 15 2018 Miro Hrončok <mhroncok@redhat.com> - 16.0.0-5
- Use wheels from RPM packages
- Put wheels needed for Python 2.6 into a subpackage
- Only have one /usr/bin/virtualenv (#1599422)
- Provide "virtualenv" (#1502670)
* Wed Jul 18 2018 Miro Hrončok <mhroncok@redhat.com> - 16.0.0-4
- Reintroduce support for Python 2.6 (#1602347)
- Add missing bundled provides
* Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 16.0.0-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Sat Jun 16 2018 Miro Hrončok <mhroncok@redhat.com> - 16.0.0-2
- Rebuilt for Python 3.7
* Thu May 17 2018 Steve Milner <smilner@redhat.com> - 16.0.0-1
- Updated for upstream release.
* Wed Feb 28 2018 Iryna Shcherbina <ishcherb@redhat.com> - 15.1.0-5
- Update Python 2 dependency declarations to new packaging standards
(See https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3)
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 15.1.0-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
* Fri Sep 29 2017 Troy Dawson <tdawson@redhat.com> - 15.1.0-3
- Cleanup spec file conditionals
* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 15.1.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
* Mon Jun 12 2017 Steve Milner <smilner@redhat.com> - 15.1.0-1
- Update to 15.1.0 per https://bugzilla.redhat.com/show_bug.cgi?id=1454962
* Fri Feb 17 2017 Michal Cyprian <mcyprian@redhat.com> - 15.0.3-6
- Check if exec_dir exists before listing it's content during venv create process
* Sat Feb 11 2017 Fedora Release Engineering <releng@fedoraproject.org> - 15.0.3-5
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
* Wed Jan 4 2017 Steve Milner <smilner@redhat.com> - 15.0.3-4
- Updated version binaries per discussion at bz#1385240.
* Tue Dec 13 2016 Stratakis Charalampos <cstratak@redhat.com> - 15.0.3-3
- Rebuild for Python 3.6
* Mon Oct 17 2016 Steve Milner <smilner@redhat.com> - 15.0.3-2
- Added MAJOR symlinks per bz#1385240.
* Mon Aug 8 2016 Steve Milner <smilner@redhat.com> - 15.0.3-1
- Update for upstream release.
* Tue Jul 19 2016 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 14.0.6-2
- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages
* Sun Feb 21 2016 Orion Poplawski <orion@cora.nwra.com> - 14.0.6-1
- Update to 14.0.6
* Tue Feb 2 2016 Orion Poplawski <orion@cora.nwra.com> - 13.1.2-4
- Modernize spec
- Fix python3 package file ownership
* Wed Dec 2 2015 Orion Poplawski <orion@cora.nwra.com> - 13.1.2-3
- Move documentation to separate package (bug #1219139)
* Wed Oct 14 2015 Robert Kuska <rkuska@redhat.com> - 13.1.2-2
- Rebuilt for Python3.5 rebuild
* Mon Aug 24 2015 Steve Milner <smilner@redhat.com> - 13.1.2-1
- Update for upstream release.
* Thu Jun 18 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 12.0.7-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
* Mon Mar 16 2015 Matej Stuchlik <mstuchli@redhat.com> - 12.0.7-1
- Update to 12.0.7
* Thu Jan 15 2015 Matthias Runge <mrunge@redhat.com> - 1.11.6-2
- add a python3-package, thanks to Matej Stuchlik (rhbz#1179150)
* Wed Jul 09 2014 Matthias Runge <mrunge@redhat.com> - 1.11.6-1
- update to 1.11.6:
Upstream updated setuptools to 3.6, updated pip to 1.5.6
* Sun Jun 08 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.10.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
* Thu Aug 15 2013 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.10.1-1
- Upstream upgraded pip to v1.4.1
- Upstream upgraded setuptools to v0.9.8 (fixes CVE-2013-1633)
* Sun Aug 04 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.9.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
* Tue May 14 2013 Toshio Kuratomi <toshio@fedoraproject.org> - 1.9.1-1
- Update to upstream 1.9.1 because of security issues with the bundled
python-pip in older releases. This is just a quick fix until a
python-virtualenv maintainer can unbundle the python-pip package
see: https://bugzilla.redhat.com/show_bug.cgi?id=749378
* Thu Feb 14 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7.2-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
* Tue Aug 14 2012 Steve Milner <me@stevemilner.org> - 1.7.2-1
- Update for upstream bug fixes.
- Added path for versioned binary.
- Patch no longer required.
* Sat Jul 21 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7.1.2-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
* Wed Mar 14 2012 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.7.1.2-1
- Update for upstream bug fixes.
- Added patch for sphinx building
* Sat Jan 14 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
* Tue Dec 20 2011 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.7-1
- Update for https://bugzilla.redhat.com/show_bug.cgi?id=769067
* Wed Feb 09 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.5.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
* Sat Oct 16 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.5.1-1
- Added _weakrefset requirement for Python 2.7.1.
- Add support for PyPy.
- Uses a proper temporary dir when installing environment requirements.
- Add --prompt option to be able to override the default prompt prefix.
- Add fish and csh activate scripts.
* Thu Jul 22 2010 David Malcolm <dmalcolm@redhat.com> - 1.4.8-4
- Rebuilt for https://fedoraproject.org/wiki/Features/Python_2.7/MassRebuild
* Wed Jul 7 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.8-3
- Fixed EPEL installation issue from BZ#611536
* Wed Jun 9 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.8-2
- Only replace the python shebang on the first line (Robert Buchholz)
* Wed Apr 28 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.8-1
- update pip to 0.7
- move regen-docs into bin/
- Fix #31, make activate_this.py work on Windows (use Lib/site-packages)
unset PYTHONHOME envioronment variable -- first step towards fixing the PYTHONHOME issue; see e.g. https://bugs.launchpad.net/virtualenv/+bug/290844
- unset PYTHONHOME in the (Unix) activate script (and reset it in deactivate())
- use the activate.sh in virtualenv.py via running bin/rebuild-script.py
- add warning message if PYTHONHOME is set
* Fri Apr 2 2010 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.6-1
- allow script creation without setuptools
- fix problem with --relocate when bin/ has subdirs (fixes #12)
- Allow more flexible .pth file fixup
- make nt a required module, along with posix. it may not be a builtin module on jython
- don't mess with PEP 302-supplied __file__, from CPython, and merge in a small startup optimization for Jython, from Jython
* Tue Dec 22 2009 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.3-1
- Updated for upstream release.
* Thu Nov 12 2009 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.4.2-1
- Updated for upstream release.
* Sun Jul 26 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.3-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
* Tue Apr 28 2009 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3.3-1
- Updated for upstream release.
* Thu Feb 26 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.2-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
* Thu Dec 25 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3.2-1
- Updated for upstream release.
* Thu Dec 04 2008 Ignacio Vazquez-Abrams <ivazqueznet+rpm@gmail.com> - 1.3.1-4
- Rebuild for Python 2.6
* Mon Dec 1 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3.1-3
- Added missing dependencies.
* Sat Nov 29 2008 Ignacio Vazquez-Abrams <ivazqueznet+rpm@gmail.com> - 1.3.1-2
- Rebuild for Python 2.6
* Fri Nov 28 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3.1-1
- Updated for upstream release
* Sun Sep 28 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.3-1
- Updated for upstream release
* Sat Aug 30 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.2-1
- Updated for upstream release
* Fri Aug 29 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.1-3
- Updated from review notes
* Thu Aug 28 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.1-2
- Updated from review notes
* Tue Aug 26 2008 Steve 'Ashcrow' Milner <me@stevemilner.org> - 1.1-1
- Initial Version

@ -0,0 +1,70 @@
From fc8e412fa8fe524ab3f112f02e865aa42608388b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= <Kulikjak@gmail.com>
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

@ -0,0 +1,445 @@
From 82dbaffa36fe317e331b2d22c5efbb6a0b6c7b19 Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Tue, 8 Oct 2024 09:15:09 +0200
Subject: [PATCH] Prevent command injection
---
docs/changelog/2768.bugfix.rst | 2 ++
src/virtualenv/activation/bash/activate.sh | 9 +++++----
src/virtualenv/activation/batch/__init__.py | 4 ++++
src/virtualenv/activation/cshell/activate.csh | 10 +++++-----
src/virtualenv/activation/fish/activate.fish | 8 ++++----
src/virtualenv/activation/nushell/__init__.py | 19 ++++++++++++++++++
src/virtualenv/activation/nushell/activate.nu | 8 ++++----
.../activation/powershell/__init__.py | 12 +++++++++++
.../activation/powershell/activate.ps1 | 6 +++---
src/virtualenv/activation/python/__init__.py | 6 +++++-
.../activation/python/activate_this.py | 6 +++---
src/virtualenv/activation/via_template.py | 15 ++++++++++++--
tests/conftest.py | 6 +++++-
tests/unit/activation/conftest.py | 3 +--
tests/unit/activation/test_batch.py | 10 +++++-----
tests/unit/activation/test_powershell.py | 20 ++++++++++++++-----
16 files changed, 105 insertions(+), 39 deletions(-)
create mode 100644 docs/changelog/2768.bugfix.rst
diff --git a/docs/changelog/2768.bugfix.rst b/docs/changelog/2768.bugfix.rst
new file mode 100644
index 0000000..7651eb6
--- /dev/null
+++ b/docs/changelog/2768.bugfix.rst
@@ -0,0 +1,2 @@
+Properly quote string placeholders in activation script templates to mitigate
+potential command injection - by :user:`y5c4l3`.
diff --git a/src/virtualenv/activation/bash/activate.sh b/src/virtualenv/activation/bash/activate.sh
index fb40db6..d047ea4 100644
--- a/src/virtualenv/activation/bash/activate.sh
+++ b/src/virtualenv/activation/bash/activate.sh
@@ -44,14 +44,14 @@ deactivate () {
# unset irrelevant variables
deactivate nondestructive
-VIRTUAL_ENV='__VIRTUAL_ENV__'
+VIRTUAL_ENV=__VIRTUAL_ENV__
if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then
VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV")
fi
export VIRTUAL_ENV
_OLD_VIRTUAL_PATH="$PATH"
-PATH="$VIRTUAL_ENV/__BIN_NAME__:$PATH"
+PATH="$VIRTUAL_ENV/"__BIN_NAME__":$PATH"
export PATH
# unset PYTHONHOME if set
@@ -62,8 +62,9 @@ fi
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1-}"
- if [ "x__VIRTUAL_PROMPT__" != x ] ; then
- PS1="(__VIRTUAL_PROMPT__) ${PS1-}"
+ if [ "x"__VIRTUAL_PROMPT__ != x ] ; then
+ PROMPT=__VIRTUAL_PROMPT__
+ PS1="(${PROMPT}) ${PS1-}"
else
PS1="(`basename \"$VIRTUAL_ENV\"`) ${PS1-}"
fi
diff --git a/src/virtualenv/activation/batch/__init__.py b/src/virtualenv/activation/batch/__init__.py
index 13ba097..b3e8540 100644
--- a/src/virtualenv/activation/batch/__init__.py
+++ b/src/virtualenv/activation/batch/__init__.py
@@ -13,6 +13,10 @@ class BatchActivator(ViaTemplateActivator):
yield "deactivate.bat"
yield "pydoc.bat"
+ @staticmethod
+ def quote(string):
+ return string
+
def instantiate_template(self, replacements, template, creator):
# ensure the text has all newlines as \r\n - required by batch
base = super().instantiate_template(replacements, template, creator)
diff --git a/src/virtualenv/activation/cshell/activate.csh b/src/virtualenv/activation/cshell/activate.csh
index 837dcda..fcec426 100644
--- a/src/virtualenv/activation/cshell/activate.csh
+++ b/src/virtualenv/activation/cshell/activate.csh
@@ -10,15 +10,15 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA
# Unset irrelevant variables.
deactivate nondestructive
-setenv VIRTUAL_ENV '__VIRTUAL_ENV__'
+setenv VIRTUAL_ENV __VIRTUAL_ENV__
set _OLD_VIRTUAL_PATH="$PATH:q"
-setenv PATH "$VIRTUAL_ENV:q/__BIN_NAME__:$PATH:q"
+setenv PATH "$VIRTUAL_ENV:q/"__BIN_NAME__":$PATH:q"
-if ('__VIRTUAL_PROMPT__' != "") then
- set env_name = '(__VIRTUAL_PROMPT__) '
+if (__VIRTUAL_PROMPT__ != "") then
+ set env_name = __VIRTUAL_PROMPT__
else
set env_name = '('"$VIRTUAL_ENV:t:q"') '
endif
@@ -42,7 +42,7 @@ if ( $do_prompt == "1" ) then
if ( "$prompt:q" =~ *"$newline:q"* ) then
:
else
- set prompt = "$env_name:q$prompt:q"
+ set prompt = '('"$env_name:q"') '"$prompt:q"
endif
endif
endif
diff --git a/src/virtualenv/activation/fish/activate.fish b/src/virtualenv/activation/fish/activate.fish
index 62f631e..3637d55 100644
--- a/src/virtualenv/activation/fish/activate.fish
+++ b/src/virtualenv/activation/fish/activate.fish
@@ -57,7 +57,7 @@ end
# Unset irrelevant variables.
deactivate nondestructive
-set -gx VIRTUAL_ENV '__VIRTUAL_ENV__'
+set -gx VIRTUAL_ENV __VIRTUAL_ENV__
# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling
if test (echo $FISH_VERSION | head -c 1) -lt 3
@@ -65,7 +65,7 @@ if test (echo $FISH_VERSION | head -c 1) -lt 3
else
set -gx _OLD_VIRTUAL_PATH $PATH
end
-set -gx PATH "$VIRTUAL_ENV"'/__BIN_NAME__' $PATH
+set -gx PATH "$VIRTUAL_ENV"'/'__BIN_NAME__ $PATH
# Unset `$PYTHONHOME` if set.
if set -q PYTHONHOME
@@ -87,8 +87,8 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
# Prompt override provided?
# If not, just prepend the environment name.
- if test -n '__VIRTUAL_PROMPT__'
- printf '(%s) ' '__VIRTUAL_PROMPT__'
+ if test -n __VIRTUAL_PROMPT__
+ printf '(%s) ' __VIRTUAL_PROMPT__
else
printf '(%s) ' (basename "$VIRTUAL_ENV")
end
diff --git a/src/virtualenv/activation/nushell/__init__.py b/src/virtualenv/activation/nushell/__init__.py
index 4e2ea77..c96af22 100644
--- a/src/virtualenv/activation/nushell/__init__.py
+++ b/src/virtualenv/activation/nushell/__init__.py
@@ -5,6 +5,25 @@ class NushellActivator(ViaTemplateActivator):
def templates(self):
yield "activate.nu"
+ @staticmethod
+ def quote(string):
+ """
+ Nushell supports raw strings like: r###'this is a string'###.
+
+ This method finds the maximum continuous sharps in the string and then
+ quote it with an extra sharp.
+ """
+ max_sharps = 0
+ current_sharps = 0
+ for char in string:
+ if char == "#":
+ current_sharps += 1
+ max_sharps = max(current_sharps, max_sharps)
+ else:
+ current_sharps = 0
+ wrapping = "#" * (max_sharps + 1)
+ return f"r{wrapping}'{string}'{wrapping}"
+
def replacements(self, creator, dest_folder): # noqa: U100
return {
"__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt,
diff --git a/src/virtualenv/activation/nushell/activate.nu b/src/virtualenv/activation/nushell/activate.nu
index 3da1519..569a8a1 100644
--- a/src/virtualenv/activation/nushell/activate.nu
+++ b/src/virtualenv/activation/nushell/activate.nu
@@ -31,8 +31,8 @@ export-env {
}
let is_windows = ($nu.os-info.name | str downcase) == 'windows'
- let virtual_env = '__VIRTUAL_ENV__'
- let bin = '__BIN_NAME__'
+ let virtual_env = __VIRTUAL_ENV__
+ let bin = __BIN_NAME__
let path_sep = (char esep)
let path_name = (if $is_windows {
if (has-env 'Path') {
@@ -73,10 +73,10 @@ export-env {
$new_env
} else {
# Creating the new prompt for the session
- let virtual_prompt = (if ('__VIRTUAL_PROMPT__' == '') {
+ let virtual_prompt = (if (__VIRTUAL_PROMPT__ == '') {
$'(char lparen)($virtual_env | path basename)(char rparen) '
} else {
- '(__VIRTUAL_PROMPT__) '
+ (__VIRTUAL_PROMPT__)
})
# Back up the old prompt builder
diff --git a/src/virtualenv/activation/powershell/__init__.py b/src/virtualenv/activation/powershell/__init__.py
index 4e74ecb..773d690 100644
--- a/src/virtualenv/activation/powershell/__init__.py
+++ b/src/virtualenv/activation/powershell/__init__.py
@@ -5,6 +5,18 @@ class PowerShellActivator(ViaTemplateActivator):
def templates(self):
yield "activate.ps1"
+ @staticmethod
+ def quote(string):
+ """
+ This should satisfy PowerShell quoting rules [1], unless the quoted
+ string is passed directly to Windows native commands [2].
+
+ [1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules
+ [2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters
+ """ # noqa: D205
+ string = string.replace("'", "''")
+ return f"'{string}'"
+
__all__ = [
"PowerShellActivator",
diff --git a/src/virtualenv/activation/powershell/activate.ps1 b/src/virtualenv/activation/powershell/activate.ps1
index d524347..346980d 100644
--- a/src/virtualenv/activation/powershell/activate.ps1
+++ b/src/virtualenv/activation/powershell/activate.ps1
@@ -35,18 +35,18 @@ $env:VIRTUAL_ENV = $VIRTUAL_ENV
New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH
-$env:PATH = "$env:VIRTUAL_ENV/__BIN_NAME____PATH_SEP__" + $env:PATH
+$env:PATH = "$env:VIRTUAL_ENV/" + __BIN_NAME__ + __PATH_SEP__ + $env:PATH
if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) {
function global:_old_virtual_prompt {
""
}
$function:_old_virtual_prompt = $function:prompt
- if ("__VIRTUAL_PROMPT__" -ne "") {
+ if (__VIRTUAL_PROMPT__ -ne "") {
function global:prompt {
# Add the custom prefix to the existing prompt
$previous_prompt_value = & $function:_old_virtual_prompt
- ("(__VIRTUAL_PROMPT__) " + $previous_prompt_value)
+ ((__VIRTUAL_PROMPT__) + $previous_prompt_value)
}
}
else {
diff --git a/src/virtualenv/activation/python/__init__.py b/src/virtualenv/activation/python/__init__.py
index a49444b..8f0971e 100644
--- a/src/virtualenv/activation/python/__init__.py
+++ b/src/virtualenv/activation/python/__init__.py
@@ -9,10 +9,14 @@ class PythonActivator(ViaTemplateActivator):
def templates(self):
yield "activate_this.py"
+ @staticmethod
+ def quote(string):
+ return repr(string)
+
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
+ lib_folders = os.pathsep.join(lib_folders.keys())
win_py2 = creator.interpreter.platform == "win32" and creator.interpreter.version_info.major == 2
replacements.update(
{
diff --git a/src/virtualenv/activation/python/activate_this.py b/src/virtualenv/activation/python/activate_this.py
index e8eeb84..976952e 100644
--- a/src/virtualenv/activation/python/activate_this.py
+++ b/src/virtualenv/activation/python/activate_this.py
@@ -14,7 +14,7 @@ except NameError:
raise AssertionError("You must use exec(open(this_file).read(), {'__file__': this_file}))")
bin_dir = os.path.dirname(abs_file)
-base = bin_dir[: -len("__BIN_NAME__") - 1] # strip away the bin part from the __file__, plus the path separator
+base = bin_dir[: -len(__BIN_NAME__) - 1] # strip away the bin part from the __file__, plus the path separator
# prepend bin to PATH (this file is inside the bin directory)
os.environ["PATH"] = os.pathsep.join([bin_dir] + os.environ.get("PATH", "").split(os.pathsep))
@@ -22,9 +22,9 @@ os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory
# add the virtual environments libraries to the host python import mechanism
prev_length = len(sys.path)
-for lib in "__LIB_FOLDERS__".split(os.pathsep):
+for lib in __LIB_FOLDERS__.split(os.pathsep):
path = os.path.realpath(os.path.join(bin_dir, lib))
- site.addsitedir(path.decode("utf-8") if "__DECODE_PATH__" else path)
+ site.addsitedir(path.decode("utf-8") if __DECODE_PATH__ else path)
sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length]
sys.real_prefix = sys.prefix
diff --git a/src/virtualenv/activation/via_template.py b/src/virtualenv/activation/via_template.py
index cc9dbda..b1dfa6b 100644
--- a/src/virtualenv/activation/via_template.py
+++ b/src/virtualenv/activation/via_template.py
@@ -1,4 +1,5 @@
import os
+import shlex
import sys
from abc import ABCMeta, abstractmethod
@@ -19,6 +20,16 @@ class ViaTemplateActivator(Activator, metaclass=ABCMeta):
def templates(self):
raise NotImplementedError
+ @staticmethod
+ def quote(string):
+ """
+ Quote strings in the activation script.
+
+ :param string: the string to quote
+ :return: quoted string that works in the activation script
+ """
+ return shlex.quote(string)
+
def generate(self, creator):
dest_folder = creator.bin_dir
replacements = self.replacements(creator, dest_folder)
@@ -58,8 +69,8 @@ class ViaTemplateActivator(Activator, metaclass=ABCMeta):
binary = read_binary(self.__module__, template)
text = binary.decode("utf-8", errors="strict")
for key, value in replacements.items():
- value = self._repr_unicode(creator, value)
- text = text.replace(key, value)
+ value_uni = self._repr_unicode(creator, value)
+ text = text.replace(key, self.quote(value_uni))
return text
@staticmethod
diff --git a/tests/conftest.py b/tests/conftest.py
index a7ec4e0..b2fae66 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -292,7 +292,11 @@ def is_inside_ci():
@pytest.fixture(scope="session")
def special_char_name():
- base = "e-$ èрт🚒♞中片-j"
+ base = "'\";&&e-$ èрт🚒♞中片-j"
+ if IS_WIN:
+ # get rid of invalid characters on Windows
+ base = base.replace('"', "")
+ base = base.replace(";", "")
# workaround for pypy3 https://bitbucket.org/pypy/pypy/issues/3147/venv-non-ascii-support-windows
encoding = "ascii" if IS_WIN else sys.getfilesystemencoding()
# let's not include characters that the file system cannot encode)
diff --git a/tests/unit/activation/conftest.py b/tests/unit/activation/conftest.py
index e24a4fc..2d8b3ce 100644
--- a/tests/unit/activation/conftest.py
+++ b/tests/unit/activation/conftest.py
@@ -4,7 +4,6 @@ import subprocess
import sys
from os.path import dirname, normcase
from pathlib import Path
-from shlex import quote
from subprocess import Popen
import pytest
@@ -146,7 +145,7 @@ class ActivationTester:
assert out[-1] == "None", raw
def quote(self, s):
- return quote(s)
+ return self.of_class.quote(s)
def python_cmd(self, cmd):
return f"{os.path.basename(sys.executable)} -c {self.quote(cmd)}"
diff --git a/tests/unit/activation/test_batch.py b/tests/unit/activation/test_batch.py
index 9e6d6d6..e95f7ea 100644
--- a/tests/unit/activation/test_batch.py
+++ b/tests/unit/activation/test_batch.py
@@ -1,5 +1,3 @@
-from shlex import quote
-
import pytest
from virtualenv.activation import BatchActivator
@@ -25,10 +23,12 @@ def test_batch(activation_tester_class, activation_tester, tmp_path):
return ["@echo off", "", "chcp 65001 1>NUL"] + super()._get_test_lines(activate_script)
def quote(self, s):
- """double quotes needs to be single, and single need to be double"""
- return "".join(("'" if c == '"' else ('"' if c == "'" else c)) for c in quote(s))
+ if '"' in s or " " in s:
+ text = s.replace('"', r"\"")
+ return f'"{text}"'
+ return s
def print_prompt(self):
- return "echo %PROMPT%"
+ return 'echo "%PROMPT%"'
activation_tester(Batch)
diff --git a/tests/unit/activation/test_powershell.py b/tests/unit/activation/test_powershell.py
index 761237f..f495353 100644
--- a/tests/unit/activation/test_powershell.py
+++ b/tests/unit/activation/test_powershell.py
@@ -1,5 +1,4 @@
import sys
-from shlex import quote
import pytest
@@ -19,10 +18,6 @@ def test_powershell(activation_tester_class, activation_tester, monkeypatch):
self.activate_cmd = "."
self.script_encoding = "utf-16"
- def quote(self, s):
- """powershell double quote needed for quotes within single quotes"""
- return quote(s).replace('"', '""')
-
def _get_test_lines(self, activate_script):
# for BATCH utf-8 support need change the character code page to 650001
return super()._get_test_lines(activate_script)
@@ -33,4 +28,19 @@ def test_powershell(activation_tester_class, activation_tester, monkeypatch):
def print_prompt(self):
return "prompt"
+ def quote(self, s):
+ """
+ Tester will pass strings to native commands on Windows so extra
+ parsing rules are used. Check `PowerShellActivator.quote` for more
+ details.
+ """
+ text = PowerShellActivator.quote(s)
+ return text.replace('"', '""') if sys.platform == "win32" else text
+
+ def activate_call(self, script):
+ # Commands are called without quotes in PowerShell
+ cmd = self.activate_cmd
+ scr = self.quote(str(script))
+ return f"{cmd} {scr}".strip()
+
activation_tester(PowerShell)
--
2.46.2

@ -0,0 +1,464 @@
From 71de653a714958d6649501b874009bafec74d3f4 Mon Sep 17 00:00:00 2001
From: Philipp A <flying-sheep@web.de>
Date: Tue, 23 Apr 2024 18:46:41 +0200
Subject: [PATCH 1/2] Allow builtin interpreter discovery to find specific
Python versions given a general spec (#2709)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Bernát Gábor <gaborjbernat@gmail.com>
Backported to 20.21.1 (without typing improvements).
---
docs/changelog/2709.bugfix.rst | 1 +
src/virtualenv/discovery/builtin.py | 78 +++++++++++++-------------
src/virtualenv/discovery/py_spec.py | 42 ++++++--------
tests/unit/discovery/test_discovery.py | 15 ++++-
4 files changed, 67 insertions(+), 69 deletions(-)
create mode 100644 docs/changelog/2709.bugfix.rst
diff --git a/docs/changelog/2709.bugfix.rst b/docs/changelog/2709.bugfix.rst
new file mode 100644
index 0000000..904da84
--- /dev/null
+++ b/docs/changelog/2709.bugfix.rst
@@ -0,0 +1 @@
+allow builtin discovery to discover specific interpreters (e.g. ``python3.12``) given an unspecific spec (e.g. ``python3``) - by :user:`flying-sheep`.
diff --git a/src/virtualenv/discovery/builtin.py b/src/virtualenv/discovery/builtin.py
index 40320d3..f79b5a5 100644
--- a/src/virtualenv/discovery/builtin.py
+++ b/src/virtualenv/discovery/builtin.py
@@ -1,6 +1,7 @@
import logging
import os
import sys
+from pathlib import Path
from virtualenv.info import IS_WIN
@@ -67,7 +68,7 @@ def get_interpreter(key, try_first_with, app_data=None, env=None):
proposed_paths.add(key)
-def propose_interpreters(spec, try_first_with, app_data, env=None):
+def propose_interpreters(spec, try_first_with, app_data=None, env=None): # noqa: C901, PLR0912
# 0. try with first
env = os.environ if env is None else env
for py_exe in try_first_with:
@@ -101,20 +102,17 @@ def propose_interpreters(spec, try_first_with, app_data, env=None):
for interpreter in propose_interpreters(spec, app_data, env):
yield interpreter, True
# finally just find on path, the path order matters (as the candidates are less easy to control by end user)
- paths = get_paths(env)
tested_exes = set()
- for pos, path in enumerate(paths):
- path_str = str(path)
- logging.debug(LazyPathDump(pos, path_str, env))
- for candidate, match in possible_specs(spec):
- found = check_path(candidate, path_str)
- if found is not None:
- exe = os.path.abspath(found)
- if exe not in tested_exes:
- tested_exes.add(exe)
- interpreter = PathPythonInfo.from_exe(exe, app_data, raise_on_error=False, env=env)
- if interpreter is not None:
- yield interpreter, match
+ find_candidates = path_exe_finder(spec)
+ for pos, path in enumerate(get_paths(env)):
+ logging.debug(LazyPathDump(pos, path, env))
+ for exe, impl_must_match in find_candidates(path):
+ if exe in tested_exes:
+ continue
+ tested_exes.add(exe)
+ interpreter = PathPythonInfo.from_exe(str(exe), app_data, raise_on_error=False, env=env)
+ if interpreter is not None:
+ yield interpreter, impl_must_match
def get_paths(env):
@@ -125,14 +123,14 @@ def get_paths(env):
except (AttributeError, ValueError):
path = os.defpath
if not path:
- paths = []
- else:
- paths = [p for p in path.split(os.pathsep) if os.path.exists(p)]
- return paths
+ return None
+ for p in map(Path, path.split(os.pathsep)):
+ if p.exists():
+ yield p
class LazyPathDump:
- def __init__(self, pos, path, env):
+ def __init__(self, pos, path, env) -> None:
self.pos = pos
self.path = path
self.env = env
@@ -141,35 +139,35 @@ class LazyPathDump:
content = f"discover PATH[{self.pos}]={self.path}"
if self.env.get("_VIRTUALENV_DEBUG"): # this is the over the board debug
content += " with =>"
- for file_name in os.listdir(self.path):
+ for file_path in self.path.iterdir():
try:
- file_path = os.path.join(self.path, file_name)
- if os.path.isdir(file_path) or not os.access(file_path, os.X_OK):
+ if file_path.is_dir() or not (file_path.stat().st_mode & os.X_OK):
continue
except OSError:
pass
content += " "
- content += file_name
+ content += file_path.name
return content
-def check_path(candidate, path):
- _, ext = os.path.splitext(candidate)
- if sys.platform == "win32" and ext != ".exe":
- candidate = candidate + ".exe"
- if os.path.isfile(candidate):
- return candidate
- candidate = os.path.join(path, candidate)
- if os.path.isfile(candidate):
- return candidate
- return None
-
-
-def possible_specs(spec):
- # 4. then maybe it's something exact on PATH - if it was direct lookup implementation no longer counts
- yield spec.str_spec, False
- # 5. or from the spec we can deduce a name on path that matches
- yield from spec.generate_names()
+def path_exe_finder(spec):
+ """Given a spec, return a function that can be called on a path to find all matching files in it."""
+ pat = spec.generate_re(windows=sys.platform == "win32")
+ direct = spec.str_spec
+ if sys.platform == "win32":
+ direct = f"{direct}.exe"
+
+ def path_exes(path):
+ # 4. then maybe it's something exact on PATH - if it was direct lookup implementation no longer counts
+ yield (path / direct), False
+ # 5. or from the spec we can deduce if a name on path matches
+ for exe in path.iterdir():
+ match = pat.fullmatch(exe.name)
+ if match:
+ # the implementation must match when we find “python[ver]”
+ yield exe.absolute(), match["impl"] == "python"
+
+ return path_exes
class PathPythonInfo(PythonInfo):
diff --git a/src/virtualenv/discovery/py_spec.py b/src/virtualenv/discovery/py_spec.py
index 103c7ae..cbfdfb8 100644
--- a/src/virtualenv/discovery/py_spec.py
+++ b/src/virtualenv/discovery/py_spec.py
@@ -1,10 +1,9 @@
"""A Python specification is an abstract requirement definition of an interpreter"""
+from __future__ import annotations
+
import os
import re
-from collections import OrderedDict
-
-from virtualenv.info import fs_is_case_sensitive
PATTERN = re.compile(r"^(?P<impl>[a-zA-Z]+)?(?P<version>[0-9.]+)?(?:-(?P<arch>32|64))?$")
@@ -12,7 +11,7 @@ PATTERN = re.compile(r"^(?P<impl>[a-zA-Z]+)?(?P<version>[0-9.]+)?(?:-(?P<arch>32
class PythonSpec:
"""Contains specification about a Python Interpreter"""
- def __init__(self, str_spec, implementation, major, minor, micro, architecture, path):
+ def __init__(self, str_spec, implementation, major, minor, micro, architecture, path) -> None: # noqa: PLR0913
self.str_spec = str_spec
self.implementation = implementation
self.major = major
@@ -22,7 +21,7 @@ class PythonSpec:
self.path = path
@classmethod
- def from_string_spec(cls, string_spec):
+ def from_string_spec(cls, string_spec): # noqa: C901, PLR0912
impl, major, minor, micro, arch, path = None, None, None, None, None, None
if os.path.isabs(string_spec):
path = string_spec
@@ -64,27 +63,18 @@ class PythonSpec:
return cls(string_spec, impl, major, minor, micro, arch, path)
- def generate_names(self):
- impls = OrderedDict()
- if self.implementation:
- # first consider implementation as it is
- impls[self.implementation] = False
- if fs_is_case_sensitive():
- # for case sensitive file systems consider lower and upper case versions too
- # trivia: MacBooks and all pre 2018 Windows-es were case insensitive by default
- impls[self.implementation.lower()] = False
- impls[self.implementation.upper()] = False
- impls["python"] = True # finally consider python as alias, implementation must match now
- version = self.major, self.minor, self.micro
- try:
- version = version[: version.index(None)]
- except ValueError:
- pass
- for impl, match in impls.items():
- for at in range(len(version), -1, -1):
- cur_ver = version[0:at]
- spec = f"{impl}{'.'.join(str(i) for i in cur_ver)}"
- yield spec, match
+ def generate_re(self, *, windows):
+ """Generate a regular expression for matching against a filename."""
+ version = r"{}(\.{}(\.{})?)?".format(
+ *(r"\d+" if v is None else v for v in (self.major, self.minor, self.micro))
+ )
+ impl = "python" if self.implementation is None else f"python|{re.escape(self.implementation)}"
+ suffix = r"\.exe" if windows else ""
+ # Try matching `direct` first, so the `direct` group is filled when possible.
+ return re.compile(
+ rf"(?P<impl>{impl})(?P<v>{version}){suffix}$",
+ flags=re.IGNORECASE,
+ )
@property
def is_abs(self):
diff --git a/tests/unit/discovery/test_discovery.py b/tests/unit/discovery/test_discovery.py
index c4a2cc7..51bae24 100644
--- a/tests/unit/discovery/test_discovery.py
+++ b/tests/unit/discovery/test_discovery.py
@@ -14,16 +14,25 @@ from virtualenv.info import fs_supports_symlink
@pytest.mark.skipif(not fs_supports_symlink(), reason="symlink not supported")
@pytest.mark.parametrize("case", ["mixed", "lower", "upper"])
-def test_discovery_via_path(monkeypatch, case, tmp_path, caplog, session_app_data):
+@pytest.mark.parametrize("specificity", ["more", "less"])
+def test_discovery_via_path(monkeypatch, case, specificity, tmp_path, caplog, session_app_data): # noqa: PLR0913
caplog.set_level(logging.DEBUG)
current = PythonInfo.current_system(session_app_data)
- core = f"somethingVeryCryptic{'.'.join(str(i) for i in current.version_info[0:3])}"
name = "somethingVeryCryptic"
if case == "lower":
name = name.lower()
elif case == "upper":
name = name.upper()
- exe_name = f"{name}{current.version_info.major}{'.exe' if sys.platform == 'win32' else ''}"
+ if specificity == "more":
+ # e.g. spec: python3, exe: /bin/python3.12
+ core_ver = current.version_info.major
+ exe_ver = ".".join(str(i) for i in current.version_info[0:2])
+ elif specificity == "less":
+ # e.g. spec: python3.12.1, exe: /bin/python3
+ core_ver = ".".join(str(i) for i in current.version_info[0:3])
+ exe_ver = current.version_info.major
+ core = f"somethingVeryCryptic{core_ver}"
+ exe_name = f"{name}{exe_ver}{'.exe' if sys.platform == 'win32' else ''}"
target = tmp_path / current.install_path("scripts")
target.mkdir(parents=True)
executable = target / exe_name
--
2.45.2
From 4bb73d175614aa6041b1e9e57d7302b9c253b5ef Mon Sep 17 00:00:00 2001
From: Ofek Lev <ofekmeister@gmail.com>
Date: Sat, 27 Apr 2024 14:43:00 -0400
Subject: [PATCH 2/2] Fix PATH-based Python discovery on Windows (#2712)
---
docs/changelog/2712.bugfix.rst | 1 +
src/virtualenv/discovery/builtin.py | 44 ++++++++++++++++++++------
src/virtualenv/discovery/py_spec.py | 10 +++++-
src/virtualenv/info.py | 5 +++
tests/unit/discovery/test_discovery.py | 8 +++--
5 files changed, 55 insertions(+), 13 deletions(-)
create mode 100644 docs/changelog/2712.bugfix.rst
diff --git a/docs/changelog/2712.bugfix.rst b/docs/changelog/2712.bugfix.rst
new file mode 100644
index 0000000..fee5436
--- /dev/null
+++ b/docs/changelog/2712.bugfix.rst
@@ -0,0 +1 @@
+fix PATH-based Python discovery on Windows - by :user:`ofek`.
diff --git a/src/virtualenv/discovery/builtin.py b/src/virtualenv/discovery/builtin.py
index f79b5a5..456c68b 100644
--- a/src/virtualenv/discovery/builtin.py
+++ b/src/virtualenv/discovery/builtin.py
@@ -3,7 +3,7 @@ import os
import sys
from pathlib import Path
-from virtualenv.info import IS_WIN
+from virtualenv.info import IS_WIN, fs_path_id
from .discover import Discover
from .py_info import PythonInfo
@@ -68,9 +68,10 @@ def get_interpreter(key, try_first_with, app_data=None, env=None):
proposed_paths.add(key)
-def propose_interpreters(spec, try_first_with, app_data=None, env=None): # noqa: C901, PLR0912
+def propose_interpreters(spec, try_first_with, app_data=None, env=None): # noqa: C901, PLR0912, PLR0915
# 0. try with first
env = os.environ if env is None else env
+ tested_exes: set[str] = set()
for py_exe in try_first_with:
path = os.path.abspath(py_exe)
try:
@@ -78,7 +79,12 @@ def propose_interpreters(spec, try_first_with, app_data=None, env=None): # noqa
except OSError:
pass
else:
- yield PythonInfo.from_exe(os.path.abspath(path), app_data, env=env), True
+ exe_raw = os.path.abspath(path)
+ exe_id = fs_path_id(exe_raw)
+ if exe_id in tested_exes:
+ continue
+ tested_exes.add(exe_id)
+ yield PythonInfo.from_exe(exe_raw, app_data, env=env), True
# 1. if it's a path and exists
if spec.path is not None:
@@ -88,29 +94,44 @@ def propose_interpreters(spec, try_first_with, app_data=None, env=None): # noqa
if spec.is_abs:
raise
else:
- yield PythonInfo.from_exe(os.path.abspath(spec.path), app_data, env=env), True
+ exe_raw = os.path.abspath(spec.path)
+ exe_id = fs_path_id(exe_raw)
+ if exe_id not in tested_exes:
+ tested_exes.add(exe_id)
+ yield PythonInfo.from_exe(exe_raw, app_data, env=env), True
if spec.is_abs:
return
else:
# 2. otherwise try with the current
- yield PythonInfo.current_system(app_data), True
+ current_python = PythonInfo.current_system(app_data)
+ exe_raw = str(current_python.executable)
+ exe_id = fs_path_id(exe_raw)
+ if exe_id not in tested_exes:
+ tested_exes.add(exe_id)
+ yield current_python, True
# 3. otherwise fallback to platform default logic
if IS_WIN:
from .windows import propose_interpreters
for interpreter in propose_interpreters(spec, app_data, env):
+ exe_raw = str(interpreter.executable)
+ exe_id = fs_path_id(exe_raw)
+ if exe_id in tested_exes:
+ continue
+ tested_exes.add(exe_id)
yield interpreter, True
# finally just find on path, the path order matters (as the candidates are less easy to control by end user)
- tested_exes = set()
find_candidates = path_exe_finder(spec)
for pos, path in enumerate(get_paths(env)):
logging.debug(LazyPathDump(pos, path, env))
for exe, impl_must_match in find_candidates(path):
- if exe in tested_exes:
+ exe_raw = str(exe)
+ exe_id = fs_path_id(exe_raw)
+ if exe_id in tested_exes:
continue
- tested_exes.add(exe)
- interpreter = PathPythonInfo.from_exe(str(exe), app_data, raise_on_error=False, env=env)
+ tested_exes.add(exe_id)
+ interpreter = PathPythonInfo.from_exe(exe_raw, app_data, raise_on_error=False, env=env)
if interpreter is not None:
yield interpreter, impl_must_match
@@ -159,7 +180,10 @@ def path_exe_finder(spec):
def path_exes(path):
# 4. then maybe it's something exact on PATH - if it was direct lookup implementation no longer counts
- yield (path / direct), False
+ direct_path = path / direct
+ if direct_path.exists():
+ yield direct_path, False
+
# 5. or from the spec we can deduce if a name on path matches
for exe in path.iterdir():
match = pat.fullmatch(exe.name)
diff --git a/src/virtualenv/discovery/py_spec.py b/src/virtualenv/discovery/py_spec.py
index cbfdfb8..04b63b8 100644
--- a/src/virtualenv/discovery/py_spec.py
+++ b/src/virtualenv/discovery/py_spec.py
@@ -70,9 +70,17 @@ class PythonSpec:
)
impl = "python" if self.implementation is None else f"python|{re.escape(self.implementation)}"
suffix = r"\.exe" if windows else ""
+ version_conditional = (
+ "?"
+ # Windows Python executables are almost always unversioned
+ if windows
+ # Spec is an empty string
+ or self.major is None
+ else ""
+ )
# Try matching `direct` first, so the `direct` group is filled when possible.
return re.compile(
- rf"(?P<impl>{impl})(?P<v>{version}){suffix}$",
+ rf"(?P<impl>{impl})(?P<v>{version}){version_conditional}{suffix}$",
flags=re.IGNORECASE,
)
diff --git a/src/virtualenv/info.py b/src/virtualenv/info.py
index a4fc4bf..dd96f10 100644
--- a/src/virtualenv/info.py
+++ b/src/virtualenv/info.py
@@ -47,11 +47,16 @@ def fs_supports_symlink():
return _CAN_SYMLINK
+def fs_path_id(path: str) -> str:
+ return path.casefold() if fs_is_case_sensitive() else path
+
+
__all__ = (
"IS_PYPY",
"IS_CPYTHON",
"IS_WIN",
"fs_is_case_sensitive",
+ "fs_path_id",
"fs_supports_symlink",
"ROOT",
"IS_ZIPAPP",
diff --git a/tests/unit/discovery/test_discovery.py b/tests/unit/discovery/test_discovery.py
index 51bae24..6c64b40 100644
--- a/tests/unit/discovery/test_discovery.py
+++ b/tests/unit/discovery/test_discovery.py
@@ -14,7 +14,7 @@ from virtualenv.info import fs_supports_symlink
@pytest.mark.skipif(not fs_supports_symlink(), reason="symlink not supported")
@pytest.mark.parametrize("case", ["mixed", "lower", "upper"])
-@pytest.mark.parametrize("specificity", ["more", "less"])
+@pytest.mark.parametrize("specificity", ["more", "less", "none"])
def test_discovery_via_path(monkeypatch, case, specificity, tmp_path, caplog, session_app_data): # noqa: PLR0913
caplog.set_level(logging.DEBUG)
current = PythonInfo.current_system(session_app_data)
@@ -31,7 +31,11 @@ def test_discovery_via_path(monkeypatch, case, specificity, tmp_path, caplog, se
# e.g. spec: python3.12.1, exe: /bin/python3
core_ver = ".".join(str(i) for i in current.version_info[0:3])
exe_ver = current.version_info.major
- core = f"somethingVeryCryptic{core_ver}"
+ elif specificity == "none":
+ # e.g. spec: python3.12.1, exe: /bin/python
+ core_ver = ".".join(str(i) for i in current.version_info[0:3])
+ exe_ver = ""
+ core = "" if specificity == "none" else f"{name}{core_ver}"
exe_name = f"{name}{exe_ver}{'.exe' if sys.platform == 'win32' else ''}"
target = tmp_path / current.install_path("scripts")
target.mkdir(parents=True)
--
2.45.2

@ -0,0 +1,60 @@
From 11c30f6c69c4516b406c1c62f472d37898c58b93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
Date: Mon, 4 Dec 2023 14:26:52 +0100
Subject: [PATCH] Run CI tests on Python 3.13, fix tests (#2673)
---
docs/changelog/2673.feature.rst | 1 +
tests/unit/create/test_creator.py | 6 +++++-
tests/unit/create/via_global_ref/builtin/testing/path.py | 6 +++++-
3 files changed, 11 insertions(+), 2 deletions(-)
create mode 100644 docs/changelog/2673.feature.rst
diff --git a/docs/changelog/2673.feature.rst b/docs/changelog/2673.feature.rst
new file mode 100644
index 0000000..0adf4a0
--- /dev/null
+++ b/docs/changelog/2673.feature.rst
@@ -0,0 +1 @@
+The tests now pass on the CI with Python 3.13.0a2 - by :user:`hroncok`.
diff --git a/tests/unit/create/test_creator.py b/tests/unit/create/test_creator.py
index 8b9d688..fc21ad8 100644
--- a/tests/unit/create/test_creator.py
+++ b/tests/unit/create/test_creator.py
@@ -231,7 +231,11 @@ def test_create_no_seed(python, creator, isolated, system, coverage_env, special
assert os.path.exists(make_file)
git_ignore = (dest / ".gitignore").read_text(encoding="utf-8")
- assert git_ignore.splitlines() == ["# created by virtualenv automatically", "*"]
+ if creator_key == "venv" and sys.version_info >= (3, 13):
+ comment = "# Created by venv; see https://docs.python.org/3/library/venv.html"
+ else:
+ comment = "# created by virtualenv automatically"
+ assert git_ignore.splitlines() == [comment, "*"]
def test_create_vcs_ignore_exists(tmp_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..d833de6 100644
--- a/tests/unit/create/via_global_ref/builtin/testing/path.py
+++ b/tests/unit/create/via_global_ref/builtin/testing/path.py
@@ -44,11 +44,15 @@ class PathMockABC(FakeDataABC, Path):
"""Mocks the behavior of `Path`"""
_flavour = getattr(Path(), "_flavour", None)
-
if hasattr(_flavour, "altsep"):
# Allows to pass some tests for Windows via PosixPath.
_flavour.altsep = _flavour.altsep or "\\"
+ # Python 3.13 renamed _flavour to parser
+ parser = getattr(Path(), "parser", None)
+ if hasattr(parser, "altsep"):
+ parser.altsep = parser.altsep or "\\"
+
def exists(self):
return self.is_file() or self.is_dir()
--
2.43.0

@ -0,0 +1,180 @@
%bcond bootstrap 0
%bcond tests %{without bootstrap}
Name: python-virtualenv
Version: 20.21.1
Release: %autorelease
Summary: Tool to create isolated Python environments
License: MIT
URL: http://pypi.python.org/pypi/virtualenv
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
# Fix compatibility with Python 3.13
# https://github.com/pypa/virtualenv/pull/2673
# https://github.com/pypa/virtualenv/pull/2702
Patch5: py3.13.patch
# Allow builtin interpreter discovery to find specific Python versions given a
# general spec
# https://github.com/pypa/virtualenv/pull/2709
# which contains regressions (mostly but perhaps not exclusively on Windows)
# that are fixed by:
# Fix PATH-based Python discovery on Windows
# https://github.com/pypa/virtualenv/pull/2712
#
# Backported to 20.21.1.
Patch6: prs-2709-and-2712.patch
# Quote template strings in activation scripts
# to prevent possible command injection.
# https://github.com/pypa/virtualenv/issues/2768
# Backported from 20.26.6
Patch7: prevent_command_injection.patch
BuildArch: noarch
BuildRequires: python3-devel
%if %{with tests}
BuildRequires: fish
BuildRequires: tcsh
BuildRequires: gcc
BuildRequires: python3-flaky
BuildRequires: python3-packaging
BuildRequires: python3-pytest
BuildRequires: python3-pytest-mock
BuildRequires: python3-pytest-randomly
BuildRequires: python3-pytest-timeout
%endif
# RPM installed wheels
BuildRequires: %{python_wheel_pkg_prefix}-pip-wheel
BuildRequires: %{python_wheel_pkg_prefix}-setuptools-wheel
BuildRequires: %{python_wheel_pkg_prefix}-wheel-wheel
%description
virtualenv is a tool to create isolated Python environments. virtualenv
is a successor to workingenv, and an extension of virtual-python. It is
written by Ian Bicking, and sponsored by the Open Planning Project. It is
licensed under an MIT-style permissive license.
%package -n python3-virtualenv
Summary: Tool to create isolated Python environments
# This virtualenv requires the "venv" install scheme on Pythons
# where we patch "posix_prefix".
# Explicitly conflict with Pythons where we don't have it yet.
Conflicts: python3.11 < 3.11.0~a2
%if 0%{?fedora} >= 36
Conflicts: python3.10 < 3.10.0-3
%endif
Obsoletes: python3-virtualenv-python26 < 16.6
%{?python_provide:%python_provide python3-virtualenv}
# Provide "virtualenv" for convenience
Provides: virtualenv = %{version}-%{release}
# RPM installed wheels
Requires: %{python_wheel_pkg_prefix}-pip-wheel
Requires: %{python_wheel_pkg_prefix}-setuptools-wheel
Requires: %{python_wheel_pkg_prefix}-wheel-wheel
# For Python 3.11
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
written by Ian Bicking, and sponsored by the Open Planning Project. It is
licensed under an MIT-style permissive license
%prep
%autosetup -p1 -n virtualenv-%{version}
# Remove the wheels provided by RPM packages
rm src/virtualenv/seed/wheels/embed/pip-*
rm src/virtualenv/seed/wheels/embed/setuptools-*
rm src/virtualenv/seed/wheels/embed/wheel-*
test ! -f src/virtualenv/seed/embed/wheels/*.whl
# Relax the upper bounds of some dependencies to their known available versions in EL 9
# We run tests and CI to verify nothing broke
sed -i -e 's/distlib<1,>=0.3.6/distlib<1,>=0.3.2/' \
-e 's/filelock<4,>=3.4.1/filelock<4,>=3.3.1/' \
-e 's/platformdirs<4,>=2.4/platformdirs<4,>=2.3/' \
pyproject.toml
%generate_buildrequires
%pyproject_buildrequires
%build
%pyproject_wheel
%install
%pyproject_install
%pyproject_save_files virtualenv
# EPEL 9: old version of setuptools_scm produces files incompatible with
# assumptions in virtualenv code, we append the expected attributes:
echo '__version__, __version_tuple__ = version, version_tuple' >> %{buildroot}%{python3_sitelib}/virtualenv/version.py
%if %{with tests}
%check
# 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
# - test_base_bootstrap_via_pip_invoke
# - test_acquire.py (whole file)
# - test_bundle.py (whole file)
# Uses disabled functionalities around automatic updates:
# - test_periodic_update.py (whole file)
# Requires Python 2:
# - test_py_pyc_missing
PIP_CERT=/etc/pki/tls/certs/ca-bundle.crt \
%pytest -vv -k "not test_bundle and \
not test_acquire and \
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"
%endif
%files -n python3-virtualenv -f %{pyproject_files}
%doc README.md
%{_bindir}/virtualenv
%changelog
%autochangelog

@ -0,0 +1,172 @@
From 3f22c840a0b26dae8dd09985501eaa33846f063c Mon Sep 17 00:00:00 2001
From: Lumir Balhar <lbalhar@redhat.com>
Date: Thu, 27 Oct 2022 11:50:54 +0200
Subject: [PATCH] RPM wheels
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Miro Hrončok <miro@hroncok.cz>
---
src/virtualenv/run/__init__.py | 5 ++--
src/virtualenv/seed/embed/base_embed.py | 16 ++++++++++-
src/virtualenv/seed/embed/pip_invoke.py | 1 +
.../seed/embed/via_app_data/via_app_data.py | 1 +
src/virtualenv/seed/wheels/acquire.py | 3 ++-
src/virtualenv/seed/wheels/embed/__init__.py | 3 +++
src/virtualenv/util/path/_system_wheels.py | 27 +++++++++++++++++++
7 files changed, 52 insertions(+), 4 deletions(-)
create mode 100644 src/virtualenv/util/path/_system_wheels.py
diff --git a/src/virtualenv/run/__init__.py b/src/virtualenv/run/__init__.py
index 6d22b71..19d1791 100644
--- a/src/virtualenv/run/__init__.py
+++ b/src/virtualenv/run/__init__.py
@@ -87,8 +87,9 @@ def build_parser_only(args=None):
def handle_extra_commands(options):
if options.upgrade_embed_wheels:
- result = manual_upgrade(options.app_data, options.env)
- raise SystemExit(result)
+ # result = manual_upgrade(options.app_data, options.env)
+ logging.warning("virtualenv installed from the RPM package uses wheels from RPM packages as well. Updating them via virtualenv is not possible. The RPM packaged wheels are updated together with other RPM packages of the system.")
+ raise SystemExit(1)
def load_app_data(args, parser, options):
diff --git a/src/virtualenv/seed/embed/base_embed.py b/src/virtualenv/seed/embed/base_embed.py
index f29110b..07649c2 100644
--- a/src/virtualenv/seed/embed/base_embed.py
+++ b/src/virtualenv/seed/embed/base_embed.py
@@ -3,8 +3,9 @@ from pathlib import Path
from ..seeder import Seeder
from ..wheels import Version
+from virtualenv.util.path._system_wheels import get_system_wheels_paths
-PERIODIC_UPDATE_ON_BY_DEFAULT = True
+PERIODIC_UPDATE_ON_BY_DEFAULT = False
class BaseEmbed(Seeder, metaclass=ABCMeta):
@@ -27,6 +28,15 @@ class BaseEmbed(Seeder, metaclass=ABCMeta):
if not self.distribution_to_versions():
self.enabled = False
+ if "embed" in (self.pip_version, self.setuptools_version, self.wheel_version):
+ raise RuntimeError(
+ "Embedded wheels are not available if virtualenv "
+ "is installed from the RPM package.\nEither install "
+ "virtualenv from PyPI (via pip) or use 'bundle' "
+ "version which uses the system-wide pip, setuptools "
+ "and wheel wheels provided also by RPM packages."
+ )
+
@classmethod
def distributions(cls):
return {
@@ -105,6 +115,10 @@ class BaseEmbed(Seeder, metaclass=ABCMeta):
result += f" {distribution}{ver},"
return result[:-1] + ")"
+ def insert_system_wheels_paths(self, creator):
+ system_wheels_paths = get_system_wheels_paths(creator.interpreter)
+ self.extra_search_dir = list(system_wheels_paths) + self.extra_search_dir
+
__all__ = [
"BaseEmbed",
diff --git a/src/virtualenv/seed/embed/pip_invoke.py b/src/virtualenv/seed/embed/pip_invoke.py
index 2ca9438..339295f 100644
--- a/src/virtualenv/seed/embed/pip_invoke.py
+++ b/src/virtualenv/seed/embed/pip_invoke.py
@@ -15,6 +15,7 @@ class PipInvoke(BaseEmbed):
def run(self, creator):
if not self.enabled:
return
+ self.insert_system_wheels_paths(creator)
for_py_version = creator.interpreter.version_release_str
with self.get_pip_install_cmd(creator.exe, for_py_version) as cmd:
env = pip_wheel_env_run(self.extra_search_dir, self.app_data, self.env)
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..d7a0f5a 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
@@ -37,6 +37,7 @@ class FromAppData(BaseEmbed):
def run(self, creator):
if not self.enabled:
return
+ self.insert_system_wheels_paths(creator)
with self._get_seed_wheels(creator) as name_to_whl:
pip_version = name_to_whl["pip"].version_tuple if "pip" in name_to_whl else None
installer_class = self.installer_class(pip_version)
diff --git a/src/virtualenv/seed/wheels/acquire.py b/src/virtualenv/seed/wheels/acquire.py
index 21fde34..4370b0d 100644
--- a/src/virtualenv/seed/wheels/acquire.py
+++ b/src/virtualenv/seed/wheels/acquire.py
@@ -24,11 +24,12 @@ def get_wheel(distribution, version, for_py_version, search_dirs, download, app_
if download and wheel is None and version != Version.embed:
# 2. download from the internet
+ from virtualenv.util.path._system_wheels import get_system_wheels_paths
wheel = download_wheel(
distribution=distribution,
version_spec=Version.as_version_spec(version),
for_py_version=for_py_version,
- search_dirs=search_dirs,
+ search_dirs=get_system_wheels_paths(sys),
app_data=app_data,
to_folder=app_data.house,
env=env,
diff --git a/src/virtualenv/seed/wheels/embed/__init__.py b/src/virtualenv/seed/wheels/embed/__init__.py
index 782051a..71ec712 100644
--- a/src/virtualenv/seed/wheels/embed/__init__.py
+++ b/src/virtualenv/seed/wheels/embed/__init__.py
@@ -52,8 +52,11 @@ BUNDLE_SUPPORT = {
}
MAX = "3.12"
+# Redefined here because bundled wheels are removed in RPM build
+BUNDLE_SUPPORT = None
def get_embed_wheel(distribution, for_py_version):
+ return None # BUNDLE_SUPPORT == None anyway
path = BUNDLE_FOLDER / (BUNDLE_SUPPORT.get(for_py_version, {}) or BUNDLE_SUPPORT[MAX]).get(distribution)
return Wheel.from_path(path)
diff --git a/src/virtualenv/util/path/_system_wheels.py b/src/virtualenv/util/path/_system_wheels.py
new file mode 100644
index 0000000..fc7e942
--- /dev/null
+++ b/src/virtualenv/util/path/_system_wheels.py
@@ -0,0 +1,27 @@
+from pathlib import Path
+from subprocess import check_output, CalledProcessError
+
+
+def get_system_wheels_paths(interpreter):
+ # ensurepip wheels
+ # We need subprocess here to check ensurepip with the Python we are creating
+ # a new virtual environment for
+ executable = interpreter.executable
+ try:
+ ensurepip_path = check_output((executable, "-u", "-c", 'import ensurepip; print(ensurepip.__path__[0])'), universal_newlines=True)
+ ensurepip_path = Path(ensurepip_path.strip()) / "_bundled"
+ except CalledProcessError:
+ pass
+ else:
+ if ensurepip_path.is_dir():
+ yield ensurepip_path
+
+ # Standard wheels path
+ # The EL 9 main Python has just 3 (this is the %{python3_pkgversion} RPM macro)
+ if interpreter.version_info[:2] == (3, 9):
+ python3_pkgversion = "3"
+ else:
+ python3_pkgversion = "{0.major}.{0.minor}".format(interpreter.version_info)
+ wheels_dir = Path(f"/usr/share/python{python3_pkgversion}-wheels")
+ if wheels_dir.exists():
+ yield wheels_dir
--
2.47.0

@ -0,0 +1 @@
SHA512 (virtualenv-20.21.1.tar.gz) = 5f5c4ce677feffe24b87ff4b3837e6f7cda6a5017eea122ac089be2066b74c7eaf452dbc0ab7723e9433716aeca13e6f85ea23d02fb28a83d76df5db0068572a

@ -0,0 +1,57 @@
---
- hosts: localhost
roles:
- role: standard-test-basic
tags:
- classic
repositories:
- repo: "https://src.fedoraproject.org/tests/python.git"
dest: "python"
tests:
- smoke39:
dir: python/smoke
run: VERSION=3.9 METHOD=virtualenv ./venv.sh
- smoke39nd:
dir: python/smoke
run: VERSION=3.9 METHOD=virtualenv-no-download ./venv.sh
- smoke39d:
dir: python/smoke
run: VERSION=3.9 METHOD=virtualenv-download TOX=false ./venv.sh
- smoke39sp:
dir: python/smoke
run: VERSION=3.9 METHOD=virtualenv-seeder-pip ./venv.sh
- smoke311:
dir: python/smoke
run: VERSION=3.11 METHOD=virtualenv ./venv.sh
- smoke311d:
dir: python/smoke
run: VERSION=3.11 METHOD=virtualenv-download TOX=false ./venv.sh
- smoke311nd:
dir: python/smoke
run: VERSION=3.11 METHOD=virtualenv-no-download ./venv.sh
- smoke311sp:
dir: python/smoke
run: VERSION=3.11 METHOD=virtualenv-seeder-pip ./venv.sh
- smoke312:
dir: python/smoke
run: VERSION=3.12 METHOD=virtualenv ./venv.sh
- smoke312d:
dir: python/smoke
run: VERSION=3.12 METHOD=virtualenv-download TOX=false ./venv.sh
- smoke312nd:
dir: python/smoke
run: VERSION=3.12 METHOD=virtualenv-no-download ./venv.sh
- smoke312sp:
dir: python/smoke
run: VERSION=3.12 METHOD=virtualenv-seeder-pip ./venv.sh
- rpm_qa:
run: rpm -qa | sort
required_packages:
- gcc
- virtualenv
- python3.9
- python3.11-devel
- python3.12-devel
- python3-devel
- python3-tox
- rpm
Loading…
Cancel
Save