Skip to content

Conversation

@radoering
Copy link
Member

@radoering radoering commented Nov 8, 2025

Pull Request Check List

Resolves: https://github.com/orgs/python-poetry/discussions/10601

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

Summary by Sourcery

Add support for free-threaded Python interpreters by introducing a free_threading marker and updating tag generation to include or exclude the 't' ABI suffix accordingly.

Bug Fixes:

  • Ensure get_supported_tags omits the 't' ABI when free_threading is false and includes it when true

Enhancements:

  • Add free_threading field to MarkerEnv and populate it in system_env and script_strings

Tests:

  • Add test_env_get_supported_tags_free_threading to verify tag lists for both free-threaded and standard interpreters

@sourcery-ai
Copy link

sourcery-ai bot commented Nov 8, 2025

Reviewer's Guide

Add detection and propagation of free-threaded (GIL-disabled) Python build in the environment markers and tag generation logic, and include tests to verify ABI ‘t’ suffix handling when free-threading is enabled.

Class diagram for updated MarkerEnv and tag generation

classDiagram
    class MarkerEnv {
        str interpreter_name
        str interpreter_version
        str sysconfig_platform
        bool free_threading
    }
    class Env {
        <<abstract>>
    }
    class VirtualEnv {
        get_supported_tags() : list[Tag]
    }
    MarkerEnv <|-- Env
    Env <|-- VirtualEnv
    VirtualEnv --> MarkerEnv : uses
Loading

Flow diagram for propagation of free_threading marker

flowchart TD
    A["sysconfig.get_config_var('Py_GIL_DISABLED')"] --> B["Set free_threading in MarkerEnv"]
    B --> C["get_supported_tags() uses free_threading"]
    C --> D["ABI 't' suffix added if free_threading is True"]
Loading

File-Level Changes

Change Details Files
Extend the MarkerEnv to include a free_threading flag and populate it during environment introspection
  • Add free_threading field to MarkerEnv type
  • Set free_threading from sysconfig.get_config_var("Py_GIL_DISABLED") in both script_strings and system_env
src/poetry/utils/env/base_env.py
src/poetry/utils/env/script_strings.py
src/poetry/utils/env/system_env.py
Make VirtualEnv.get_supported_tags aware of free-threaded builds by supplying custom ABI list
  • Read free_threading from marker_env
  • When CPython and free_threading is true, set abis to ["t"]
  • Pass abis into cpython_tags instead of relying on default ABIs
src/poetry/utils/env/virtual_env.py
Add pytest to verify supported tags include or exclude 't' ABI based on free_threading
  • Introduce test_env_get_supported_tags_free_threading with skip for non-CPython
  • Check absence of 't' suffix when free_threading is false, and presence when toggled to true
tests/utils/env/test_env.py

Possibly linked issues


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. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the 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 exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

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 there - I've reviewed your changes - here's some feedback:

  • Explicitly convert sysconfig.get_config_var("Py_GIL_DISABLED") to a boolean (e.g. bool(...) or compare to 1) to avoid storing non‐boolean values like '1' in marker_env.
  • Consider centralizing the free_threading detection logic instead of duplicating it in both script_strings.py and system_env.py to keep them in sync.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Explicitly convert sysconfig.get_config_var("Py_GIL_DISABLED") to a boolean (e.g. bool(...) or compare to 1) to avoid storing non‐boolean values like '1' in marker_env.
- Consider centralizing the free_threading detection logic instead of duplicating it in both script_strings.py and system_env.py to keep them in sync.

## Individual Comments

### Comment 1
<location> `src/poetry/utils/env/script_strings.py:89` </location>
<code_context>
     ),
     "interpreter_version": interpreter_version(),
     "sysconfig_platform": sysconfig.get_platform(),
+    "free_threading": sysconfig.get_config_var("Py_GIL_DISABLED") or False,
 }

</code_context>

<issue_to_address>
**suggestion:** Explicitly cast Py_GIL_DISABLED to bool for clarity.

Casting to bool ensures consistent boolean output, regardless of the original type returned by sysconfig.get_config_var.

```suggestion
    "free_threading": bool(sysconfig.get_config_var("Py_GIL_DISABLED")),
```
</issue_to_address>

### Comment 2
<location> `src/poetry/utils/env/system_env.py:77` </location>
<code_context>
     ),
     "interpreter_version": interpreter_version(),
     "sysconfig_platform": sysconfig.get_platform(),
+    "free_threading": sysconfig.get_config_var("Py_GIL_DISABLED") or False,
 }

</code_context>

<issue_to_address>
**suggestion:** Explicitly cast Py_GIL_DISABLED to bool for consistency.

Casting to bool prevents unexpected behavior if Py_GIL_DISABLED is not strictly boolean.

```suggestion
            "free_threading": bool(sysconfig.get_config_var("Py_GIL_DISABLED")),
```
</issue_to_address>

### Comment 3
<location> `src/poetry/utils/env/virtual_env.py:61` </location>
<code_context>
    def get_supported_tags(self) -> list[Tag]:
        from packaging.tags import compatible_tags
        from packaging.tags import cpython_tags
        from packaging.tags import generic_tags

        python = self.version_info[:3]
        interpreter_name = self.marker_env["interpreter_name"]
        interpreter_version = self.marker_env["interpreter_version"]
        sysconfig_platform = self.marker_env["sysconfig_platform"]
        free_threading = self.marker_env["free_threading"]

        abis: list[str] | None = None
        if interpreter_name == "pp":
            interpreter = "pp3"
        elif interpreter_name == "cp":
            interpreter = f"{interpreter_name}{interpreter_version}"
            if free_threading:
                abis = [f"{interpreter}t"]
        else:
            interpreter = None

        # Why using sysconfig.get_platform() and not ...
        # ... platform.machine()
        #  This one is also different for x86_64 Linux and aarch64 Linux,
        #  but it is the same for a 32 Bit and a 64 Bit Python on Windows!
        # ... platform.architecture()
        #  This one is also different for a 32 Bit and a 64 Bit Python on Windows,
        #  but it is the same for x86_64 Linux and aarch64 Linux!
        platforms = None
        if sysconfig_platform != sysconfig.get_platform():
            # Relevant for the following use cases, for example:
            # - using a 32 Bit Python on a 64 Bit Windows
            # - using an emulated aarch Python on an x86_64 Linux
            output = self.run_python_script(GET_PLATFORMS)
            platforms = json.loads(output)

        return [
            *(
                cpython_tags(python, abis=abis, platforms=platforms)
                if interpreter_name == "cp"
                else generic_tags(platforms=platforms)
            ),
            *compatible_tags(python, interpreter=interpreter, platforms=platforms),
        ]

</code_context>

<issue_to_address>
**issue (code-quality):** We've found these issues:

- Move assignments closer to their usage ([`move-assign`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/move-assign/))
- Simplify conditional into switch-like form [×2] ([`switch`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/switch/))
</issue_to_address>

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.

@radoering radoering force-pushed the fix-free-threading-support branch from eb93819 to c6d0f2a Compare November 8, 2025 16:30
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.

1 participant