Skip to content

Support functools.Placeholder in partial type inference (Python 3.14+) - #5598

Open
La1itchauhan wants to merge 3 commits into
pyrefly-org:mainfrom
La1itchauhan:support-functools-placeholder
Open

La1itchauhan wants to merge 3 commits into
pyrefly-org:mainfrom
La1itchauhan:support-functools-placeholder

Conversation

@La1itchauhan

@La1itchauhan La1itchauhan commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds type inference support for functools.Placeholder introduced in Python 3.14:

  • Preserves positional Placeholder arguments as required positional-only parameters rather than binding them.
  • Substitutes placeholders with explicit Any during target callability checks, avoiding false positive bad-argument-type and unknown-argument-type diagnostics.
  • Recognizes placeholders in expressions: Name, Attribute, Subscript (holes[0]), Named ((h := Placeholder)), Call (get_placeholder()), and If (Placeholder if flag else Placeholder). Other expression forms are not recognized.
  • Performs early keyword argument rejection (cannot use functools.Placeholder as keyword argument), including calls containing *args or **kwargs splats (partial(*targets, y=Placeholder) and partial(f, y=Placeholder, **extra)).
  • Tracked limitations via passing bug= tests: trailing positional placeholders accepted statically; hole plus **kwargs keyword collisions.
  • Intentional inference fallback behaviors covered by unit tests: generic overloads, nested partials, and ParamSpec targets.

Fixes #5479

  • I am a human, and I fully understand the code I am submitting.

Test Plan

  • Unit tests in pyrefly/lib/test/functools/partial.rs covering supported expressions, early keyword rejection, fallbacks, and bug= markers.
  • Formatting and linting verified locally:
    python3 test.py --no-test --no-tensor-shapes --no-conformance --no-jsonschema --no-extension
    cargo fmt -- --check
  • git status --short confirms no generated files modified.
  • Full CI test matrix green across Ubuntu, macOS, Windows, CodSpeed benchmarks, and 15 mypy_primer shards.

@meta-cla meta-cla Bot added the cla signed label Nov 9, 2026
@github-actions github-actions Bot added the size/m label Nov 9, 2026
@meta-codesync

meta-codesync Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

This pull request has been imported. If you are a Meta employee, you can view this in D130700129. (Because this pull request was imported automatically, there will not be any future comments.)

@La1itchauhan
La1itchauhan force-pushed the support-functools-placeholder branch from ff06121 to b54fde6 Compare November 9, 2026 18:59
@github-actions github-actions Bot added size/m and removed size/m labels Nov 9, 2026
@github-actions

This comment has been minimized.

@La1itchauhan
La1itchauhan marked this pull request as ready for review November 9, 2026 19:40
@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

✅ This PR meets all requirements from our pull request checklist.

Note: this comment updates automatically after changes to the PR. Updates may take up to half an hour.

@stroxler stroxler left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @La1itchauhan, thanks for the PR!

Could you rebase, and include the checkbox saying you're a human (sorry about that, but unfortunately we've introduced some friction and automated checks to help us keep the flow of incoming PRs at something we can handle)?

The rebase should trigger an auto-import of the diff, which will make it easier to review and merge.

While you're at it... based on an initial AI-assisted code review I think the overall direction here is pretty good. But it appears that the support is incomplete, there are a number of gaps. For each gap, we have a choice: we could either fix the gap with a unit test covering it, or opt not to fix the gap but include a (passing) unit test with a bug= marker so that we don't lose track of it.


  1. False “argument type is unknown” on a known placeholder.
    partial(f, Placeholder, 2) produces unknown-argument-type when that diagnostic is enabled. This affects the basic supported use case—not an exotic expression—because the implementation substitutes implicit Any. I’d fix this before merging.

  2. Placeholder-valued expressions aren’t recognized.
    Examples: holes[0], (hole := Placeholder), a conditional with placeholder-valued branches, or get_placeholder(). Even when Pyrefly knows the expression has _PlaceholderType, it treats it as a concrete bound argument and subsequently rejects valid calls.
    Worth fixing if it stays local; otherwise a documented first-cut limitation with bug= tests is reasonable. Don’t substantially complicate contextual inference to cover every expression.

  3. A hole plus **kwargs loses a keyword-collision restriction.

    def f(x: int, y: int, **kwargs: int) -> int:
        return x + y
    
    p = partial(f, Placeholder, 2)
    p(1, x=3)  # Accepted, but runtime gets multiple values for x.

    The distinction matters because an originally positional-only x legitimately permits that same-name keyword through **kwargs.
    Reasonable to defer with a bug= test if fixing it requires new signature machinery. This is an uncommon missed error; don’t broaden the PR into fixing all existing partial/keyword collisions.

  4. Early fallbacks skip keyword-placeholder rejection.
    Examples: partial(f, y=Placeholder, **extra) with an ordinary dictionary splat, and partial(*targets, y=Placeholder). Both are runtime-invalid, but the new validation is bypassed.
    Prefer a small early-validation fix; otherwise track with bug= tests. This does not require precise inference of arbitrary splats.

  5. Trailing placeholders aren’t rejected.
    For example, partial(f, Placeholder, y=2) has a trailing positional placeholder despite the later keyword. The runtime we tested rejects it, while Pyrefly accepts it.
    Already an explicit deferred limit in the PR; fine to document and track rather than block on it.

  6. Some compositions lose precision.
    Generic overloaded targets can yield Unknown; nested partials and ParamSpec cases can fall back to nominal partial[R].
    Fine to retain as explicit inference limits. These are not, by themselves, evidence of incorrect implementation. Use ordinary tests for intentional fallback behavior, and bug= only where the current behavior is considered undesirable.

@stroxler stroxler self-assigned this Nov 10, 2026
@La1itchauhan
La1itchauhan force-pushed the support-functools-placeholder branch from b54fde6 to 7c73e8e Compare November 13, 2026 15:53
@github-actions github-actions Bot added size/m and removed size/m labels Nov 13, 2026
@github-actions

Copy link
Copy Markdown

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@La1itchauhan

Copy link
Copy Markdown
Contributor Author

Hi @stroxler, thanks for the review! I’ve rebased on main, updated the PR description with the checklist, addressed items 1, 2, and 4 with fixes, and added tests for 3, 5 (tracked with bug=), and 6

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

support functools.Placeholder

2 participants