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

Pass Install Extras to Markers #9553

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

reesehyde
Copy link

@reesehyde reesehyde commented Jul 15, 2024

Pull Request Check List

Resolves:

This PR exposes the extras supplied at install to the 'extra' marker for dependencies, taking up @radoering's offer to create a PR implementing this functionality to allow switching torch installation versions based on a cuda extra.

It is a duplicate of the feature in python-poetry/poetry-core#613 but it appears that PR is no longer active, with the last updates in August 2023 and an unanswered question about timeline from March 2024. This PR takes a different approach: I noticed that python-poetry/poetry-core#636 added special handling for the extras marker value but that marker isn't populated often, so I opted to populate the value when extras are specified in the installer.

See issues and unit tests for more details but the idea is to, among other things, enable exclusive extras like so:

[tool.poetry]
package-mode = false

[tool.poetry.dependencies]
python = "^3.10"
click = [
    { markers = "extra == 'one' and extra != 'two' and extra != 'three'", version = "8.1.1", optional = true},
    { markers = "extra != 'one' and extra == 'two' and extra != 'three'", version = "8.1.2", optional = true},
    { markers = "extra != 'one' and extra != 'two' and extra == 'three'", version = "8.1.3", optional = true}
 ]

[tool.poetry.extras]
one = ["click"]
two = ["click"]
three = ["click"]

@radoering it looks like you're very familiar with this issue, would you be the right person to review? 🙏
I'm sure I'm missing something here but look forward to iterating!

Edit by @radoering: fix links to poetry-core PRs

  • Added tests for changed code.
  • Updated documentation for changed code.

@Secrus Secrus requested a review from radoering July 16, 2024 09:22
@radoering
Copy link
Member

radoering commented Jul 16, 2024

I will try to take a closer look at the end of the week.

Copy link
Member

@radoering radoering left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really good. Nice work. 👍

It looks like some of the changes are not tested yet or may be redundant. See separate comments for details.

"""
Marker values, per:
1. marker_env of `self._env`
2. 'extras' will be added to the 'extra' marker if not already present
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. cannot happen, can it?

Comment on lines +988 to +991
if "extra" not in result:
result["extra"] = set(extras)
else:
result["extra"] = set(result["extra"]).union(extras)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think that it is possible that extra is already present. This might even be an error. Thus, maybe we just assert "extra" not in result.

@@ -860,7 +881,7 @@ def _is_relevant_marker(
get_python_constraint_from_marker(marker)
)
and (active_extras is None or marker.validate({"extra": active_extras}))
and (not self._env or marker.validate(self._env.marker_env))
and (not self._env or marker.validate(self._marker_values(active_extras)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change should not be necessary because extras are already handled one line above (no matter if self._env is set or not).

@@ -601,7 +620,9 @@ def complete_package(

# For dependency resolution, markers of duplicate dependencies must be
# mutually exclusive.
active_extras = None if package.is_root() else dependency.extras
active_extras = (
self._active_root_extras if package.is_root() else dependency.extras
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like that is not tested yet. (Tests succeed without this change.) You probably have to add a test similar to test_solver_resolves_duplicate_dependency_in_extra, which tests this for non root extras.

@@ -541,7 +555,12 @@ def complete_package(
if dep.name in self.UNSAFE_PACKAGES:
continue

if self._env and not dep.marker.validate(self._env.marker_env):
active_extras = (
self._active_root_extras if package.is_root() else dependency.extras
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the non root part is not tested yet. (Tests succeed when replacing dependency.extras with None.) Maybe, this path can be triggered by taking one of your tests and introducing an intermediate package?

Comment on lines +465 to +474
and (
not self._env
or dep.marker.validate(
self._marker_values(
self._active_root_extras
if dependency_package.package.is_root()
else dependency_package.dependency.extras
)
)
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this change is not required by any test. To be honest, I have currently no idea if this change is redundant or if a test is missing...

@reesehyde
Copy link
Author

Thank you so much for the great feedback here! All the comments make sense to me, I'm working through it and will submit updates and responses later this week

@GeeCastro
Copy link

GeeCastro commented Aug 22, 2024

How would this differentiate between the extras for the project itself and the extras for a dependency package?

  1. For the project the scenario would be "I want to install my project with cuda124 extra" which I guess you could interface with the -E/--extra option
poetry install -E cuda124

and define the in the package

[tool.poetry.extras]
cuda124 = ["torch"]

Here your suggestion would add

[tool.poetry.dependencies]
python = "^3.10"
torch = [
    { markers = "extra == 'cuda124'", version = "2.4.0+cu124", optional = true},
                ^^^^^^
  1. And a package-specific extra for example
poetry add "pandas[aws]"

which would show in the extras field as follows

[tool.poetry.dependencies]
python = "^3.11"
pandas = {extras = ["aws"], version = "^2.2.2"}
          ^^^^^

Would the differentiation be made on the following?

  1. project extras in the marker string
  2. package's extras in the extras field

If so, I must have misunderstood the docs because I thought these 2 syntaces express the same requirements/constraints

@reesehyde
Copy link
Author

I apologize for the long delay here, I had to pause this before I managed to finish all the updates. But it hasn't left my mind, and I'll be able to resume the updates in a couple weeks and will push them up and respond to everything in mid-September! Thanks for the patience

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants