Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion mypyc/irbuild/classdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,10 @@ def finalize(self, ir: ClassIR) -> None:
dec = self.builder.accept(
next(d for d in self.cdef.decorators if is_dataclass_decorator(d))
)
dataclass_type_val = self.builder.load_str(dataclass_type(self.cdef) or "unknown")
self.builder.call_c(
dataclass_sleight_of_hand,
[dec, self.type_obj, self.non_ext.dict, self.non_ext.anns],
[dec, self.type_obj, self.non_ext.dict, self.non_ext.anns, dataclass_type_val],
self.cdef.line,
)

Expand Down
2 changes: 2 additions & 0 deletions mypyc/irbuild/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ def is_dataclass(cdef: ClassDef) -> bool:
return any(is_dataclass_decorator(d) for d in cdef.decorators)


# The string values returned by this function are inspected in
# mypyc/lib-rt/misc_ops.c:CPyDataclass_SleightOfHand(...).
def dataclass_type(cdef: ClassDef) -> str | None:
for d in cdef.decorators:
typ = dataclass_decorator_type(d)
Expand Down
3 changes: 2 additions & 1 deletion mypyc/lib-rt/CPy.h
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,8 @@ PyObject *CPyType_FromTemplateWrapper(PyObject *template_,
PyObject *orig_bases,
PyObject *modname);
int CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp,
PyObject *dict, PyObject *annotations);
PyObject *dict, PyObject *annotations,
PyObject *dataclass_type);
PyObject *CPyPickle_SetState(PyObject *obj, PyObject *state);
PyObject *CPyPickle_GetState(PyObject *obj);
CPyTagged CPyTagged_Id(PyObject *o);
Expand Down
28 changes: 25 additions & 3 deletions mypyc/lib-rt/misc_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,15 @@ static int _CPy_UpdateObjFromDict(PyObject *obj, PyObject *dict)
* tp: The class we are making a dataclass
* dict: The dictionary containing values that dataclasses needs
* annotations: The type annotation dictionary
* dataclass_type: A str object with the return value of util.py:dataclass_type()
*/
int
CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp,
PyObject *dict, PyObject *annotations) {
PyObject *dict, PyObject *annotations,
PyObject *dataclass_type) {
PyTypeObject *ttp = (PyTypeObject *)tp;
Py_ssize_t pos;
PyObject *res;
PyObject *res = NULL;

/* Make a copy of the original class __dict__ */
PyObject *orig_dict = PyDict_Copy(ttp->tp_dict);
Expand Down Expand Up @@ -381,17 +383,37 @@ CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp,
if (!res) {
goto fail;
}
Py_DECREF(res);
const char *dataclass_type_ptr = PyUnicode_AsUTF8(dataclass_type);
if (dataclass_type_ptr == NULL) {
goto fail;
}
if (strcmp(dataclass_type_ptr, "attr") == 0 ||
strcmp(dataclass_type_ptr, "attr-auto") == 0) {
// These attributes are added or modified by @attr.s(slots=True).
const char * const keys[] = {"__attrs_attrs__", "__attrs_own_setattr__", "__init__", ""};
for (const char * const *key_iter = keys; **key_iter != '\0'; key_iter++) {
PyObject *value = NULL;
int rv = PyObject_GetOptionalAttrString(res, *key_iter, &value);
if (rv == 1) {
PyObject_SetAttrString(tp, *key_iter, value);
Py_DECREF(value);
} else if (rv == -1) {
goto fail;
}
}
}

/* Copy back the original contents of the dict */
if (_CPy_UpdateObjFromDict(tp, orig_dict) != 0) {
goto fail;
}

Py_DECREF(res);
Py_DECREF(orig_dict);
return 1;

fail:
Py_XDECREF(res);
Py_XDECREF(orig_dict);
return 0;
}
Expand Down
8 changes: 7 additions & 1 deletion mypyc/primitives/misc_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,13 @@
# Create a dataclass from an extension class. See
# CPyDataclass_SleightOfHand for more docs.
dataclass_sleight_of_hand = custom_op(
arg_types=[object_rprimitive, object_rprimitive, dict_rprimitive, dict_rprimitive],
arg_types=[
object_rprimitive,
object_rprimitive,
dict_rprimitive,
dict_rprimitive,
str_rprimitive,
],
return_type=bit_rprimitive,
c_function_name="CPyDataclass_SleightOfHand",
error_kind=ERR_FALSE,
Expand Down
14 changes: 14 additions & 0 deletions mypyc/test-data/run-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -2705,3 +2705,17 @@ print(native.ColorCode.OKGREEN.value)

[out]
okgreen

[case testAttrWithSlots]
import attr

@attr.s(slots=True)
class A:
ints: list[int] = attr.ib()

[file driver.py]
import native
print(native.A(ints=[1, -17]).ints)

[out]
\[1, -17]
Loading