mypyc silently drops class keyword arguments
还没有人认领这个 Issue。
评估
调研方向
在解释型 Python 和 mypyc 下运行提供的 repro.py 示例,然后阅读 mypyc/irbuild/classdef.py 中的 ClassBuilder.finalize 和 mypyc/primitives/misc_ops.py 中的 py_init_subclass_op。将这些路径与非原生的 builder.py_call 路径进行比较,并检查 mypy/nodes.py 和 mypy/fastparse.py 中的 ClassDef.keywords。完成的标准是:类关键字在两种情况下都得到保留,或者不支持的情况会被拒绝,而不是被静默编译。
由索引模型根据 Issue 内容生成。
描述
Bug Report
Keyword arguments given in a class definition line are not forwarded when the
module is compiled. __init_subclass__ is called with no keywords, and a
metaclass is called with none either. Nothing is reported — neither by mypyc
during compilation nor by mypy when type checking.
This is not a case of an unsupported construct being rejected: __init_subclass__
is supported (there is a dedicated py_init_subclass_op in
mypyc/primitives/misc_ops.py), and the docs state that a class using an
unsupported metaclass "is compiled into a regular Python class (non-native
class)". The class is created and the hook does run — only the keywords are
missing, so the resulting behaviour still differs from interpreted Python.
To Reproduce
# repro.py
from typing import Any
class Base:
def __init_subclass__(cls, **kwargs: Any) -> None:
print("__init_subclass__ received:", kwargs)
class Child(Base, marker=1):
pass
$ python -c "import repro"
__init_subclass__ received: {'marker': 1}
$ python -m mypyc repro.py
$ python -c "import repro"
__init_subclass__ received: {}
$ mypy --strict repro.py
Success: no issues found in 1 source file
Expected Behavior
__init_subclass__ (and a metaclass) receives {'marker': 1} in both cases,
as it does in interpreted Python. If forwarding class keywords is out of scope,
mypyc should reject the class rather than compile it and drop them silently.
Actual Behavior
The keywords are dropped. Compilation produces no diagnostic, and the resulting
extension module behaves differently from the same source under the interpreter.
Where this comes from
ClassDef.keywords is populated by the parser (mypy/nodes.py,
mypy/fastparse.py) and used by the checker, but mypyc never reads it —
searching the mypyc package for .keywords returns nothing. The two paths
that create the class both pass positional arguments only:
- native classes —
ClassBuilder.finalizeinmypyc/irbuild/classdef.pycalls
py_init_subclass_opwith[self.type_obj], and the primitive itself is
declared witharg_types=[object_rprimitive], so there is no place for
keywords to go; - non-native classes — the metaclass is invoked as
builder.py_call(non_ext.metaclass, [cls_name, non_ext.bases, non_ext.dict]).
I could not find this limitation mentioned in the mypyc documentation. The
"Native classes" page documents which metaclasses are supported and what
happens to classes that use others, but says nothing about class keywords.
Real-world impact
Libraries that configure classes through class keywords lose that
configuration. With msgspec, whose Struct takes its options this way:
# tags.py
import msgspec
class Fact(msgspec.Struct, frozen=True, tag=True):
call_id: str
seq: int
$ python -c "import tags; c = tags.Fact.__struct_config__; print(c.tag, c.frozen)"
Fact True
$ python -m mypyc tags.py
$ python -c "import tags; c = tags.Fact.__struct_config__; print(c.tag, c.frozen)"
None False
$ python -c "
import tags
f = tags.Fact(call_id='c', seq=1)
f.call_id = 'mutated'
print(f)"
Fact(call_id='mutated', seq=1)
frozen=True becomes frozen=False, so instances meant to be immutable can be
mutated. tag=True is lost, so tagged unions stop decoding:
TypeError: If a type union contains multiple Struct types, all Struct types
must be tagged (via `tag` or `tag_field` kwarg)
The tagged-union failure is at least loud. The loss of frozen is not: the code
compiles, passes mypy --strict, and the immutability guarantee simply
disappears at runtime.
@mypy_extensions.mypyc_attr(native_class=False) does not help — the keywords
are dropped either way. Building the struct through a runtime call
(msgspec.defstruct(..., frozen=True, tag=True)) does preserve them, but gives
up static field types.
Your Environment
- Mypy version used: 2.3.1, and 2.4.0+dev.543dbceea27fc9cca25d8adaaa0b36a77c3e1de8 (same behaviour on both)
- Mypy command-line flags:
python -m mypyc repro.py; type checking withmypy --strict repro.py - Mypy configuration options from
mypy.ini(and other config files): none - Python version used: 3.14.7, reproduced on both the default build and the free-threaded build (
3.14.7t) - Operating system and version: Linux x86_64, GCC 16.2.1
- msgspec version (for the second example): 0.21.1
- 主要语言
- Python
- 星标
- 20.6k
- 派生
- 3.3k
- 平均合并
- 1 天 18 小时
- 30 天内合并 PR
- 54
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
python/mypy 的其他 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 75/100
-
bug
难度 2/5 1-3 小时 新手友好度 78/100
-
bug
难度 2/5 1-3 小时 新手友好度 76/100
-
documentation
难度 2/5 1-3 小时 新手友好度 72/100
-
bug topic-configuration topic-error-reporting
难度 2/5 1-3 小时 新手友好度 68/100
相似的 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 86/100
zostera/django-bootstrap4#894 ·
-
难度 2/5 1-3 小时 新手友好度 78/100
use-agent-os/agent-os#3276 ·
-
难度 2/5 1-3 小时 新手友好度 88/100
zephyrproject-rtos/zephyr#119726 ·
-
area/auth bug comp/agent P3 platform/discord type/security
难度 2/5 1-3 小时 新手友好度 88/100
NousResearch/hermes-agent#117848 ·
-
难度 2/5 1-3 小时 新手友好度 82/100
zilliztech/memsearch#759 ·