Title: Assertion failure from `functools.partial` using a `str` subclass that raises on `__eq__` · Issue #146075 · python/cpython · GitHub
Open Graph Title: Assertion failure from `functools.partial` using a `str` subclass that raises on `__eq__` · Issue #146075 · python/cpython
X Title: Assertion failure from `functools.partial` using a `str` subclass that raises on `__eq__` · Issue #146075 · python/cpython
Description: Crash report What happened? It's possible to make the interpreter abort by passing an evil str subclass that raises on __eq__ to be used as a keyword by partial. Automated diagnosis: Bug: PyDict_Contains -1 treated as truthy in partial._...
Open Graph Description: Crash report What happened? It's possible to make the interpreter abort by passing an evil str subclass that raises on __eq__ to be used as a keyword by partial. Automated diagnosis: Bug: PyDict_Co...
X Description: Crash report What happened? It's possible to make the interpreter abort by passing an evil str subclass that raises on __eq__ to be used as a keyword by partial. Automated diagnosis: Bug: PyDic...
Opengraph URL: https://github.com/python/cpython/issues/146075
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Assertion failure from `functools.partial` using a `str` subclass that raises on `__eq__`","articleBody":"# Crash report\n\n### What happened?\n\nIt's possible to make the interpreter abort by passing an evil `str` subclass that raises on `__eq__` to be used as a keyword by `partial`.\n\n### Automated diagnosis:\n\nBug: `PyDict_Contains` -1 treated as truthy in `partial.__call__` vectorcall. When a keyword key's `__eq__` or `__hash__` raises during the `PyDict_Contains` check, the error return (-1) is treated as \"found\" (truthy), silently swallowing the exception and taking the wrong code path for keyword merging.\n\nFile: `Modules/_functoolsmodule.c`, line 455\n\nMRE:\n```python\nfrom functools import partial\n\nclass BadStr(str):\n def __eq__(self, other):\n raise RuntimeError\n def __hash__(self):\n return str.__hash__(self)\n\ndef f(**kwargs):\n return kwargs\n\np = partial(f, poison=\"\")\nresult = p(**{BadStr(\"poison\"): \"new_value\"})\n```\n\nBacktrace:\n```\npython: Python/generated_cases.c.h:12739: PyObject *_PyEval_EvalFrameDefault(PyThreadState *, _PyInterpreterFrame *, int): Assertion `!_PyErr_Occurred(tstate)' failed.\n\nProgram received signal SIGABRT, Aborted.\n\n#0 __pthread_kill_implementation (threadid=\u003coptimized out\u003e, signo=6, no_tid=0) at ./nptl/pthread_kill.c:44\n#1 __pthread_kill_internal (threadid=\u003coptimized out\u003e, signo=6) at ./nptl/pthread_kill.c:89\n#2 __GI___pthread_kill (threadid=\u003coptimized out\u003e, signo=signo@entry=6) at ./nptl/pthread_kill.c:100\n#3 0x00007ffff7c45e2e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26\n#4 0x00007ffff7c28888 in __GI_abort () at ./stdlib/abort.c:77\n#5 0x00007ffff7c287f0 in __assert_fail_base (fmt=\u003coptimized out\u003e, assertion=\u003coptimized out\u003e, file=\u003coptimized out\u003e, line=\u003coptimized out\u003e, function=\u003coptimized out\u003e) at ./assert/assert.c:118\n#6 0x00007ffff7c3c19f in __assert_fail (assertion=\u003coptimized out\u003e, file=\u003coptimized out\u003e, line=\u003coptimized out\u003e, function=\u003coptimized out\u003e) at ./assert/assert.c:127\n#7 0x0000555555e5d037 in _PyEval_EvalFrameDefault (tstate=\u003coptimized out\u003e, frame=\u003coptimized out\u003e, throwflag=\u003coptimized out\u003e) at Python/generated_cases.c.h:12739\n#8 0x0000555555e57778 in _PyEval_EvalFrame (tstate=0x5555568f7b18 \u003c_PyRuntime+360664\u003e, frame=0x7e8ff6fe52a8, throwflag=0) at ./Include/internal/pycore_ceval.h:118\n#9 _PyEval_Vector (tstate=\u003coptimized out\u003e, func=\u003coptimized out\u003e, locals=\u003coptimized out\u003e, args=\u003coptimized out\u003e, argcount=\u003coptimized out\u003e, kwnames=0x0) at Python/ceval.c:2134\n#10 0x0000555555ab9e00 in _PyObject_VectorcallTstate (tstate=tstate@entry=0x5555568f7b18 \u003c_PyRuntime+360664\u003e, callable=0x7cfff703cf60, args=args@entry=0x7bfff5b7e4a8,\n nargsf=nargsf@entry=9223372036854775809, kwnames=kwnames@entry=0x0) at ./Include/internal/pycore_call.h:136\n#11 0x0000555555abcda5 in PyObject_CallOneArg (func=\u003coptimized out\u003e, arg=0x7d3ff7026870) at Objects/call.c:395\n#12 0x0000555555c6fb74 in call_unbound_noarg (unbound=1, func=0x7cfff703cf60, self=0x7d3ff7026870) at Objects/typeobject.c:3033\n#13 maybe_call_special_no_args (self=\u003coptimized out\u003e, attr=\u003coptimized out\u003e, attr_is_none=\u003coptimized out\u003e) at Objects/typeobject.c:3146\n#14 0x0000555555caaeb6 in slot_tp_hash (self=\u003coptimized out\u003e) at Objects/typeobject.c:10705\n#15 0x0000555555b97cd7 in _PyObject_HashFast (op=0x7d3ff7026870) at ./Include/internal/pycore_object.h:849\n#16 setitem_take2_lock_held (mp=0x7c7ff70decc0, key=0x7d3ff7026870, value=0x7c6ff70eb5a0) at Objects/dictobject.c:2737\n#17 0x000055555625d598 in partial_vectorcall (self=\u003coptimized out\u003e, args=\u003coptimized out\u003e, nargsf=\u003coptimized out\u003e, kwnames=\u003coptimized out\u003e) at ./Modules/_functoolsmodule.c:467\n#18 0x0000555555abc520 in _PyVectorcall_Call (tstate=\u003coptimized out\u003e, func=\u003coptimized out\u003e, callable=\u003coptimized out\u003e, tuple=0x5555568c0b50 \u003c_PyRuntime+135440\u003e, kwargs=\u003coptimized out\u003e)\n at Objects/call.c:285\n#19 0x0000555555e904e2 in _PyEval_EvalFrameDefault (tstate=\u003coptimized out\u003e, frame=\u003coptimized out\u003e, throwflag=\u003coptimized out\u003e) at Python/generated_cases.c.h:2937\n#20 0x0000555555e57778 in _PyEval_EvalFrame (tstate=0x5555568f7b18 \u003c_PyRuntime+360664\u003e, frame=0x7e8ff6fe5220, throwflag=0) at ./Include/internal/pycore_ceval.h:118\n#21 _PyEval_Vector (tstate=\u003coptimized out\u003e, func=\u003coptimized out\u003e, locals=\u003coptimized out\u003e, args=\u003coptimized out\u003e, argcount=\u003coptimized out\u003e, kwnames=0x0) at Python/ceval.c:2134\n#22 0x0000555555e57195 in PyEval_EvalCode (co=\u003coptimized out\u003e, globals=\u003coptimized out\u003e, locals=0x7c7ff70884c0) at Python/ceval.c:681\n#23 0x0000555556061fb0 in run_eval_code_obj (tstate=tstate@entry=0x5555568f7b18 \u003c_PyRuntime+360664\u003e, co=co@entry=0x7d2ff6ffdfd0, globals=globals@entry=0x7c7ff70884c0,\n locals=locals@entry=0x7c7ff70884c0) at Python/pythonrun.c:1368\n#24 0x000055555606117c in run_mod (mod=\u003coptimized out\u003e, filename=\u003coptimized out\u003e, globals=\u003coptimized out\u003e, locals=\u003coptimized out\u003e, flags=\u003coptimized out\u003e, arena=\u003coptimized out\u003e,\n interactive_src=\u003coptimized out\u003e, generate_new_source=\u003coptimized out\u003e) at Python/pythonrun.c:1471\n```\n\nFound using [cpython-review-toolkit](https://github.com/devdanzin/cpython-review-toolkit) with Claude Opus 4.6, using the `/cpython-review-toolkit:explore Modules/_functoolsmodule.c all deep` command.\n\n\n### CPython versions tested on:\n\nCPython main branch\n\n### Operating systems tested on:\n\nLinux\n\n### Output from running 'python -VV' on the command line:\n\nPython 3.15.0a7+ (heads/main:99e2c5eccd2, Mar 17 2026, 08:26:50) [Clang 21.1.2 (2ubuntu6)]\n\n\u003c!-- gh-linked-prs --\u003e\n### Linked PRs\n* gh-146078\n\u003c!-- /gh-linked-prs --\u003e\n","author":{"url":"https://github.com/devdanzin","@type":"Person","name":"devdanzin"},"datePublished":"2026-03-17T18:14:07.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":3},"url":"https://github.com/146075/cpython/issues/146075"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:23c0eca8-388f-6059-b337-fa06ace778e1 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | CE4A:381ACD:4D352C0:6BFD111:6A50EF59 |
| html-safe-nonce | 8204294232b0e6e40f4ecca4a3157bc7d62dfaaa762caa1eca0a381214f18a6a |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDRTRBOjM4MUFDRDo0RDM1MkMwOjZCRkQxMTE6NkE1MEVGNTkiLCJ2aXNpdG9yX2lkIjoiMzU1NTY5OTg4NzM2NTQ4NDM3OCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | d7cd9d2334350d848a3b6eb5f03c37e2ab7ff575fa6fb5e03ff1eda4ba6a973b |
| hovercard-subject-tag | issue:4090312737 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/python/cpython/146075/issue_layout |
| twitter:image | https://opengraph.githubassets.com/54f169694303c73851e011a409c4c273e4509eb9a810832da8bae9e8cb02b178/python/cpython/issues/146075 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/54f169694303c73851e011a409c4c273e4509eb9a810832da8bae9e8cb02b178/python/cpython/issues/146075 |
| og:image:alt | Crash report What happened? It's possible to make the interpreter abort by passing an evil str subclass that raises on __eq__ to be used as a keyword by partial. Automated diagnosis: Bug: PyDict_Co... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | devdanzin |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5266e58c17a510c403505cc811606465e90a881d2007ee7df1c4800d5c659838 |
| turbo-cache-control | no-preview |
| go-import | github.com/python/cpython git https://github.com/python/cpython.git |
| octolytics-dimension-user_id | 1525981 |
| octolytics-dimension-user_login | python |
| octolytics-dimension-repository_id | 81598961 |
| octolytics-dimension-repository_nwo | python/cpython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 81598961 |
| octolytics-dimension-repository_network_root_nwo | python/cpython |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 5ec60191a48933536a90c8a19f47142fcd0d4739 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width