jcrist · GitHub

@jcrist

Previously we supported encoding dataclasses (determined as any object
with a `__dataclass_fields__` attribute), provided those objects were
implemented in a way similar-enough to how they were implemented in the
standard library. The intent was to support stdlib dataclasses, and if
alternative implementations (e.g. `pydantic.dataclasses`) happened to
work then all the better.
However, due to how we were detecting if an object was a dataclass,
there was no way to override our builtin support if an alternative
implementation (in this case `edgedb.Object`) didn't work.
To fix this, we now make fewer assumptions about how the backing
dataclass object is implemented.
Pros:
- We can now natively encode objects implemented using `dataclasses`,
  `pydantic.dataclasses`, and `edgedb.Object`.
- We now only encode fields as declared on the dataclass object.
  Previously we encoded any attribute lacking a leading underscore,
  which was efficient and worked well in practice (it's also what
  `orjson` does). However, this can lead to weird behavior if some
  fields intentionally start with an `_` (like `_id` in mongodb) or if
  the object makes use of `functools.cached_property`.
Cons:
- This flexibility and correctness comes at a performance cost. The fast
  path is the common case (dataclass uses `__dict__`, doesn't override
  `__getattribute__`), but encoding is now ~20% slower than before.
  Before we encoded `__dict__` based dataclasses 20% faster than orjson;
  now we're faster for small classes (<= 8 items, on my machine) and
  slower for larger numbers of fields. For `__slots__` based classes
  we're still around 2x faster than `orjson`.

Read the original on github.com ↗