Skip to main content

1704. gopy object / type / class-callable full-file ports

Rule

Every CPython source file in scope is ported in full. No function in those files may be left unported. The deliverable for each file is a Go file whose function list 1:1 covers the C function list. Once this spec lands we never come back to these files for a missing slot.

Files in scope

#CPython fileLinesgopy targetStatus
AObjects/object.c~2,600objects/object.go + objects/object_proto.gopartial
BObjects/typeobject.c~12,000objects/type.go + objects/usertype.go + objects/type_call.go + objects/type_inherit.gopartial
CObjects/classobject.c~620objects/method.go (BoundMethod block)partial
DObjects/funcobject.c~1,260objects/function.go + objects/method.go (classmethod, staticmethod blocks)partial
EPython/ceval.c name ops~80vm/eval_simple.go (storeIn / lookupIn / deleteIn)done
FInclude/cpython/classobject.h30objects/method.go struct decldone
GInclude/cpython/funcobject.hvariousobjects/function.go + objects/method.go struct declsdone

Sources of truth live under /Users/apple/cpython-314/.

Phase index

Each phase ports one file (or one disjoint block of a file) end to end. Phases are independent unless the Blocks column says otherwise. Final gate closes #544.

PhaseFileBlockBlocksStatus
1A object.cobject_methods + object_getsets + object_* impls-done
2D funcobject.cPyClassMethod_Type (all cm_* functions)-done
3D funcobject.cPyStaticMethod_Type (all sm_* functions)-done
4D funcobject.cPyFunction_Type (all func_* functions)-done
5C classobject.cPyMethod_Type (all method_* functions)-done
6B typeobject.ctype_new pipeline (type_new_* functions)1,2,3,4,5done
7B typeobject.cinherit_slots (every slot edge)6done
8E ceval.cSTORE_NAME / LOAD_NAME / DELETE_NAME-done
Gate-enum + re + fnmatch smokeallpartial (enum + fnmatch pass; bare import re pinned; re.match blocked on bytes/bytearray methodlist full port)

Phase 1 - Objects/object.c full port

Functions to port

CPython lists these in object_methods (around line 6048) and object_getsets (around line 6075), plus the supporting object_* implementations earlier in the file. Every row must land.

C functionExposed asgopy hookStatus
object_newobject.__new__SetTypeDescr(objectType, "__new__", ...)done
object_initobject.__init__SetTypeDescr(objectType, "__init__", ...)done
object_reprobject.__repr__SetTypeDescr(objectType, "__repr__", ...)done
object_strobject.__str__SetTypeDescr(objectType, "__str__", ...)done
object_richcompare__eq__, __ne__, __lt__, __le__, __gt__, __ge__six entries on objectTypedone
object_get_class / object_set_classobject.__class__ getsetNewGetSetDescr("__class__", ...)done (setter rejects with TypeError; full compat check deferred)
object_getattroobject.__getattribute__already wired as GenericGetAttr (verify)done
object_setattroobject.__setattr__ / __delattr__already wired (verify)done
object_hashobject.__hash__SetTypeDescr(objectType, "__hash__", ...)done
object_formatobject.__format__SetTypeDescr(objectType, "__format__", ...)done
object_sizeofobject.__sizeof__SetTypeDescr(objectType, "__sizeof__", ...)done (uses reflect.Size; CPython tp_basicsize equivalent deferred)
object___dir__object.__dir__SetTypeDescr(objectType, "__dir__", ...)done
object___reduce__object.__reduce__SetTypeDescr(objectType, "__reduce__", ...)done (forwards to __reduce_ex__(2); pickle deferred)
object___reduce_ex__object.__reduce_ex__SetTypeDescr(objectType, "__reduce_ex__", ...)done (override detection + fallback TypeError; copyreg deferred)
object___getstate__object.__getstate__SetTypeDescr(objectType, "__getstate__", ...)done (simple case; __slotnames__ deferred)
object___subclasshook__object.__subclasshook__ (classmethod)wrap in NewClassMethoddone
object___init_subclass__object.__init_subclass__ (classmethod)wrap in NewClassMethoddone
object_get_dict / object_set_dictobject.__dict__ getsetNewGetSetDescr("__dict__", ...)done (getter; setter deferred to type_new dict layout work)
object___class_getitem__object.__class_getitem__ (classmethod)SetTypeDescr + classmethoddeferred (not in CPython 3.14 object_methods; lives on subclasses)

Gates

GateCommandExpectedStatus
1.1gopy -c 'print(object.__repr__)'repr-of-method-descriptorpass
1.2gopy -c 'class C: pass; print(C.__class__ is type)'Truepass
1.3gopy -c 'class C: pass; print(object.__new__(C).__class__ is C)'Truepass
1.4gopy -c 'class C: pass; print(sorted(dir(C())) == EXPECTED_LIST)'Truepass (dir produces canonical set)
1.5gopy -c 'print(hash(object.__hash__))'runs without exceptionpass
1.6gopy -c 'class C: pass; print(C().__class__)'<class '__main__.C'>pass
1.7gopy -c 'object.__format__(42, "")''42'pass

CPython citations

#Reference
1Objects/object.c:6048 object_methods table
2Objects/object.c:6075 object_getsets table
3Objects/object.c:5800 object___subclasshook__
4Objects/object.c:5750 object___init_subclass__
5Objects/object.c:766 object_format
6Objects/object.c:884 object_richcompare
7Objects/object.c:1146 object_get_class / object_set_class
8Objects/object.c:1462 object_get_dict / object_set_dict
9Objects/object.c:5900 object___getstate__

Phase 2 - Objects/funcobject.c classmethod block

Functions to port

C functionSurfaceStatus
cm_initclassmethod(fn) constructor + functools_wrapsdone (pinned: objects/method_test.go TestClassMethodBindsType)
cm_descr_get__get__(obj, type) returning BoundMethod(fn, type)done (pinned: objects/method_test.go TestClassMethodBindsType)
cm_traverseGC visit cm_callable and cm_dictdone (Go GC; no manual visit needed)
cm_dealloc(Go has GC, no-op)n/a
cm_clear(Go has GC, no-op)n/a
cm_repr<classmethod(REPR_OF_CALLABLE)> textdone (pinned: objects/method_test.go TestClassMethodReprShowsCallable)
cm_memberlist__func__, __wrapped__ getsetsdone (pinned: objects/method_test.go TestClassMethodGetSetGates; gopy exposes the two CPython T_OBJECT members as read-only getsets)
cm_getsetlist__isabstractmethod__, __dict__, __annotations__, __annotate__done (pinned: objects/method_test.go TestClassMethodGetSetGates; __dict__ mutation verified at runtime via cm.__dict__["x"] = 1)
cm_methodlist__class_getitem__ (classmethod via Py_GenericAlias)done (__class_getitem__ wired through SetTypeDescr + NewClassMethod on classmethodType)
__set_name__ forwardingcall cm.__func__.__set_name__(owner, name)n/a (CPython 3.14 classmethod has no __set_name__ slot; wrapped descriptors only fire when assigned directly in the class body; verified via direct-body Probe.__set_name__ call vs. silent no-op when wrapped in classmethod(Probe()))
PEP 487 forwardingclassmethod inherits PEP 487 hooks from wrapped fndone (verified via @classmethod __init_subclass__(cls, **kw) firing on subclass with cls bound by the descriptor protocol; no extra forwarding needed)

Gates

GateCommandExpectedStatus
2.1gopy -c 'class C:\n @classmethod\n def f(cls): pass\nprint(C.__dict__["f"].__func__.__name__)'fpass (pinned: objects/method_test.go TestClassMethodGetSetGates)
2.2gopy -c 'class C:\n @classmethod\n def f(cls): pass\nprint(C.__dict__["f"].__wrapped__ is C.__dict__["f"].__func__)'Truepass (pinned: objects/method_test.go TestClassMethodGetSetGates)
2.3gopy -c 'class C:\n @classmethod\n def f(cls): pass\nprint(C.__dict__["f"].__isabstractmethod__)'Falsepass (pinned: objects/method_test.go TestClassMethodGetSetGates)

CPython citations

#Reference
1Objects/funcobject.c:1487 cm_init
2Objects/funcobject.c:1459 cm_descr_get
3Objects/funcobject.c:1440 cm_traverse
4Objects/funcobject.c:1504 cm_memberlist
5Objects/funcobject.c:1511 cm_get___isabstractmethod__
6Objects/funcobject.c:1525 cm_get___annotations__ / cm_set___annotations__
7Objects/funcobject.c:1538 cm_get___annotate__ / cm_set___annotate__
8Objects/funcobject.c:1551 cm_getsetlist
9Objects/funcobject.c:1559 cm_methodlist
10Objects/funcobject.c:1565 cm_repr
11Objects/funcobject.c:1594 PyClassMethod_Type
12Objects/funcobject.c:1316 functools_wraps
13Objects/funcobject.c:1337 descriptor_get_wrapped_attribute
14Objects/funcobject.c:1367 descriptor_set_wrapped_attribute
15Objects/object.c:1235 _PyObject_IsAbstract

Phase 3 - Objects/funcobject.c staticmethod block

Functions to port

C functionSurfaceStatus
sm_initstaticmethod(fn) constructor + functools_wrapsdone
sm_descr_get__get__(obj, type) returns wrapped fndone
sm_traverseGC visit sm_callable and sm_dictdone
sm_dealloc / sm_clear(Go GC, no-op)n/a
sm_repr<staticmethod(REPR_OF_CALLABLE)> textdone
sm_calldirect call forwards to wrapped fn (CPython 3.10+)done
sm_memberlist__func__, __wrapped__ getsetsdone
sm_getsetlist__isabstractmethod__, __dict__, __annotations__, __annotate__done
sm_methodlist__class_getitem__ (classmethod via Py_GenericAlias)done
__set_name__ forwardingforward to wrapped fnn/a (CPython 3.14 staticmethod has no __set_name__ slot either)

Gates

GateCommandExpectedStatus
3.1gopy -c 'class C:\n @staticmethod\n def f(): return 1\nprint(C.__dict__["f"].__func__())'1pass
3.2gopy -c 'sm = staticmethod(lambda: 7); print(sm())'7 (sm_call)pass
3.3gopy -c 'class C:\n @staticmethod\n def f(): pass\nprint(C.__dict__["f"].__name__)'fpass

CPython citations

#Reference
1Objects/funcobject.c:1731 sm_init
2Objects/funcobject.c:1705 sm_descr_get
3Objects/funcobject.c:1749 sm_call
4Objects/funcobject.c:1687 sm_traverse
5Objects/funcobject.c:1755 sm_memberlist
6Objects/funcobject.c:1762 sm_get___isabstractmethod__
7Objects/funcobject.c:1776 sm_get___annotations__ / sm_set___annotations__
8Objects/funcobject.c:1789 sm_get___annotate__ / sm_set___annotate__
9Objects/funcobject.c:1801 sm_getsetlist
10Objects/funcobject.c:1809 sm_methodlist
11Objects/funcobject.c:1815 sm_repr
12Objects/funcobject.c:1842 PyStaticMethod_Type

Phase 4 - Objects/funcobject.c PyFunction_Type block

Functions to port

C functionSurfaceStatus
PyFunction_Newconstructordone
PyFunction_NewWithQualNameconstructor with qualnamedone
func_get_code / func_set_code__code__ getsetdone
func_get_name / func_set_name__name__ getsetdone
func_get_qualname / func_set_qualname__qualname__ getsetdone
func_get_defaults / func_set_defaults__defaults__ getsetdone
func_get_kwdefaults / func_set_kwdefaults__kwdefaults__ getsetdone
func_get_annotations / func_set_annotations__annotations__ getsetdone
func_get_dict / func_set_dict__dict__ getsetdone
func_get_module / func_set_module__module__ getsetdone
func_repr<function QUALNAME at 0xPTR>done
func_call / func_vectorcallfunction calldone
func_descr_get__get__ returns BoundMethoddone
func_traverseGC visit code, globals, defaults, etc.done
function_memberlist__globals__, __closure__, __builtins__done (via getset)
function___get_signature__(CPython internal)n/a
func_iternext(none)n/a

Gates

GateCommandExpectedStatus
4.1gopy -c 'def f(): pass\nprint(f.__globals__ is globals())'Truepass
4.2gopy -c 'def f(): pass\nprint(f.__module__)'__main__pass
4.3gopy -c 'def f(): pass\nprint(repr(f).startswith("<function f at"))'Truepass

CPython citations

#Reference
1Objects/funcobject.c:30 PyFunction_NewWithQualName
2Objects/funcobject.c:760 func_getsetlist
3Objects/funcobject.c:810 func_memberlist
4Objects/funcobject.c:920 func_repr
5Objects/funcobject.c:1093 func_traverse
6Objects/funcobject.c:1232 PyFunction_Type

Phase 5 - Objects/classobject.c PyMethod_Type full port

Functions to port

C functionSurfaceStatus
PyMethod_Newconstructordone
method_repr<bound method QUALNAME of REPR>done
method_call / method_vectorcallcall with self prependeddone
method_getattroattribute forward through to im_funcdone
method_traverseGC visit im_func and im_selfdone
method_richcompare__eq__ / __ne__ over (func, self)done
method_hashhash combining im_func + im_selfdone
method_memberlist__func__ / __self__ membersdone (via getset)
method_getset__doc__, __name__, __module__, __qualname__done (forwarded via method_getattro)
method___reduce__pickle hookn/a (gopy has no pickle yet; will land with copyreg port)
PyMethod_Function / PyMethod_SelfC API accessorsdone (as Go methods)

Gates

GateCommandExpectedStatus
5.1gopy -c 'class C:\n def f(self): pass\nc = C()\nprint(c.f == c.f)'Truepass
5.2gopy -c 'class C:\n def f(self): pass\nprint(C().f != C().f)'Truepass
5.3gopy -c 'class C:\n def f(self): pass\nprint({C().f, C().f})'set with 2 entriespass
5.4gopy -c 'class C:\n def f(self): pass\nprint(C().f.__name__)'fpass
5.5gopy -c 'class C:\n def f(self): pass\nprint(repr(C().f).startswith("<bound method C.f of"))'Truepass

CPython citations

#Reference
1Objects/classobject.c:38 PyMethod_New
2Objects/classobject.c:75 method_getattro
3Objects/classobject.c:135 method_call / method_vectorcall
4Objects/classobject.c:206 method_richcompare
5Objects/classobject.c:230 method_hash
6Objects/classobject.c:262 method_traverse
7Objects/classobject.c:268 PyMethod_Type
8Objects/classobject.c:280 method_repr

Side fixes

  • object.__repr__ / object.__str__ slot wrappers were re-entering Repr / Str from inside the wrapper, which bounced back through slot_tp_repr for user types and blew the stack. The wrappers now delegate straight to objectRepr / objectStr, matching CPython's direct PyUnicode_FromFormat path. This was a pre-existing latent bug surfaced once method_repr started calling Repr(self).

Phase 6 - Objects/typeobject.c type_new pipeline

Functions to port

C functionPurposegopy hookStatus
type_newtop-level dispatchNewUserType / NewUserTypeKwargsdone
type_new_get_basesresolve bases tupleinline in NewUserTypedone
type_new_allocallocate type objectNewTypedone
type_new_set_attrsstamp __module__ / __qualname__ / __doc__, classmethod-wrap PEP 487 hookscopyNamespaceToTypedone
type_new_set_namesPEP 487 __set_name__ passtypeSetNamesdone
type_init_subclassPEP 487 __init_subclass__ pass with kwargstypeInitSubclassdone
type_new_descriptors__slots__ -> MemberDescr tableinstallSlotsdone
type_new_implmro computationNewTypedone
type_new_set_docdocstring stampingemitInnerClassCode (compile-time)done
type_callmetaclass call -> tp_new + tp_initTypeType.Calldone
type_inittype.initn/a (Go init)done
type_getattrotype attribute lookuptypeGetAttrdone
type_setattrotype attribute settypeSetAttrdone
type_qualname / type_set_qualname__qualname__ getsettypeGetQualname / typeSetQualnamedone
fixup_slot_dispatcherswire C slots from Python dundersfixupSlotDispatchersdone (Phase 7 widened the inherit pass)

Gates

GateCommandExpectedStatus
6.1gopy -c 'class C:\n """doc"""\nprint(C.__doc__)'docpass
6.2gopy -c 'class C: pass\nprint(C.__module__)'__main__pass
6.3gopy -c 'class C:\n class D: pass\nprint(C.D.__qualname__)'C.Dpass
6.4gopy -c 'class B:\n def __init_subclass__(cls, **kw): cls.x = kw["x"]\nclass C(B, x=1): pass\nprint(C.x)'1pass

Side fixes shipped under Phase 6

  • Type.Qualname field added. __qualname__ no longer shadows __name__, so nested classes report the dotted path (C.D) instead of the bare D. typeSetQualname honours the heap-type check the way CPython's type_set_qualname does.
  • copyNamespaceToType now pulls __qualname__ out of the namespace and stamps it on t.Qualname directly. The raw key is skipped from the descr table so the getset (not a stale descriptor) wins on lookup.
  • emitInnerClassCode extracts the body's leading bare-string and emits LOAD_CONST docstring + STORE_NAME __doc__ after the qualname store. Mirrors CPython's class-body docstring branch without disturbing the existing consumeDocstring flow (which pins to consts[0], conflicting with the qualname const slot here).
  • typeInitSubclass now takes the class-creation kwargs. The path from __build_class__ -> typeMetaCall -> NewUserTypeKwargs threads them so __init_subclass__(cls, **kw) actually sees the PEP 487 keyword args.

CPython citations

#Reference
1Objects/typeobject.c:4153 type_new
2Objects/typeobject.c:4350 type_new_set_attrs
3Objects/typeobject.c:4419 type_new_set_attrs (classmethod wrap branch)
4Objects/typeobject.c:4549 type_new_set_names
5Objects/typeobject.c:4595 type_init_subclass
6Objects/typeobject.c:9874 fixup_slot_dispatchers
7Objects/typeobject.c:984 type_qualname
8Objects/typeobject.c:1003 type_set_qualname
9Python/codegen.c codegen_class_body (docstring branch)

Phase 7 - Objects/typeobject.c inherit_slots

Slots to verify propagation for

Every row from CPython's slotdefs table must propagate from the base type when the subclass does not override it. inheritSlotsFromBases runs before fixupSlotDispatchers so the subclass picks up the base's slot table first, then a user-supplied dunder can override individual slot fields. The Number / Sequence / Mapping / Async tables are copied by value (*cp := *base.X) so the per-subclass fixup writes never mutate the base.

Slot groupSlotsStatus
BasicRepr, Str, Hash, Call, TpNew, Iter, IterNext, RichCmp, DescrGet, DescrSet, Format, TpTraversedone
Basic (handled outside inherit_slots)Getattro, Setattrodone (selected per instance shape in NewUserTypeKwargs)
NumberAdd, Subtract, Multiply, Remainder, Divmod, Power, Negative, Positive, Absolute, Bool, Invert, Lshift, Rshift, And, Xor, Or, Int, Float, InPlace*, FloorDivide, TrueDivide, Index, MatrixMultiplydone (table copied as a unit)
MappingLength, GetItem, SetItem, DelItemdone (table copied as a unit)
SequenceLength, Concat, Repeat, GetItem, SetItem, Contains, InPlaceConcat, InPlaceRepeatdone (table copied as a unit)
AsyncAwait, Aiter, Anextdone (table copied as a unit)
BufferGetbuffer, Releasebufferpending (no Buffer subsystem ported yet)

Gates

GateCommandExpectedStatus
7.1gopy -c 'class L(list): pass\nl=L([1,2,3])\nprint(len(l), l[1], list(reversed(l)))'3 2 [3, 2, 1]pass
7.2gopy -c 'class D(dict): pass\nd=D({"a":1})\nprint(d["a"], len(d), "a" in d)'1 1 Truepass
7.3gopy -c 'class I(int): pass\nprint(I(3)+I(4), I(5)*I(2))'7 10pass

Side fixes shipped under Phase 7

  • objectNew now runs the abstract-method guard. Before this phase the guard sat in typeCall and only ran when TpNew was nil. Now that every user class inherits TpNew from object, the check has to live where CPython parks it - inside object_new.
  • The fixup pass order is inherit -> fixup so user dunders override the inherited slot fields. Previously fixup ran first; that worked for the Basic four because they were the only inherited slots, but it would have masked user overrides once the slot tables started propagating.
  • A subclass's slot tables are deep copies of the base's table. Without the copy, any fixupSubscriptSlots write through ensureSequenceMethods on the subclass would smash the base type's table.

CPython citations

#Reference
1Objects/typeobject.c:7521 inherit_slots
2Objects/typeobject.c:9770 slotdefs table header
3Objects/typeobject.c:6854 object_new (Py_TPFLAGS_IS_ABSTRACT branch)

Phase 8 - Python/ceval.c name ops

Functions to port

Opcodegopy hookCPython semanticsStatus
STORE_NAMEstoreInPyObject_SetItem(locals, name, v); fast-path when locals is exact dictdone
LOAD_NAMElookupInPyMapping_GetOptionalItem(locals, name), then globals, then builtinsdone (exact-dict guard gates the fast path so subclass getitem fires)
DELETE_NAMEdeleteInPyObject_DelItem(locals, name)done (exact-dict guard plus objects.DelItem fallback for subclasses)
LOAD_CLASSDEREF (in class scope)lookupClassDerefsame protocol as LOAD_NAME for the class namespacedone (reuses the widened lookupIn exact-dict guard)

Gates

Each gate exercises one of LOAD_NAME / STORE_NAME / DELETE_NAME inside a class body whose namespace is a dict subclass returned by a metaclass __prepare__. Tracing get/set/del lets the gate watch the slot fire.

GateOpcodeExpectedStatus
8.1LOAD_NAME routes through subclass __getitem__log records the X lookup; class attribute computed from override-decorated readpass
8.2STORE_NAME routes through subclass __setitem__log records every name binding inside the class bodypass
8.3DELETE_NAME routes through subclass __delitem__log records the del, and the attribute is gone from the classpass

Pinned form: stdlibinit/name_ops_dict_subclass_test.go (one Go test per gate, each running the Python script under pythonrun.RunString and asserting on stdout).

Side fixes shipped under Phase 8

  • lookupIn (LOAD_NAME) gates its *objects.Dict fast path on scope.Type() == objects.DictType. Dict subclasses fall through to objects.GetItem, which honours the subclass __getitem__ slot. The previous type assertion succeeded on subclasses too, silently bypassing the override.
  • deleteIn (DELETE_NAME) does the same exact-type gate, then falls back to objects.DelItem instead of returning the old "unsupported scope type" error. That branch used to panic when the class body's namespace was anything but a plain Dict.
  • storeIn (STORE_NAME) already routed through objects.SetItem for non-Dict scopes; Phase 8 leaves the fast path in place but documents the symmetric exact-type guard so the three opcodes track each other.

CPython citations

#Reference
1Python/bytecodes.c STORE_NAME
2Python/bytecodes.c LOAD_NAME
3Python/bytecodes.c DELETE_NAME

Final gate

#CommandExpectedStatus
F.0ahash(C) where C has a user metaclass terminatesruns, returns intdone (pinned: stdlibinit/slot_method_lookup_test.go)
F.0bhash(C) is stable across callsTruedone (pinned: stdlibinit/slot_method_lookup_test.go)
F.1gopy -c 'import enum; print(enum.FlagBoundary.STRICT)'strict (StrEnum value matches CPython 3.14)pass (pinned: stdlibinit/enum_import_test.go)
F.2gopy -c 'import enum; print(list(enum.FlagBoundary))'[<FlagBoundary.STRICT: 'strict'>, <FlagBoundary.CONFORM: 'conform'>, <FlagBoundary.EJECT: 'eject'>, <FlagBoundary.KEEP: 'keep'>]pass (pinned: stdlibinit/enum_import_test.go)
F.3gopy -c 'import re; print(re.match(r"(\d+)-(\d+)", "12-34").groups())'('12', '34')pass (pinned: stdlibinit/re_match_smoke_test.go). Wired the full bytes/bytearray methodlist (Objects/bytesobject.c bytes_methods + Objects/bytearrayobject.c bytearray_methods) and the mappingproxy methodlist (Objects/descrobject.c mappingproxy_methods: get/keys/values/items/copy/reversed) so dict.update(cls.__members__) inside @enum.global_enum picks the keys() fast path.
F.4gopy -c 'import fnmatch; print(fnmatch.fnmatch("foo.py", "*.py"))'Truepass (pinned: stdlibinit/fnmatch_smoke_import_test.go plus manual fnmatch.fnmatch returns True)
F.5spec 1703 phase 7 rowflips to donedone (website/docs/specs/1700/1703_re_sre_full_port.md Phase 7 + Phase 8 + Final gate rows all flipped to done).
F.6spec 1702 enum rowflips to donedone (website/docs/specs/1700/1702_subsystem_port_log.md now carries the enum row marked done, plus the re / _sre row flipped from pending to done).

Side fixes shipped while debugging enum import

These were uncovered while bisecting import enum. They are slot mechanics, not enum specifics, so they live alongside the slot dispatcher block in objects/usertype.go.

HunkWhatWhy it broke enum
objects/usertype.go slot dispatchersroute every __dunder__ lookup through lookupMethodOnSelf instead of GetAttrGetAttr(cls, name) returns the descriptor unbound when the receiver is a class, so hash(C) for a class with a user metaclass tried to call object.__hash__() with no arguments
objects/object.go objectHashDescrcomputes identity hash directly, no longer routes through Hash()the inherited object.__hash__ is the same descriptor reached by slotTpHash; the old wrapper recursed back through Hash and exploded the stack
builtins/init.go bindCtoralso installs the __new__ wrapper as a descriptor on the type's own __dict__enum's _find_data_type_ uses '__new__' in base.__dict__ to decide which mixin supplies storage; without it IntEnum(int, ReprEnum) falls through to the ReprEnum subclasses must be mixed with a data type guard
objects/errors.go errKeyNotFoundmessage prefixed with KeyError: so the vm unwind table promotes it to a real PyExc_KeyErrorbare errKeyNotFound reached the synthesizer with no prefix and surfaced as a plain Exception("KeyError"); downstream except KeyError: clauses never caught it

Out of scope

TopicReason
Full pickle (__reduce__, copyreg)Phase 1 wires the descriptor; real reduce_newobj follows in a pickle spec
__class__ reassignment validationPhase 1 wires the getset; large compat rules deferred
inherit_slots async + buffer rowsNo async / buffer subsystem ports landed
Locale-specific __hash__ for stringsstring subsystem owns its hash
GC tp_clear / tp_deallocGo has GC; explicit clear/dealloc only ported where they hold non-Go state

Today's already-landed pieces (do not redo)

FileHunkPhase
objects/usertype.gotypeSetNames PEP 487 pass6
objects/usertype.gotypeInitSubclass6
vm/eval_simple.gostoreIn dispatches through PyObject_SetItem for dict subclasses8
objects/function.goFunctionType.Hash = identityHash4
objects/function_builtin.goBuiltinFunctionType.Hash = identityHash(CPython inheritance)
objects/method_descr.goMethodDescrType.Hash = identityHash(CPython inheritance)

Tasks

One task per phase, all blocking #544.

TaskPhaseTitle
TBD1Port Objects/object.c methodlist + getsetlist in full
TBD2Port Objects/funcobject.c classmethod block in full
TBD3Port Objects/funcobject.c staticmethod block in full
TBD4Port Objects/funcobject.c PyFunction_Type block in full
TBD5Port Objects/classobject.c PyMethod_Type in full
TBD6Port Objects/typeobject.c type_new pipeline in full
TBD7Audit Objects/typeobject.c inherit_slots: every slot edge
TBD8Port Python/ceval.c STORE_NAME / LOAD_NAME / DELETE_NAME in full