Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python.path with "Python 3.11.0+" is considered to be invalid / gets replaced #1510

Closed
Tracked by #100
blueyed opened this issue Nov 10, 2022 · 11 comments
Closed
Tracked by #100
Labels
🐛 bug Something isn't working

Comments

@blueyed
Copy link
Contributor

blueyed commented Nov 10, 2022

Given .pdm.toml:

[python]
path = "/project/.venv/bin/python"

and Python compiled from the 3.11 branch (not on a release tag), where python --version gives "Python 3.11.0+" pdm will replace the config with python.path = "/usr/bin/python3.10".

This is likely due to frostming/findpython#12, but on top it seems like a configured path should be not changed automatically, and also be used without (too strict) checks.

@blueyed blueyed added the 🐛 bug Something isn't working label Nov 10, 2022
@frostming
Copy link
Collaborator

I made a simple fix to address the upstream problem. It will be released soon.

@frostming
Copy link
Collaborator

@ssbarnea
Copy link

I think that I found another place where it chokes on the same problem:

pdm.termui: Running PEP 517 backend to get metadata for <Link file:///Users/ssbarnea/c/os/mkdocs/mkdocstrings (from None)>
pdm.termui: Preparing isolated env for PEP 517 build...
pdm.termui: ======== Start resolving requirements ========
pdm.termui:   pdm-pep517
pdm.termui:   python>=3.11,<3.11.1
pdm.termui:   Adding requirement pdm-pep517
unearth.collector: Collecting links from https://pypi.org/simple/pdm-pep517/
unearth.auth: Found index url https://pypi.org/simple
unearth.collector: Fetching HTML page https://pypi.org/simple/pdm-pep517/ (from cache)
unearth.evaluator: Skipping link <Link https://files.pythonhosted.org/packages/9a/56/6a14fd290afe5d29b51249a98871b7f130da770249c2cb6024ffe7bd2c0c/pdm-pep517-0.3.0.tar.gz (from https://pypi.org/simple/pdm-pep517/)>: Yanked due to True
unearth.evaluator: Skipping link <Link https://files.pythonhosted.org/packages/cd/9c/f1fc7b224a2a6937972b7639ddeacd596f053a2765d97f949a733e75e73d/pdm_pep517-0.3.0-py3-none-any.whl (from https://pypi.org/simple/pdm-pep517/)>: Yanked due to True
unearth.evaluator: Skipping link <Link https://files.pythonhosted.org/packages/37/d6/95aa238e39e92d51c2d94e902149109a1e0f05f9d4c2cdb5d31c3ddf2ad6/pdm-pep517-0.5.0.tar.gz (from https://pypi.org/simple/pdm-pep517/)>: Yanked due to True
unearth.evaluator: Skipping link <Link https://files.pythonhosted.org/packages/05/f6/9aa5d8a094af9173da4738610b3ecda29ef286bcc4ca8da5c6106494fae0/pdm_pep517-0.5.0-py3-none-any.whl (from https://pypi.org/simple/pdm-pep517/)>: Yanked due to True
unearth.evaluator: Skipping link <Link https://files.pythonhosted.org/packages/02/7e/9d029024a12a8fb7b373e3eff8c1a519f1945d9ee6a677225e9cff7bbbd6/pdm-pep517-0.9.0.tar.gz (from https://pypi.org/simple/pdm-pep517/)>: Yanked due to A typo in the message
unearth.evaluator: Skipping link <Link https://files.pythonhosted.org/packages/e6/c8/0a2dc4476133683a85e68ce6fc88c0bd6662178572cced5014bc106d39db/pdm_pep517-0.9.0-py3-none-any.whl (from https://pypi.org/simple/pdm-pep517/)>: Yanked due to A typo in the message
unearth.evaluator: Skipping link <Link https://files.pythonhosted.org/packages/7a/63/e9591545817c6834b8670c41b1af16a21af324ab34511102db3ec18c6f2c/pdm-pep517-0.9.1.tar.gz (from https://pypi.org/simple/pdm-pep517/)>: Yanked due to True
unearth.evaluator: Skipping link <Link https://files.pythonhosted.org/packages/8c/06/3f0129c4ee6636e6fefc003b457647004248c1489fde461bb348e3cbe785/pdm_pep517-0.9.1-py3-none-any.whl (from https://pypi.org/simple/pdm-pep517/)>: Yanked due to True
pdm.termui: 	Found matching candidates:
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  <Candidate [email protected] from https://pypi.org/simple/pdm-pep517/>
pdm.termui: 	  ... [104 more candidate(s)]
pdm.termui:   Adding requirement python>=3.11,<3.11.1
pdm.termui: ======== Starting round 0 ========
pdm.termui: Pinning: python None
pdm.termui: ======== Ending round 0 ========
pdm.termui: ======== Starting round 1 ========
pdm.termui: Pinning: pdm-pep517 1.0.6
pdm.termui: ======== Ending round 1 ========
pdm.termui: ======== Starting round 2 ========
pdm.termui: ======== Resolution Result ========
pdm.termui: Stable pins:
pdm.termui:       python None
pdm.termui:   pdm-pep517 1.0.6
pdm.termui: Installing pdm-pep517 1.0.6
unearth.preparer: Using cached <Link https://files.pythonhosted.org/packages/71/2a/ae8abd6ebb76edd97e0c3dc3e61913df438c17e44a9aed05fd2073bb800f/pdm_pep517-1.0.6-py3-none-any.whl (from https://pypi.org/simple/pdm-pep517/)>
pdm.termui: Error occurs
Traceback (most recent call last):
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/pdm/termui.py", line 240, in logging
    yield logger
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/pdm/cli/actions.py", line 141, in resolve_candidates_from_lockfile
    mapping, *_ = resolve(
                  ^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/pdm/resolver/core.py", line 35, in resolve
    result = resolver.resolve(requirements, max_rounds)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/resolvelib/resolvers.py", line 521, in resolve
    state = resolution.resolve(requirements, max_rounds=max_rounds)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/resolvelib/resolvers.py", line 402, in resolve
    failure_causes = self._attempt_to_pin_criterion(name)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/resolvelib/resolvers.py", line 238, in _attempt_to_pin_criterion
    criteria = self._get_updated_criteria(candidate)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/resolvelib/resolvers.py", line 228, in _get_updated_criteria
    for requirement in self._p.get_dependencies(candidate=candidate):
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/pdm/resolver/providers.py", line 189, in get_dependencies
    deps, requires_python, _ = self.repository.get_dependencies(candidate)
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/pdm/models/repositories.py", line 107, in get_dependencies
    reqs = [
           ^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/pdm/models/repositories.py", line 108, in <listcomp>
    req for req in reqs if not req.marker or req.marker.evaluate(pep508_env)
                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/packaging/markers.py", line 245, in evaluate
    return _evaluate_markers(self._markers, current_environment)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/packaging/markers.py", line 151, in _evaluate_markers
    groups[-1].append(_eval_op(lhs_value, op, rhs_value))
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/packaging/markers.py", line 109, in _eval_op
    return spec.contains(lhs, prereleases=True)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/packaging/specifiers.py", line 565, in contains
    normalized_item = _coerce_version(item)
                      ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/packaging/specifiers.py", line 36, in _coerce_version
    version = Version(version)
              ^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/os/mkdocs/mkdocstrings/venv/lib/python3.11/site-packages/packaging/version.py", line 197, in __init__
    raise InvalidVersion(f"Invalid version: '{version}'")
packaging.version.InvalidVersion: Invalid version: '3.11.0+'

@frostming
Copy link
Collaborator

frostming commented Jan 19, 2023

@ssbarnea please provide more information to help address the cause

  1. Paste the output of pdm info --env
  2. Run the script, with 3.11 dev python:
import packaging
from packaging.markers import default_environment, Marker
print(packaging.__version__)  # make sure it is >=22
print(default_environment())
print(Marker('python_full_version >= "3.8.0"').evaluate())

@ssbarnea
Copy link

Here are the below results, which were like I was expecting. The root cause of the bug was to assume that python_full_version is a valid semantic version. There are multiple cases, not only pyenv dev builds where python full version returned string does not return a valid Version, and that's is ok, it was never meant to do so.

Instead the python_version and the python version tuple are supposed to be "machine-friendly".

I mention this because I personally faced a similar bug in another project, which I was able to address by avoiding the use of the free-form string. In other cases where I had to deal with Version parsing, I catched the exception and provide a fallback mechanism.

Please note that I would usually just raise a PR for simple bugs like this but I do not yet know the pdm project good-enough to know how to address this correctly, especially as there might be multiple places that might need similar patching for version identification.

$ pdm info --env
{
  "implementation_name": "cpython",
  "implementation_version": "3.11.0",
  "os_name": "posix",
  "platform_machine": "arm64",
  "platform_release": "22.2.0",
  "platform_system": "Darwin",
  "platform_version": "Darwin Kernel Version 22.2.0: Fri Nov 11 02:03:51 PST 2022; root:xnu-8792.61.2~4/RELEASE_ARM64_T6000",
  "python_full_version": "3.11.0+",
  "platform_python_implementation": "CPython",
  "python_version": "3.11",
  "sys_platform": "darwin"
}
ssbarnea@m1: ~/c/os/mkdocs/mkdocstrings v chore/grammar
$ python
Python 3.11.0+ (heads/3.11:4cd5ea62ac, Oct 25 2022, 18:19:49) [Clang 14.0.0 (clang-1400.0.29.102)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import packaging
>>> from packaging.markers import default_environment, Marker
>>> print(packaging.__version__)  # make sure it is >=22
23.0
>>> print(default_environment())
{'implementation_name': 'cpython', 'implementation_version': '3.11.0', 'os_name': 'posix', 'platform_machine': 'arm64', 'platform_release': '22.2.0', 'platform_system': 'Darwin', 'platform_version': 'Darwin Kernel Version 22.2.0: Fri Nov 11 02:03:51 PST 2022; root:xnu-8792.61.2~4/RELEASE_ARM64_T6000', 'python_full_version': '3.11.0+', 'platform_python_implementation': 'CPython', 'python_version': '3.11', 'sys_platform': 'darwin'}
>>> print(Marker('python_full_version >= "3.8.0"').evaluate())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/ssbarnea/c/a/r1/v/lib/python3.11/site-packages/packaging/markers.py", line 245, in evaluate
    return _evaluate_markers(self._markers, current_environment)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/a/r1/v/lib/python3.11/site-packages/packaging/markers.py", line 151, in _evaluate_markers
    groups[-1].append(_eval_op(lhs_value, op, rhs_value))
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/a/r1/v/lib/python3.11/site-packages/packaging/markers.py", line 109, in _eval_op
    return spec.contains(lhs, prereleases=True)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/a/r1/v/lib/python3.11/site-packages/packaging/specifiers.py", line 565, in contains
    normalized_item = _coerce_version(item)
                      ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/a/r1/v/lib/python3.11/site-packages/packaging/specifiers.py", line 36, in _coerce_version
    version = Version(version)
              ^^^^^^^^^^^^^^^^
  File "/Users/ssbarnea/c/a/r1/v/lib/python3.11/site-packages/packaging/version.py", line 197, in __init__
    raise InvalidVersion(f"Invalid version: '{version}'")
packaging.version.InvalidVersion: Invalid version: '3.11.0+'

@frostming
Copy link
Collaborator

packaging.version.InvalidVersion: Invalid version: '3.11.0+'

So i guess pip install "tomllib; python_full_version < '3.11.0'" will also fail

@ssbarnea
Copy link

ssbarnea commented Jan 19, 2023

Definitely that it will not work like expected, you are probably the first person that I see using it in markers instead of the documented pip install "tomllib; python_version < '3.11.0'"

https://sourcegraph.com/search?q=context:global+path:setup.cfg+%22python_version+%3C%22&patternType=standard&sm=0&groupBy=repo with uncounted results versus https://sourcegraph.com/search?q=context:global+path:setup.cfg+python_full_version&patternType=standard&sm=0&groupBy=repo which reports only one project using that and event that one also uses python_version at the same time, so is not affected.

I do not have the link now, but I do remember reading probably even official docs that version comparison should be made using the already provided version tuple, sys.version_info.

@ssbarnea
Copy link

@frostming Any more ideas on how to address this issue? Even if the line that you asked me to try does not "work" as expected, it does not raise a tracedump, it just evaluates as false with '3.11.0+' version.

I am really wondering why pdm really needs to attempt to parse python_full_version / platform.python_version() when we already have a much more reliable way to retrieve full version information about python version? --- one that can also be used in comparisions:

>>> sys.version_info
sys.version_info(major=3, minor=11, micro=0, releaselevel='final', serial=0)

@frostming
Copy link
Collaborator

I am really wondering why pdm really needs to attempt to parse python_full_version / platform.python_version() when we already have a much more reliable way to retrieve full version information about python version? --- one that can also be used in comparisions:

It is not determined by PDM, if the requirement contains environment markers with python_full_version, the python version will be parsed and compared

@ssbarnea
Copy link

Related to pyenv/pyenv#2598 - how this is dealt with depends. I did not see any place in Python documentation that states that python_full_version must be parsable by "Version()". IMHO, assuming that was a bug, not the fact that someone used it in a marker, especially as that causes a stacktrace.

@frostming
Copy link
Collaborator

frostming commented Jan 31, 2023

The marker evaluation behavior indeed is standardized by PEP 508, notably:

Comparisons in marker expressions are typed by the comparison operator. The <marker_op> operators that are not in <version_cmp> perform the same as they do for strings in Python. The <version_cmp> operators use the PEP 440 version comparison rules when those are defined (that is when both sides have a valid version specifier)

and

Marker Python equivalent Sample values
python_full_version platform.python_version() 3.4.0, 3.5.0b1

I think this is clear enough, it doesn't suggest using sys.version_info for the python_full_version comparison. If you want to compare against sys.version_info(or more precisely, platform.python_version_tuple()), use the python_version marker. It is the package author's responsibility to choose the correct marker, and PDM won't do any silent replacement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants