diff --git a/news/6164.bugfix.rst b/news/6164.bugfix.rst new file mode 100644 index 0000000000..348a090179 --- /dev/null +++ b/news/6164.bugfix.rst @@ -0,0 +1 @@ +Pipenv only supports absolute python version. If the user specifies a Python version with inequality signs like >=3.12, <3.12 in the [requires] field, the code has been modified to explicitly express in an error log that absolute versioning must be used. diff --git a/pipenv/utils/virtualenv.py b/pipenv/utils/virtualenv.py index 6192b20833..be0ff0e368 100644 --- a/pipenv/utils/virtualenv.py +++ b/pipenv/utils/virtualenv.py @@ -1,5 +1,6 @@ import contextlib import os +import re import shutil import sys from pathlib import Path @@ -237,6 +238,15 @@ def abort(msg=""): # Find out which python is desired. if not python: python = project.required_python_version + if python: + range_pattern = r"^[<>]=?|!=" + if re.search(range_pattern, python): + err.print( + f"[bold red]Error[/bold red]: Python version range specifier '[cyan]{python}[/cyan]' is not supported. " + "[yellow]Please use an absolute version number or specify the path to the Python executable on Pipfile.[/yellow]" + ) + sys.exit(1) + if not python: python = project.s.PIPENV_DEFAULT_PYTHON_VERSION path_to_python = find_a_system_python(python) diff --git a/tests/integration/test_install_basic.py b/tests/integration/test_install_basic.py index cfe231f0eb..7be3571818 100644 --- a/tests/integration/test_install_basic.py +++ b/tests/integration/test_install_basic.py @@ -403,6 +403,64 @@ def test_create_pipfile_requires_python_full_version(pipenv_instance_private_pyp 'python_version': python_version } +@pytest.mark.basic +@pytest.mark.install +@pytest.mark.virtualenv +def test_install_with_pipfile_including_exact_python_version(pipenv_instance_pypi): + valid_version = f"{sys.version_info.major}.{sys.version_info.minor}" + + with pipenv_instance_pypi() as p: + with open(p.pipfile_path, 'w') as f: + f.write(f""" +[[source]] +url = "https://test.pypi.org/simple" +verify_ssl = true +name = "testpypi" + +[packages] +pytz = "*" + +[requires] +python_version = "{valid_version}" +""") + + c = p.pipenv("install") + assert c.returncode == 0 + assert os.path.isfile(p.pipfile_path) + assert p.pipfile["requires"]["python_version"] == valid_version + + c = p.pipenv("--rm") + assert c.returncode == 0 + +@pytest.mark.basic +@pytest.mark.install +@pytest.mark.virtualenv +def test_install_with_pipfile_including_invalid_python_version(pipenv_instance_pypi): + invalid_versions = [ + f">={sys.version_info.major}.{sys.version_info.minor}", + f"<={sys.version_info.major}.{sys.version_info.minor}", + f">{sys.version_info.major}.{sys.version_info.minor}", + f"<{sys.version_info.major}.{sys.version_info.minor}", + ] + + with pipenv_instance_pypi() as p: + for version in invalid_versions: + with open(p.pipfile_path, 'w') as f: + f.write(f""" +[[source]] +url = "https://test.pypi.org/simple" +verify_ssl = true +name = "testpypi" + +[packages] +pytz = "*" + +[requires] +python_version = "{version}" +""") + c = p.pipenv("install") + assert c.returncode != 0 + @pytest.mark.basic @pytest.mark.install