Skip to content

[dynamo] Fix functools.reduce polyfill signature mismatch on Python 3.15#185682

Closed
joaopedroassad wants to merge 2 commits into
pytorch:mainfrom
joaopedroassad:fix-reduce-polyfill-py315
Closed

[dynamo] Fix functools.reduce polyfill signature mismatch on Python 3.15#185682
joaopedroassad wants to merge 2 commits into
pytorch:mainfrom
joaopedroassad:fix-reduce-polyfill-py315

Conversation

@joaopedroassad

@joaopedroassad joaopedroassad commented May 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Python 3.15 will expose an introspectable signature for functools.reduce using a PEP 661 sentinel default: (function, iterable, /, initial=functools._initial_missing). The CPython change is #149591, which merged into CPython main on 2026-05-10, after 3.15.0b1 was tagged. So on 3.15.0b1 the default is still <unrepresentable> and inspect.signature() raises ValueError, which substitute_in_graph swallows and skips the check. From 3.15.0b2 the check will run.

When it runs, it compares positional parameter names, keyword-only names, and default values. The polyfill declares its own local sentinel:

_initial_missing = object()

@substitute_in_graph(functools.reduce)
def reduce(function, iterable, initial=_initial_missing, /):
    ...

That local object() is a different instance from functools._initial_missing, so the defaults dict compares unequal and substitute_in_graph raises:

File ".../torch/_dynamo/polyfills/functools.py", line N, in <module>
  @substitute_in_graph(functools.reduce)
TypeError: Signature mismatch between <built-in function reduce> and <function reduce at 0x...>:
  (function, iterable, /, initial=_initial_missing)
  != (function, iterable, initial=<object object at 0x...>, /)

The polyfills loader imports every polyfill module, so this will break import torch._dynamo entirely on Python 3.15.0b2+. Same failure mode as #185403's struct.pack fix, just one step ahead of the next beta.

The fix imports functools._initial_missing instead of declaring a local one. The polyfill's internal if initial is _initial_missing identity check still works because both sides now reference the same object. No behavior change on any Python version. functools._initial_missing has existed in functools since well before 3.10 (it's used by the pure-Python functools.reduce fallback), so the import is safe on every supported version.

Part of #184352 (Python 3.15 support). Similar to #185403.

Test plan

Verified on a locally-built Python 3.15-dev (CPython main at heads/3.15:863c7e0, which includes #149591). The upstream signature is exposed natively:

$ python3.15 -c "import functools, inspect; print(inspect.signature(functools.reduce))"
(function, iterable, /, initial=_initial_missing)
$ python3.15 -c "import functools, inspect; sig = inspect.signature(functools.reduce); print(sig.parameters['initial'].default is functools._initial_missing)"
True

Extracted the actual substitute_in_graph from torch/_dynamo/decorators.py and ran it against both polyfill versions with no monkey-patching.

Unmodified polyfill (_initial_missing = object() declared locally):

TypeError: Signature mismatch between <built-in function reduce> and <function unmodified_reduce at 0x...>:
  (function, iterable, /, initial=_initial_missing)
  != (function, iterable, initial=<object object at 0x...>, /)

Fixed polyfill (from functools import _initial_missing): no TypeError at the signature check.

Regression on Python 3.12.13 (oldest currently supported):

$ python3 -c "import inspect, functools; inspect.signature(functools.reduce)"
ValueError: no signature found for builtin <built-in function reduce>

The signature check is silently skipped on Python <= 3.14, so the fix is a no-op there. functools._initial_missing is confirmed present on 3.12.13. Eager-vs-polyfill parity verified on every input shape:

reduce(lambda a, b: a+b, [1,2,3,4])       == 10     (matches functools.reduce)
reduce(lambda a, b: a+b, [1,2,3], 100)    == 106    (matches)
reduce(lambda a, b: a+b, [])              -> TypeError("reduce() of empty iterable with no initial value")  (matches)
reduce(lambda a, b: a+b, [], 99)          == 99     (matches)

Pre-commit checks: python3 -m py_compile torch/_dynamo/polyfills/functools.py and git diff --staged --check both clean.

cc @voznesenskym @penguinwu @EikanWang @jgong5 @Guobing-Chen @XiaobingSuper @zhuhaozhe @blzheng @wenzhe-nrv @jiayisunx @kadeng @chauhang @amjames @jataylo @azahed98

@pytorch-bot

pytorch-bot Bot commented May 29, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/185682

Note: Links to docs will display an error until the docs builds have been completed.

⏳ 1 Pending, 2 Unrelated Failures

As of commit 382a377 with merge base 9082cdc (image):

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@linux-foundation-easycla

linux-foundation-easycla Bot commented May 29, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: joaopedroassad / name: jassad095 (eec353c)

@pytorch-bot

pytorch-bot Bot commented May 29, 2026

Copy link
Copy Markdown

Workflows were awaiting approval. CI has now been triggered for the ciflow labels on this PR.

@pytorch-bot

pytorch-bot Bot commented May 29, 2026

Copy link
Copy Markdown

The following ciflow label(s) have been added but CI has not been triggered yet because the workflows are awaiting approval:

  • ciflow/inductor
  • ciflow/torchtitan

Once a maintainer approves the workflows (scroll to the bottom of the PR page), the corresponding CI jobs will be triggered automatically. Please ping one of the reviewers if you do not have access to approve and run workflows.

@jansel

jansel commented May 30, 2026

Copy link
Copy Markdown
Contributor

@claude explain those CI failures, check if they are related, and review the changes in this PR

@claude

claude Bot commented May 30, 2026

Copy link
Copy Markdown

Claude finished @jansel's task in 2m 4s —— View job


CI Failures Analysis & Code Review

  • Investigate CI failures and determine if they're related to this PR
  • Review the code changes
  • Post findings

CI Failures

1. NEW FAILURE: test_compiled_dtensor_op_db_linalg_tensorsolve_cpu_float32

This test failed with "Unexpected success" — meaning the test is marked as XFAIL (expected failure) but it now passes. This is a pre-existing test expectation that needs updating in trunk; it has nothing to do with this PR's changes to functools.reduce polyfill. The test is in test/distributed/tensor/test_dtensor_ops.py and tests linalg.tensorsolve under compiled DTensor ops — completely unrelated to functools polyfills.

2. BROKEN TRUNK: inductor-test / test-osdc (inductor, 2, 2, ...)

The job was interrupted mid-execution (tests were passing at ~57% completion) when the container was forcibly terminated. The log shows "Executing the custom container implementation failed" which is a self-hosted runner infrastructure issue. Dr. CI confirms this same failure exists on the merge base commit (ed4692b), so it's a pre-existing infrastructure problem.

Neither failure is related to this PR.


Code Review

The change is minimal and correct:

+from functools import _initial_missing  # type: ignore[attr-defined]
...
-_initial_missing = object()

Correctness: The fix imports functools._initial_missing (the actual sentinel CPython uses internally) instead of creating a local object() instance. This ensures the polyfill's default value is identity-equal (is) to the one exposed by inspect.signature(functools.reduce) on Python 3.15.0b2+. The existing if initial is _initial_missing check on line 31 continues to work because both sides now reference the same object.

Backwards compatibility: functools._initial_missing has existed in CPython's functools module since at least Python 3.10 (used by the pure-Python functools.reduce fallback), and CI here confirms it works on 3.10. On Python <= 3.14, inspect.signature(functools.reduce) raises ValueError, so substitute_in_graph skips the signature check entirely — this fix is a no-op there.

The # type: ignore[attr-defined] annotation is appropriate since _initial_missing is a private/undocumented attribute that type checkers don't know about.

No issues found. This is a clean, minimal, well-motivated fix. LGTM.


@jansel

jansel commented May 30, 2026

Copy link
Copy Markdown
Contributor

@pytorchbot rebase

@pytorchmergebot

Copy link
Copy Markdown
Collaborator

@pytorchbot started a rebase job onto refs/remotes/origin/viable/strict. Check the current status here

@pytorchmergebot

Copy link
Copy Markdown
Collaborator

Tried to rebase and push PR #185682, but it was already up to date. Try rebasing against main by issuing:
@pytorchbot rebase -b main

@ezyang

ezyang commented May 30, 2026

Copy link
Copy Markdown
Contributor

@pytorchbot merge -i

@pytorch-bot pytorch-bot Bot added the ciflow/trunk Trigger trunk jobs on your pull request label May 30, 2026
@pytorchmergebot

Copy link
Copy Markdown
Collaborator

Merge started

Your change will be merged while ignoring the following 2 checks: pull / linux-jammy-py3.10-gcc11 / test-osdc (distributed, 1, 3, mt-l-x86iamx-8-64), inductor / unit-test / inductor-test / test-osdc (inductor, 2, 2, mt-l-x86aavx2-29-113-a10g)

Learn more about merging in the wiki.

Questions? Feedback? Please reach out to the PyTorch DevX Team

Advanced Debugging
Check the merge workflow status
here

@pytorchmergebot

Copy link
Copy Markdown
Collaborator

Merge failed

Reason: 1 mandatory check(s) failed. The first few are:

Dig deeper by viewing the failures on hud

Details for Dev Infra team Raised by workflow job

Failing merge rule: Core Maintainers

@joaopedroassad

Copy link
Copy Markdown
Contributor Author

@pytorchbot rebase -b main

@pytorch-bot

pytorch-bot Bot commented May 31, 2026

Copy link
Copy Markdown

You don't have permissions to rebase this PR since you are a first time contributor. If you think this is a mistake, please contact PyTorch Dev Infra.

Clean merge with origin/main; no conflicts.
@ezyang

ezyang commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

@pytorchbot merge

@pytorchmergebot

Copy link
Copy Markdown
Collaborator

Merge started

Your change will be merged once all checks pass (ETA 0-4 Hours).

Learn more about merging in the wiki.

Questions? Feedback? Please reach out to the PyTorch DevX Team

Advanced Debugging
Check the merge workflow status
here

@ezyang

ezyang commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

mergedog handoff

All CI is green (or skipped). Ready for human review and @pytorchbot merge.

Current PR head: 382a377e8e558d1510ec0440d7057ba568df900a.
No mergedog-authored commits were pushed during this run.

CI notes at handoff

mergedog is treating these still-failing checks as unrelated/spurious. Please verify before merging:

  • lintrunner-noclang-all / lint
  • linux-jammy-py3.10-gcc11 / test-osdc (distributed, 1, 3, mt-l-x86iamx-8-64)
  • torchtitan-x-pytorch-test / test-osdc (torchtitan_features_integration, 1, 1, mt-l-x86aavx2-189-704-a10g-8)
  • torchtitan-x-pytorch-test / test-osdc (torchtitan_models_integration, 1, 1, mt-l-x86aavx2-189-704-a10g-8)
  • unit-test / inductor-test / test-osdc (inductor, 2, 2, mt-l-x86aavx2-29-113-a10g)
  • win-vs2022-cpu-py3 / build
  • win-vs2022-cuda13.0-py3 / build

Warning: mergedog's suppressed check list differs from the latest Dr. CI summary.

Suppressed by mergedog but not listed by Dr. CI:

  • lintrunner-noclang-all / lint
  • linux-jammy-py3.10-gcc11 / test-osdc (distributed, 1, 3, mt-l-x86iamx-8-64)
  • unit-test / inductor-test / test-osdc (inductor, 2, 2, mt-l-x86aavx2-29-113-a10g)
  • win-vs2022-cpu-py3 / build
  • win-vs2022-cuda13.0-py3 / build
Latest Dr. CI summary
<!-- drci-comment-start -->

## :link: Helpful Links
### :test_tube: See artifacts and rendered test results at [hud.pytorch.org/pr/185682](https://hud.pytorch.org/pr/185682)
* :page_facing_up: Preview [Python docs built from this PR](https://docs-preview.pytorch.org/pytorch/pytorch/185682/index.html)
* :page_facing_up: Preview [C++ docs built from this PR](https://docs-preview.pytorch.org/pytorch/pytorch/185682/cppdocs/index.html)
* :question: Need help or want to give feedback on the CI? Visit the [bot commands wiki](https://github.com/pytorch/pytorch/wiki/Bot-commands)

Note: Links to docs will display an error until the docs builds have been completed.


## :hourglass_flowing_sand: 1 Pending, 2 Unrelated Failures
As of commit 382a377e8e558d1510ec0440d7057ba568df900a with merge base 9082cdc0be87044a0debd91ef1c67a340dace78f (<sub><sub><img alt="image" width=70 src="https://img.shields.io/date/1780252558?label=&color=FFFFFF&style=flat-square"></sub></sub>):
<details ><summary><b>BROKEN TRUNK</b> - The following jobs failed but were present on the merge base:</summary><p>👉 <b>Rebase onto the `viable/strict` branch to avoid these failures</b></p><p>

* [torchtitan-test / torchtitan-x-pytorch-test / test-osdc (torchtitan_features_integration, 1, 1, mt-l-x86aavx2-189-704-a10g-8)](https://hud.pytorch.org/pr/pytorch/pytorch/185682#78777184211) ([gh](https://github.com/pytorch/pytorch/actions/runs/26731570695/job/78777184211)) ([trunk failure](https://hud.pytorch.org/pytorch/pytorch/commit/9082cdc0be87044a0debd91ef1c67a340dace78f#78748449674))
    `[OSDC] Step script exited with code 1. This is a script/workflow error, not an infrastructure issue. Check the step logs above for the actual failure.`
* [torchtitan-test / torchtitan-x-pytorch-test / test-osdc (torchtitan_models_integration, 1, 1, mt-l-x86aavx2-189-704-a10g-8)](https://hud.pytorch.org/pr/pytorch/pytorch/185682#78777184200) ([gh](https://github.com/pytorch/pytorch/actions/runs/26731570695/job/78777184200)) ([trunk failure](https://hud.pytorch.org/pytorch/pytorch/commit/9082cdc0be87044a0debd91ef1c67a340dace78f#78748449677))
    `[OSDC] Step script exited with code 1. This is a script/workflow error, not an infrastructure issue. Check the step logs above for the actual failure.`
</p></details>


This comment was automatically generated by Dr. CI and updates every 15 minutes.
<!-- drci-comment-end -->

No LLM was invoked during this run.

@ezyang

ezyang commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/185682

Note: Links to docs will display an error until the docs builds have been completed.

✅ You can merge normally! (2 Unrelated Failures)

As of commit 382a377 with merge base 9082cdc (image):

BROKEN TRUNK - The following jobs failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

This comment was automatically generated by Dr. CI and updates every 15 minutes.

khushi-411 pushed a commit to khushi-411/pytorch that referenced this pull request Jun 1, 2026
….15 (pytorch#185682)

## Summary

Python 3.15 will expose an introspectable signature for `functools.reduce` using a PEP 661 sentinel default: `(function, iterable, /, initial=functools._initial_missing)`. The CPython change is [pytorch#149591](python/cpython#149591), which merged into CPython main on 2026-05-10, after 3.15.0b1 was tagged. So on 3.15.0b1 the default is still `<unrepresentable>` and `inspect.signature()` raises `ValueError`, which `substitute_in_graph` swallows and skips the check. From 3.15.0b2 the check will run.

When it runs, it compares positional parameter names, keyword-only names, and default values. The polyfill declares its own local sentinel:

```python
_initial_missing = object()

@substitute_in_graph(functools.reduce)
def reduce(function, iterable, initial=_initial_missing, /):
    ...
```

That local `object()` is a different instance from `functools._initial_missing`, so the defaults dict compares unequal and `substitute_in_graph` raises:

```
File ".../torch/_dynamo/polyfills/functools.py", line N, in <module>
  @substitute_in_graph(functools.reduce)
TypeError: Signature mismatch between <built-in function reduce> and <function reduce at 0x...>:
  (function, iterable, /, initial=_initial_missing)
  != (function, iterable, initial=<object object at 0x...>, /)
```

The polyfills loader imports every polyfill module, so this will break `import torch._dynamo` entirely on Python 3.15.0b2+. Same failure mode as pytorch#185403's `struct.pack` fix, just one step ahead of the next beta.

The fix imports `functools._initial_missing` instead of declaring a local one. The polyfill's internal `if initial is _initial_missing` identity check still works because both sides now reference the same object. No behavior change on any Python version. `functools._initial_missing` has existed in `functools` since well before 3.10 (it's used by the pure-Python `functools.reduce` fallback), so the import is safe on every supported version.

Part of pytorch#184352 (Python 3.15 support). Similar to pytorch#185403.

## Test plan

Verified on a locally-built Python 3.15-dev (CPython main at `heads/3.15:863c7e0`, which includes [pytorch#149591](python/cpython#149591)). The upstream signature is exposed natively:

```
$ python3.15 -c "import functools, inspect; print(inspect.signature(functools.reduce))"
(function, iterable, /, initial=_initial_missing)
$ python3.15 -c "import functools, inspect; sig = inspect.signature(functools.reduce); print(sig.parameters['initial'].default is functools._initial_missing)"
True
```

Extracted the actual `substitute_in_graph` from `torch/_dynamo/decorators.py` and ran it against both polyfill versions with no monkey-patching.

Unmodified polyfill (`_initial_missing = object()` declared locally):

```
TypeError: Signature mismatch between <built-in function reduce> and <function unmodified_reduce at 0x...>:
  (function, iterable, /, initial=_initial_missing)
  != (function, iterable, initial=<object object at 0x...>, /)
```

Fixed polyfill (`from functools import _initial_missing`): no `TypeError` at the signature check.

Regression on Python 3.12.13 (oldest currently supported):

```
$ python3 -c "import inspect, functools; inspect.signature(functools.reduce)"
ValueError: no signature found for builtin <built-in function reduce>
```

The signature check is silently skipped on Python <= 3.14, so the fix is a no-op there. `functools._initial_missing` is confirmed present on 3.12.13. Eager-vs-polyfill parity verified on every input shape:

```
reduce(lambda a, b: a+b, [1,2,3,4])       == 10     (matches functools.reduce)
reduce(lambda a, b: a+b, [1,2,3], 100)    == 106    (matches)
reduce(lambda a, b: a+b, [])              -> TypeError("reduce() of empty iterable with no initial value")  (matches)
reduce(lambda a, b: a+b, [], 99)          == 99     (matches)
```

Pre-commit checks: `python3 -m py_compile torch/_dynamo/polyfills/functools.py` and `git diff --staged --check` both clean.

Pull Request resolved: pytorch#185682
Approved by: https://github.com/ezyang

Co-authored-by: Edward Z. Yang via mergedog <ezyang@meta.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/inductor ciflow/torchtitan Run TorchTitan integration tests ciflow/trunk Trigger trunk jobs on your pull request Merged module: dynamo open source topic: not user facing topic category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants