Skip to content

Commit 4c312a8

Browse files
committed
Fix serialization of dataclass inheriting from abc.ABC and using __slots__
1 parent d52f2c9 commit 4c312a8

File tree

3 files changed

+30
-1
lines changed

3 files changed

+30
-1
lines changed

src/serialize/serializer.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,10 @@ impl<'p> Serialize for PyObjectSerializer {
257257
err!(RECURSION_LIMIT_REACHED)
258258
}
259259
let dict = ffi!(PyObject_GetAttr(self.ptr, DICT_STR));
260-
if unlikely!(dict.is_null()) {
260+
let ob_type = ob_type!(self.ptr);
261+
if unlikely!(
262+
dict.is_null() || ffi!(PyDict_Contains((*ob_type).tp_dict, SLOTS_STR)) == 1
263+
) {
261264
unsafe { pyo3::ffi::PyErr_Clear() };
262265
DataclassFallbackSerializer::new(
263266
self.ptr,

src/typeref.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub static mut EMPTY_UNICODE: *mut PyObject = 0 as *mut PyObject;
5353
pub static mut DST_STR: *mut PyObject = 0 as *mut PyObject;
5454
pub static mut DICT_STR: *mut PyObject = 0 as *mut PyObject;
5555
pub static mut DATACLASS_FIELDS_STR: *mut PyObject = 0 as *mut PyObject;
56+
pub static mut SLOTS_STR: *mut PyObject = 0 as *mut PyObject;
5657
pub static mut FIELD_TYPE_STR: *mut PyObject = 0 as *mut PyObject;
5758
pub static mut ARRAY_STRUCT_STR: *mut PyObject = 0 as *mut PyObject;
5859
pub static mut DTYPE_STR: *mut PyObject = 0 as *mut PyObject;
@@ -122,6 +123,7 @@ pub fn init_typerefs() {
122123
DICT_STR = PyUnicode_InternFromString("__dict__\0".as_ptr() as *const c_char);
123124
DATACLASS_FIELDS_STR =
124125
PyUnicode_InternFromString("__dataclass_fields__\0".as_ptr() as *const c_char);
126+
SLOTS_STR = PyUnicode_InternFromString("__slots__\0".as_ptr() as *const c_char);
125127
FIELD_TYPE_STR = PyUnicode_InternFromString("_field_type\0".as_ptr() as *const c_char);
126128
ARRAY_STRUCT_STR =
127129
pyo3::ffi::PyUnicode_InternFromString("__array_struct__\0".as_ptr() as *const c_char);

test/test_dataclass.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
22

3+
import abc
34
import unittest
45
import uuid
56
from dataclasses import InitVar, asdict, dataclass, field
@@ -95,6 +96,23 @@ def __post_init__(self, a: str, b: str):
9596
self.ab = f"{a} {b}"
9697

9798

99+
class AbstractBase(abc.ABC):
100+
@abc.abstractmethod
101+
def key(self):
102+
raise NotImplementedError
103+
104+
105+
@dataclass(frozen=True)
106+
class ConcreteAbc(AbstractBase):
107+
108+
__slots__ = ("attr",)
109+
110+
attr: float
111+
112+
def key(self):
113+
return "dkjf"
114+
115+
98116
class DataclassTests(unittest.TestCase):
99117
def test_dataclass(self):
100118
"""
@@ -291,3 +309,9 @@ def default(obj):
291309
orjson.dumps(obj, option=orjson.OPT_PASSTHROUGH_DATACLASS, default=default),
292310
b'{"name":"a","number":1}',
293311
)
312+
313+
314+
class AbstractDataclassTests(unittest.TestCase):
315+
def test_dataclass_abc(self):
316+
obj = ConcreteAbc(1.0)
317+
self.assertEqual(orjson.dumps(obj), b'{"attr":1.0}')

0 commit comments

Comments
 (0)