Skip to content

lowest-direct resolution error with constraints #8819

Description

@azmeuk

The following dockerfile is the minimum reproducible example of a situation I met in a real project CI.

The Dockerfile runs with python 3.10 but there is a setuptools dependency for python 3.12+ in pyproject.toml.
The other dependency in pyproject.toml is pycountry, on its 22.1.10 version, and it has a loose dependency to setuptools.

In this situation I would expect setuptools to not be a direct dependency. However uv --resolution=lowest-direct appears to resolve at the minimum setuptools available, that is 0.7.2. Of course, this is an old incompatible version, and the installation fails.

Removing the python_version>='3.12' constraint on the setuptools dependency appears to solve the issue, so I suppose this is a cause of the bug.

pyproject.toml

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "foobar"
version = "0.0.1"
requires-python = ">=3.10"

dependencies = [
    "pycountry >= 22.1.10",
    "setuptools >= 50.0.0; python_version>='3.12'"
]

Dockerfile

FROM ghcr.io/astral-sh/uv:0.4-python3.10-bookworm-slim
WORKDIR /foobar
COPY foobar pyproject.toml /foobar/
RUN uv sync --resolution=lowest-direct
docker build .           
[+] Building 11.3s (8/8) FINISHED                                                                                           docker:default
 => [internal] load build definition from Dockerfile                                                                                  0.1s
 => => transferring dockerfile: 185B                                                                                                  0.0s
 => [internal] load metadata for ghcr.io/astral-sh/uv:0.4-python3.10-bookworm-slim                                                    0.0s
 => [internal] load .dockerignore                                                                                                     0.1s
 => => transferring context: 2B                                                                                                       0.0s
 => [1/4] FROM ghcr.io/astral-sh/uv:0.4-python3.10-bookworm-slim                                                                      0.0s
 => [internal] load build context                                                                                                     0.1s
 => => transferring context: 326B                                                                                                     0.0s
 => CACHED [2/4] WORKDIR /foobar                                                                                                      0.0s
 => [3/4] COPY foobar pyproject.toml /foobar/                                                                                         0.3s
 => ERROR [4/4] RUN uv sync --resolution=lowest-direct                                                                               10.5s
------                                                                                                                                     
 > [4/4] RUN uv sync --resolution=lowest-direct:                                                                                           
1.423 Using CPython 3.10.15 interpreter at: /usr/local/bin/python                                                                          
1.423 Creating virtual environment at: .venv                                                                                               
1.425 warning: No `requires-python` value found in the workspace. Defaulting to `>=3.10`.                                                  
8.149 Resolved 4 packages in 6.72s                                                                                                         
9.249 error: Failed to prepare distributions
9.249   Caused by: Failed to download and build `setuptools==0.7.2`
9.249   Caused by: Build backend failed to determine requirements with `build_wheel()` (exit status: 1)
9.249 
9.249 [stdout]
9.249 --- build/src/tests/api_tests.txt	(original)
9.249 +++ build/src/tests/api_tests.txt	(refactored)
9.249 @@ -39,7 +39,7 @@
9.249      >>> dist.py_version == sys.version[:3]
9.249      True
9.249  
9.249 -    >>> print dist.platform
9.249 +    >>> print(dist.platform)
9.249      None
9.249  
9.249  Including various computed attributes::
9.249 @@ -199,7 +199,7 @@
9.249  You can ask a WorkingSet to ``find()`` a distribution matching a requirement::
9.249  
9.249      >>> from pkg_resources import Requirement
9.249 -    >>> print ws.find(Requirement.parse("Foo==1.0"))    # no match, return None
9.249 +    >>> print(ws.find(Requirement.parse("Foo==1.0")))    # no match, return None
9.249      None
9.249  
9.249      >>> ws.find(Requirement.parse("Bar==0.9"))  # match, return distribution
9.249 @@ -212,7 +212,7 @@
9.249      ...     ws.find(Requirement.parse("Bar==1.0"))
9.249      ... except pkg_resources.VersionConflict:
9.249      ...     exc = sys.exc_info()[1]
9.249 -    ...     print(str(exc))
9.249 +    ...     print((str(exc)))
9.249      ... else:
9.249      ...     raise AssertionError("VersionConflict was not raised")
9.249      (Bar 0.9 (http://example.com/something), Requirement.parse('Bar==1.0'))
9.249 @@ -222,7 +222,7 @@
9.249  once for each existing distribution in the working set, and then is called
9.249  again for new distributions added thereafter::
9.249  
9.249 -    >>> def added(dist): print "Added", dist
9.249 +    >>> def added(dist): print("Added", dist)
9.249      >>> ws.subscribe(added)
9.249      Added Bar 0.9
9.249      >>> foo12 = Distribution(project_name="Foo", version="1.2", location="f12")
9.249 @@ -338,52 +338,52 @@
9.249      >>> from pkg_resources import invalid_marker as im, evaluate_marker as em
9.249      >>> import os
9.249  
9.249 -    >>> print(im("sys_platform"))
9.249 +    >>> print((im("sys_platform")))
9.249      Comparison or logical expression expected
9.249  
9.249 -    >>> print(im("sys_platform=="))  # doctest: +ELLIPSIS
9.249 +    >>> print((im("sys_platform==")))  # doctest: +ELLIPSIS
9.249      unexpected EOF while parsing (...line 1)
9.249  
9.249 -    >>> print(im("sys_platform=='win32'"))
9.249 -    False
9.249 -
9.249 -    >>> print(im("sys=='x'"))
9.249 +    >>> print((im("sys_platform=='win32'")))
9.249 +    False
9.249 +
9.249 +    >>> print((im("sys=='x'")))
9.249      Unknown name 'sys'
9.249  
9.249 -    >>> print(im("(extra)"))
9.249 +    >>> print((im("(extra)")))
9.249      Comparison or logical expression expected
9.249  
9.249 -    >>> print(im("(extra"))  # doctest: +ELLIPSIS
9.249 +    >>> print((im("(extra")))  # doctest: +ELLIPSIS
9.249      unexpected EOF while parsing (...line 1)
9.249  
9.249 -    >>> print(im("os.open('foo')=='y'"))
9.249 +    >>> print((im("os.open('foo')=='y'")))
9.249      Language feature not supported in environment markers
9.249  
9.249 -    >>> print(im("'x'=='y' and os.open('foo')=='y'"))   # no short-circuit!
9.249 +    >>> print((im("'x'=='y' and os.open('foo')=='y'")))   # no short-circuit!
9.249      Language feature not supported in environment markers
9.249  
9.249 -    >>> print(im("'x'=='x' or os.open('foo')=='y'"))   # no short-circuit!
9.249 +    >>> print((im("'x'=='x' or os.open('foo')=='y'")))   # no short-circuit!
9.249      Language feature not supported in environment markers
9.249  
9.249 -    >>> print(im("'x' < 'y'"))
9.249 +    >>> print((im("'x' < 'y'")))
9.249      '<' operator not allowed in environment markers
9.249  
9.249 -    >>> print(im("'x' < 'y' < 'z'"))
9.249 +    >>> print((im("'x' < 'y' < 'z'")))
9.249      Chained comparison not allowed in environment markers
9.249  
9.249 -    >>> print(im("r'x'=='x'"))
9.249 +    >>> print((im("r'x'=='x'")))
9.249      Only plain strings allowed in environment markers
9.249  
9.249 -    >>> print(im("'''x'''=='x'"))
9.249 +    >>> print((im("'''x'''=='x'")))
9.249      Only plain strings allowed in environment markers
9.249  
9.249 -    >>> print(im('"""x"""=="x"'))
9.249 +    >>> print((im('"""x"""=="x"')))
9.249      Only plain strings allowed in environment markers
9.249  
9.249 -    >>> print(im(r"'x\n'=='x'"))
9.249 +    >>> print((im(r"'x\n'=='x'")))
9.249      Only plain strings allowed in environment markers
9.249  
9.249 -    >>> print(im("os.open=='y'"))
9.249 +    >>> print((im("os.open=='y'")))
9.249      Language feature not supported in environment markers
9.249  
9.249      >>> em('"x"=="x"')
9.249 
9.249 [stderr]
9.249 RefactoringTool: Skipping optional fixer: buffer
9.249 RefactoringTool: Skipping optional fixer: idioms
9.249 RefactoringTool: Skipping optional fixer: set_literal
9.249 RefactoringTool: Skipping optional fixer: ws_comma
9.249 RefactoringTool: Refactored build/src/tests/api_tests.txt
9.249 RefactoringTool: Files that were modified:
9.249 RefactoringTool: build/src/tests/api_tests.txt
9.249 root: copying setuptools/tests/indexes/test_links_priority/external.html -> build/src/setuptools/tests/indexes/test_links_priority
9.249 root: copying setuptools/tests/indexes/test_links_priority/simple/foobar/index.html -> build/src/setuptools/tests/indexes/test_links_priority/simple/foobar
9.249 root: copying docs/conf.py -> build/src/docs
9.249 root: copying docs/formats.txt -> build/src/docs
9.249 root: copying docs/index.txt -> build/src/docs
9.249 root: copying docs/easy_install.txt -> build/src/docs
9.249 root: copying docs/python3.txt -> build/src/docs
9.249 root: copying docs/using.txt -> build/src/docs
9.249 root: copying docs/setuptools.txt -> build/src/docs
9.249 root: copying docs/merge.txt -> build/src/docs
9.249 root: copying docs/roadmap.txt -> build/src/docs
9.249 root: copying docs/pkg_resources.txt -> build/src/docs
9.249 root: copying docs/merge-faq.txt -> build/src/docs
9.249 root: copying docs/_theme/nature/theme.conf -> build/src/docs/_theme/nature
9.249 root: copying docs/_theme/nature/static/pygments.css -> build/src/docs/_theme/nature/static
9.249 root: copying docs/_theme/nature/static/nature.css_t -> build/src/docs/_theme/nature/static
9.249 root: copying docs/Makefile -> build/src/docs
9.249 root: copying docs/_templates/indexsidebar.html -> build/src/docs/_templates
9.249 root: copying _markerlib/markers.py -> build/src/_markerlib
9.249 root: copying _markerlib/__init__.py -> build/src/_markerlib
9.249 root: copying ez_setup.py -> build/src
9.249 root: copying release.py -> build/src
9.249 root: copying setup.py -> build/src
9.249 root: copying distribute_setup.py -> build/src
9.249 root: copying pkg_resources.py -> build/src
9.249 root: copying easy_install.py -> build/src
9.249 root: copying CHANGES.txt -> build/src
9.249 root: copying CONTRIBUTORS.txt -> build/src
9.249 root: copying DEVGUIDE.txt -> build/src
9.249 root: copying README.txt -> build/src
9.249 root: copying MANIFEST.in -> build/src
9.249 root: copying launcher.c -> build/src
9.249 Traceback (most recent call last):
9.249   File "<string>", line 14, in <module>
9.249   File "/root/.cache/uv/builds-v0/.tmpiFWFBR/lib/python3.10/site-packages/setuptools/build_meta.py", line 333, in get_requires_for_build_wheel
9.249     return self._get_build_requires(config_settings, requirements=[])
9.249   File "/root/.cache/uv/builds-v0/.tmpiFWFBR/lib/python3.10/site-packages/setuptools/build_meta.py", line 303, in _get_build_requires
9.249     self.run_setup()
9.249   File "/root/.cache/uv/builds-v0/.tmpiFWFBR/lib/python3.10/site-packages/setuptools/build_meta.py", line 521, in run_setup
9.249     super().run_setup(setup_script=setup_script)
9.249   File "/root/.cache/uv/builds-v0/.tmpiFWFBR/lib/python3.10/site-packages/setuptools/build_meta.py", line 319, in run_setup
9.249     exec(code, locals())
9.249   File "<string>", line 34, in <module>
9.249 AttributeError: module 'distutils.util' has no attribute 'run_2to3'
------
Dockerfile:4
--------------------
   2 |     WORKDIR /foobar
   3 |     COPY foobar pyproject.toml /foobar/
   4 | >>> RUN uv sync --resolution=lowest-direct
   5 |     
--------------------
ERROR: failed to solve: process "/bin/sh -c uv sync --resolution=lowest-direct" did not complete successfully: exit code: 2

Activity

  1. changed the title [-]lowest-direct resolution also lowers indirect dependencies[/-] [+]lowest-direct resolution error with constraints[/+] on Nov 5, 2024
  2. charliermarsh commented on Nov 5, 2024

    @charliermarsh
    Member

    Ah interesting... I do think this is a bug. We consider anything listed in the pyproject.toml to be direct for all environments, even if it's only listed as direct in a specific environment.

  3. charliermarsh commented on Nov 5, 2024

    @charliermarsh
    Member

    (I assume this is not urgent / blocking since you can just remove the marker or add a [tool.uv.constraint-dependencies].)

  4. self-assigned this
    on Nov 5, 2024
  5. azmeuk commented on Nov 5, 2024

    @azmeuk
    Author

    Thank you for the quick fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions