fix Support functools.partial #3175#3178
fix Support functools.partial #3175#3178asukaminato0721 wants to merge 3 commits intofacebook:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds concrete signature support for functools.partial so Pyrefly can (a) validate bound arguments at partial-construction time and (b) type the resulting value as a callable with the remaining (unbound) parameters.
Changes:
- Extend constructor-call inference to detect
functools.partial(...)and rewrite its resulting type to aCallablewith the remaining parameter list. - Validate bound positional/keyword arguments against the target callable signature and emit arity/type errors early.
- Add tests covering remaining-signature preservation and rejection of over-binding.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
pyrefly/lib/alt/call.rs |
Implements functools.partial special-casing in constructor calls: extracts a concrete callable signature, binds arguments, and returns a rewritten callable type. |
pyrefly/lib/test/callable.rs |
Adds regression tests to ensure partial preserves remaining arity/types and errors on too many bound arguments. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| self.error( | ||
| errors, | ||
| kw.range(), | ||
| ErrorInfo::Kind(ErrorKind::BadArgumentCount), |
There was a problem hiding this comment.
The error kind for an unexpected keyword in partial(...) is reported as BadArgumentCount, but elsewhere call-site keyword issues use ErrorKind::UnexpectedKeyword (see pyrefly/lib/alt/callable.rs where the message "Unexpected keyword argument ..." is emitted with UnexpectedKeyword). Using BadArgumentCount here makes the diagnostic category/suppression code inconsistent. Switch this branch to emit ErrorKind::UnexpectedKeyword (and keep the message as-is).
| ErrorInfo::Kind(ErrorKind::BadArgumentCount), | |
| ErrorInfo::Kind(ErrorKind::UnexpectedKeyword), |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Diff from mypy_primer, showing the effect of this PR on open source code: jax (https://github.com/google/jax)
+ ERROR jax/experimental/pallas/ops/tpu/splash_attention/splash_attention_kernel.py:2576:7-11: Argument `Array | MultiHeadMask` is not assignable to parameter `mask` with type `Array` [bad-argument-type]
+ ERROR jax/experimental/pallas/ops/tpu/splash_attention/splash_attention_kernel.py:2592:11-15: Argument `Array | MultiHeadMask` is not assignable to parameter `mask` with type `Array` [bad-argument-type]
+ ERROR jax/experimental/pallas/ops/tpu/splash_attention/splash_attention_kernel.py:2593:11-26: Argument `tuple[int | Unknown | None, int | Unknown | None]` is not assignable to parameter `block_shape` with type `tuple[int, int]` [bad-argument-type]
+ ERROR jax/experimental/pallas/ops/tpu/splash_attention/splash_attention_kernel.py:2602:9-13: Argument `Array | MultiHeadMask` is not assignable to parameter `mask` with type `Array` [bad-argument-type]
+ ERROR jax/experimental/pallas/ops/tpu/splash_attention/splash_attention_kernel.py:2603:9-26: Argument `tuple[int | Unknown | None, int | Unknown | None]` is not assignable to parameter `block_shape` with type `tuple[int, int]` [bad-argument-type]
scipy (https://github.com/scipy/scipy)
- ERROR scipy/fft/_duccfft/basic.py:35:1-13: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:37:1-14: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:65:1-14: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:67:1-15: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:99:1-14: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:101:1-15: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:153:1-14: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:155:1-15: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:181:1-15: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:183:1-16: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:222:1-15: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:224:1-16: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:249:1-22: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/basic.py:251:1-23: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/realtransforms.py:49:1-13: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/realtransforms.py:51:1-14: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/realtransforms.py:54:1-13: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/realtransforms.py:56:1-14: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/realtransforms.py:102:1-14: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/realtransforms.py:104:1-15: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/realtransforms.py:107:1-14: Object of class `partial` has no attribute `__name__` [missing-attribute]
- ERROR scipy/fft/_duccfft/realtransforms.py:109:1-15: Object of class `partial` has no attribute `__name__` [missing-attribute]
pytest-autoprofile (https://gitlab.com/TTsangSC/pytest-autoprofile)
+ ERROR tests/test_doctest.py:184:30-35: Argument `CheckPytestConfig | Unknown` is not assignable to parameter `*args` with type `str` [bad-argument-type]
hydra-zen (https://github.com/mit-ll-responsible-ai/hydra-zen)
- INFO tests/annotations/declarations.py:160:16-73: revealed type: Just[partial[int]] [reveal-type]
+ INFO tests/annotations/declarations.py:160:16-73: revealed type: Just[(() -> int) & partial[int]] [reveal-type]
- INFO tests/annotations/declarations.py:166:16-58: revealed type: partial[int] [reveal-type]
+ INFO tests/annotations/declarations.py:166:16-58: revealed type: (() -> int) & partial[int] [reveal-type]
- ERROR tests/annotations/declarations.py:471:23-33: `partial[int]` is not assignable to `Partial[int]` [bad-assignment]
+ ERROR tests/annotations/declarations.py:471:23-33: `(() -> int) & partial[int]` is not assignable to `Partial[int]` [bad-assignment]
- ERROR tests/annotations/declarations.py:472:24-41: `partial[bool]` is not assignable to `Partial[bool]` [bad-assignment]
+ ERROR tests/annotations/declarations.py:472:24-41: `((*, x: str = ...) -> bool) & partial[bool]` is not assignable to `Partial[bool]` [bad-assignment]
spack (https://github.com/spack/spack)
- ERROR lib/spack/spack/solver/reuse.py:38:31-38: Argument `partial[list[Spec] | None]` is not assignable to parameter `factory` with type `() -> list[Spec]` in function `spack.spec_filter.SpecFilter.__init__` [bad-argument-type]
+ ERROR lib/spack/spack/solver/reuse.py:38:31-38: Argument `((*, configuration: Unknown = ...) -> list[Spec] | None) & partial[list[Spec] | None]` is not assignable to parameter `factory` with type `() -> list[Spec]` in function `spack.spec_filter.SpecFilter.__init__` [bad-argument-type]
prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/prefect/task_engine.py:639:9-35: Object of class `partial` has no attribute `log_on_run` [missing-attribute]
- ERROR src/prefect/task_engine.py:1263:9-35: Object of class `partial` has no attribute `log_on_run` [missing-attribute]
|
Primer Diff Classification✅ 4 improvement(s) | ➖ 2 neutral | 6 project(s) total | +11, -29 errors 4 improvement(s) across jax, scipy, pytest-autoprofile, prefect.
Detailed analysis✅ Improvement (4)jax (+5)
These are real type-level issues that pyrefly (and mypy/pyright) correctly identify. The PR's
scipy (-22)
pytest-autoprofile (+1)
prefect (-2)
➖ Neutral (2)hydra-zen (+4, -4)
spack (+1, -1)
Was this helpful? React with 👍 or 👎 Classification by primer-classifier (2 heuristic, 4 LLM) |
Summary
Fixes #3175
Implemented functools.partial support for concrete callable signatures.
When Pyrefly sees functools.partial applied to a normal list-parameter callable, it now rewrites the result to the remaining callable signature and validates the bound arguments up front.
cases like partial(f, 1) behave like a one-argument callable and over-applied constructors like partial(f, 1, "a", 2, "b", 3, "c", 4, "d") report an arity error.
Test Plan
add test