poetry: Cannot install Apache Airflow due to ambiguous constraint

  • Poetry version: Poetry (version 1.2.2)
  • Python version: Python: 3.10.8
  • OS version and name: MacOS 13.0.1
  • pyproject.toml:
[tool.poetry.dependencies]
python = "^3.10"
apache-airflow-providers-google = { version = "==8.5.0rc1", allow-prereleases = true }
  • I am on the latest stable Poetry version, installed using a recommended method.
  • I have searched the issues of this repo and believe that this is not a duplicate.
  • I have consulted the FAQ and blog for any relevant entries or release notes.
  • If an exception occurs when executing a command, I executed it again in debug mode (-vvv option) and have included the output below.

Issue

I am unable to run poetry lock with the above dependency.

Expected

Poetry locks correctly using apache-airflow-providers-common-sql==1.3.0rc1

Actual

Poetry returns the following error

poetry lock

Updating dependencies
Resolving dependencies... (1.5s)

Because apache-airflow-providers-google (8.5.0rc1) depends on apache-airflow-providers-common-sql (>=1.3.0) which doesn't match any versions, apache-airflow-providers-google is forbidden.
So, because poetry depends on apache-airflow-providers-google (==8.5.0rc1), version solving failed.

It looks like poetry has the following bug

Poetry is not supported to install airflow, and it has apparently a bug that it cannot properly handle the vaild >= .* requirement.

Note: the reproducibility of this example will stop once airflow publishes the final release of apache-airflow-providers-common-sql==1.3.0

Verifying that pypi has the expected requirement

API response

import json

import requests

package_name = 'apache-airflow-providers-google/8.5.0rc1'
url = 'https://pypi.python.org/pypi/' + str(package_name) + '/json'
data = requests.get(url).json()

print(data['info']['requires_dist'])

for apache-airflow-provider-common-sql returns the following

apache-airflow-providers-common-sql (>=1.3.0.*)

Further Notes

pipenv & pip run without issue

Pipenv

Pipfile

[packages]
apache-airflow-providers-google = { version = "==8.5.0rc1", pre="true" }

CLI

mkdir test-pipenv && cd test-pipenv
pip install pipenv
pipenv lock
nano pipfile

pipenv lock                                                                                                                                                                                                                   
Locking [packages] dependencies...
Building requirements...
Resolving dependencies...
✔ Success! 
Locking [dev-packages] dependencies...
Updated Pipfile.lock (43302be42e771518e472473549c7ab11902fd6f2e7eed04c64ffcd2d5f9be4e8)!

Locked Packages

...
        "apache-airflow-providers-common-sql": {
            "hashes": [
                "sha256:9308d3ac4d1f888f1a1f91c18cfa493291e333635cf0a0f93565581a83b1f097",
                "sha256:f5eb101f15a3535f879509873992d7194ef3e2a48316c984a0fc2a3a3af9001e"
            ],
            "markers": "python_version ~= '3.7'",
            "version": "==1.3.0rc1"
        },
        "apache-airflow-providers-google": {
            "hashes": [
                "sha256:a6d94a647c4dca9b2b75b4bf98c20c3809eb4ff8ccf4e7219f19df8fec3ea0de",
                "sha256:db9105714472b9cf344b55a72172dd87d85b7de5c8c5755bd86007c23d3ec3cf"
            ],
            "index": "pypi",
            "version": "==8.5.0rc1"
        },
...

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 38 (13 by maintainers)

Commits related to this issue

Most upvoted comments

When you talk about poetry “supporting constraints” - I think you mean: things that are like requirements eg “foo>=1.2”, but which don’t actually require the named package to be installed?

presumably the use case for such things would be for transitive dependencies where you don’t want to specify an explicit dependency, but if the solver does encounter that package then you want to guide it towards (or away from) some particular version?

I’ve seen requests for similar, but don’t recall anything spelling out this particular approach.

Feel free to raise a feature request where folk can figure out whether this is a good idea, or a duplicate, or neither, or both.

Fix for airflow provider packages in https://github.com/apache/airflow/pull/27727

Just noting that indeed poetry does make a mess of prefix matching on == constraints: this code

#!/usr/bin/env python3

from poetry.core.constraints.version.version import Version
from poetry.core.constraints.version import parse_constraint

constraint = parse_constraint("==1.1.*")
version = Version.parse("1.1a1")
allowed = constraint.allows(version)
print(f"{allowed=}")

says allowed=False which certainly disagrees with one of the examples in PEP440.

So while - at least according to my reading - the problem in this particular issue is the airflow constraint rather than the poetry handling of it, we’re certainly very adjacent to a poetry bug

A new one would be lovely, thanks

Yep. That’s what I have in my PR.

https://peps.python.org/pep-0440/#summary-of-permitted-suffixes-and-relative-ordering

Within a numeric release (1.0, 2.7.3), the following suffixes are permitted and MUST be ordered as shown:

.devN, aN, bN, rcN, <no suffix>, .postN

Each of those (alpha, beta, etc.) are completely separate naming space.

this is not true. Their order is well defined by PEP440 and a regular >= starting at the earliest should meet your need.

I think ==1.3.0.* achieves what you want per PEP 440 (though I suspect that poetry will make a mess of that). Also >=1.3.0rc1 should work (and I think poetry will get that right).

So far as I can tell PEP 440 simply doesn’t allow >=1.3.0.* and so it’s presumably more or less arbitrary whether pip or poetry happens to do what you want

@potiuk I don’t see any justification for >=1.3.0.* in https://peps.python.org/pep-0440/#inclusive-ordered-comparison.

==1.3.0.* would be allowed per the previous section (and quite possibly mis-handled by poetry) but the >= operator requires an actual version identifier - which 1.3.0.* is not.

@r-richmond that still requires that “the available version satisfies the version specifier”. poetry has done its best with the malformed version specifier, interpreting it as >=1.3.0, but no available version does satisfy that.

I’m at risk of being out of my depth here but the following statement

not a bug, because the requirement is >=1.3.0 and the highest available version is 1.3.0rc1 - which is strictly less than 1.3.0

Appears to be at odds with the spec defined in pep 440 Specifically the last portion of this sentence

Pre-releases of any kind, including developmental releases, are implicitly excluded from all version specifiers, unless they are already present on the system, explicitly requested by the user, or if the only available version that satisfies the version specifier is a pre-release.

Further Details

From the following section (Bullet 2)

  • By default, dependency resolution tools SHOULD:

    • accept already installed pre-releases for all version specifiers
    • accept remotely available pre-releases for version specifiers where there is no final or post release that satisfies the version specifier
    • exclude all other pre-releases from consideration

From the spec on Request Options

  • Dependency resolution tools SHOULD also allow users to request the following alternative behaviours:

    • accepting pre-releases for all version specifiers

I believe this is what the intent of poetry’s allow-prereleases is as well. so even if poetry wanted to break the implied behavior above shouldn’t my usage of that flag in pyproject.toml have allowed the pre-release.

not a bug, because the requirement is >=1.3.0 and the highest available version is 1.3.0rc1 - which is strictly less than 1.3.0

(possibly also duplicate #4405, though I’ve not read the whole of that issue)