Skip to content

Fix urls entries in pyproject.toml with variable cases #803

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

Closed

Conversation

gantoine
Copy link

@gantoine gantoine commented Jan 6, 2025

Came across an issue trying to poetry install today with streaming-form-data in my pyproject.toml. The library's pyproject.toml uses lowercase keys for entries under [tool.poetry.urls], which breaks when Metadata.from_package tries to build project_urls. Added a couple tests for upper, lower and mixed-case entries.

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

Summary by Sourcery

Tests:

  • Add tests for URL entries with different capitalization.

Error logs from docker build

#49 53.48 PEP517 build of a dependency failed
#49 53.48 
#49 53.48 Backend subprocess exited when trying to invoke build_wheel
#49 53.48 
#49 53.48     | Command '['/tmp/tmpfmm18r98/.venv/bin/python', '/usr/local/lib/python3.12/site-packages/pyproject_hooks/_in_process/_in_process.py', 'build_wheel', '/tmp/tmp_mphur1a']' returned non-zero exit status 1.
#49 53.48     | 
#49 53.48     | Traceback (most recent call last):
#49 53.48     |   File "/usr/local/lib/python3.12/site-packages/pyproject_hooks/_in_process/_in_process.py", line 389, in <module>
#49 53.48     |     main()
#49 53.48     |   File "/usr/local/lib/python3.12/site-packages/pyproject_hooks/_in_process/_in_process.py", line 373, in main
#49 53.48     |     json_out["return_val"] = hook(**hook_input["kwargs"])
#49 53.48     |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#49 53.48     |   File "/usr/local/lib/python3.12/site-packages/pyproject_hooks/_in_process/_in_process.py", line 280, in build_wheel
#49 53.48     |     return _build_backend().build_wheel(
#49 53.48     |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#49 53.48     |   File "/tmp/tmpfmm18r98/.venv/lib/python3.12/site-packages/poetry/core/masonry/api.py", line 58, in build_wheel
#49 53.48     |     return WheelBuilder.make_in(
#49 53.48     |            ^^^^^^^^^^^^^^^^^^^^^
#49 53.48     |   File "/tmp/tmpfmm18r98/.venv/lib/python3.12/site-packages/poetry/core/masonry/builders/wheel.py", line 84, in make_in
#49 53.48     |     wb = WheelBuilder(
#49 53.48     |          ^^^^^^^^^^^^^
#49 53.48     |   File "/tmp/tmpfmm18r98/.venv/lib/python3.12/site-packages/poetry/core/masonry/builders/wheel.py", line 65, in __init__
#49 53.48     |     super().__init__(poetry, executable=executable)
#49 53.48     |   File "/tmp/tmpfmm18r98/.venv/lib/python3.12/site-packages/poetry/core/masonry/builders/builder.py", line 43, in __init__
#49 53.48     |     self._meta = Metadata.from_package(self._package)
#49 53.48     |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#49 53.48     |   File "/tmp/tmpfmm18r98/.venv/lib/python3.12/site-packages/poetry/core/masonry/metadata.py", line 112, in from_package
#49 53.48     |     if name == "repository" and url == package.urls["Repository"]:
#49 53.48     |                                        ~~~~~~~~~~~~^^^^^^^^^^^^^^
#49 53.48     | KeyError: 'Repository'
#49 53.48 
#49 53.48 Note: This error originates from the build backend, and is likely not a problem with poetry but one of the following issues with streaming-form-data (1.19.0)
#49 53.48 
#49 53.48   - not supporting PEP 517 builds
#49 53.48   - not specifying PEP 517 build requirements correctly
#49 53.48   - the build requirements are incompatible with your operating system or Python version
#49 53.48   - the build requirements are missing system dependencies (eg: compilers, libraries, headers).
#49 53.48 
#49 53.48 You can verify this by running pip wheel --no-cache-dir --use-pep517 "streaming-form-data (==1.19.0)".
#49 53.48 
#49 ERROR: process "/bin/sh -c poetry install --no-ansi --no-cache" did not complete successfully: exit code: 1

Copy link

sourcery-ai bot commented Jan 6, 2025

Reviewer's Guide by Sourcery

This pull request fixes an issue where poetry install would fail if the urls entries in pyproject.toml were not in lowercase. This was due to the Metadata.from_package function not handling different casing for the urls. The fix adds support for case-insensitive url keys and includes tests for upper, lower, and mixed-case entries.

Sequence diagram for URL handling in Metadata.from_package

sequenceDiagram
    participant P as Package
    participant M as Metadata.from_package

    P->>M: package.urls
    Note over M: Convert URL key to lowercase
    alt homepage URL
        M->>M: Check if homepage matches
    else repository URL
        M->>M: Check if repository matches
    else documentation URL
        M->>M: Check if documentation matches
    else other URLs
        M->>M: Add to project_urls
    end
Loading

Class diagram showing Package class URL handling changes

classDiagram
    class Package {
        -dict[str, str] _urls
        +urls() dict[str, str]
        +homepage str
    }
    note for Package "Added _urls field for testing
Modified urls property to handle
case-insensitive URL keys"
Loading

File-Level Changes

Change Details Files
Add tests for case-insensitive urls
  • Added tests for upper, lower, and mixed-case entries in tool.poetry.urls of the pyproject.toml file.
  • Added a test to verify that only entries other than homepage, repository, and documentation are added to project_urls when they are in different cases.
  • Added a test to verify that only entries other than homepage, repository, and documentation are added to project_urls when they are in lowercase.
  • Added a test to verify that only entries other than homepage, repository, and documentation are added to project_urls when they are in mixed case.
tests/masonry/test_metadata.py
Handle case-insensitive urls in Metadata.from_package
  • Modified Metadata.from_package to handle case-insensitive url keys.
  • Added a private _urls attribute to the ProjectPackage class for testing purposes.
  • Added logic to the urls property of the ProjectPackage class to return the _urls attribute if it is set.
  • Updated the Metadata.from_package function to use the lowercase version of the url keys when checking for special cases like homepage, repository, and documentation.
src/poetry/core/masonry/metadata.py
src/poetry/core/packages/package.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @gantoine - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟢 General issues: all looks good
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

continue
if name == "repository" and url == package.urls["Repository"]:
if name_lower == "repository" and url == package.urls.get(name):
Copy link
Contributor

Choose a reason for hiding this comment

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

is this not always definitely true? because name and url are a pair from package.urls.items().

I am not sure what the intent of this code was but this variation seems pointless?

Copy link
Contributor

@dimbleby dimbleby Jan 6, 2025

Choose a reason for hiding this comment

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

perhaps the original code was just a roundabout way of reading package.repository_url - and doing that directly is a better fix?

Copy link
Author

Choose a reason for hiding this comment

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

yeah i think the original code wasn't well written, i'll simplify it 👍🏼

@@ -117,6 +117,9 @@ def __init__(

self._yanked = yanked

# Currently only used for testing purposes
Copy link
Contributor

@dimbleby dimbleby Jan 6, 2025

Choose a reason for hiding this comment

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

this is not true, per the change at line 360.

either way I do not love this, why not set up homepage, repository url, and documentation url in testing?

Copy link
Author

Choose a reason for hiding this comment

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

Sorry I should have clarified, this is happening in CI for a real project of mine, not just in testing. The addition of self._urls is for the tests i wrote in test_metadata.py, since url is a @property and can't be set directly.

https://github.com/rommapp/romm/actions/runs/12623342037/job/35174039307#step:10:3085

Copy link
Member

Choose a reason for hiding this comment

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

I would like to avoid adding production code that is only required for testing. Can you use a PropertyMock instead? See https://github.com/python-poetry/poetry/blob/5d8f8800b2cbb53da8057965663844bd1e04e455/tests/console/commands/test_check.py#L82-L86 for example.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is entirely redundant in the current version, the tests can manipulate the homepage and repository-url fields directly

DinisCruz added a commit to owasp-sbot/OSBot-Utils that referenced this pull request Jan 6, 2025
This has been long time coming (for example for performance reasons), but was triggered by this poetry update that crashed the CI pipeline:

 - https://github.com/owasp-sbot/OSBot-Utils/actions/runs/12632720785/job/35198284338
 - python-poetry/poetry#9961
 - python-poetry/poetry#9957
 - python-poetry/poetry-core#803
@@ -117,6 +117,9 @@ def __init__(

self._yanked = yanked

# Currently only used for testing purposes
Copy link
Member

Choose a reason for hiding this comment

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

I would like to avoid adding production code that is only required for testing. Can you use a PropertyMock instead? See https://github.com/python-poetry/poetry/blob/5d8f8800b2cbb53da8057965663844bd1e04e455/tests/console/commands/test_check.py#L82-L86 for example.

if name == "repository" and url == package.urls["Repository"]:
continue
if name == "documentation" and url == package.urls["Documentation"]:
if name_lower == "repository" or name_lower == "documentation":
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess the tests have started failing because you have changed the logic

probably want to check "if name is repository and url is repository-url", etc

@gantoine
Copy link
Author

gantoine commented Jan 6, 2025

Right updated the PR based on feedback, however it seems the downstream poetry tests are still failing with these changes. I assume I'll need to update those before this PR is accepted?

FAILED tests/masonry/builders/test_editable_builder.py::test_builder_installs_proper_files_for_standard_packages[simple_project_legacy] - AssertionError: assert 'Metadata-Ver...=========\n\n' == 'Metadata-Ver...=========\n\n'
  
  Skipping 1114 identical leading characters in diff, use -v to show
    n Modules
  + Project-URL: Documentation, https://python-poetry.org/docs
  + Project-URL: Repository, https://github.com/python-poetry/poetry
    Description-Content-Type: text/x-rst

@dimbleby
Copy link
Contributor

dimbleby commented Jan 6, 2025

poetry-core tests are also currently broken

In passing I spotted PEP753 which says that poetry should change its handling of these anyway, so I have submitted #807

SebieF added a commit to biocentral/biotrainer that referenced this pull request Jan 7, 2025
Otherwise, pip install . is currently not possible: python-poetry/poetry-core#803
SebieF added a commit to sacdallago/biotrainer that referenced this pull request Jan 7, 2025
Otherwise, pip install . is currently not possible: python-poetry/poetry-core#803
@radoering
Copy link
Member

Superseded by #807

@radoering radoering closed this Jan 11, 2025
SebieF added a commit to sacdallago/biotrainer that referenced this pull request Feb 6, 2025
Otherwise, pip install . is currently not possible: python-poetry/poetry-core#803
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