Skip to content

Commit 64d595a

Browse files
authored
v0.39.1 (#240)
* rename methods for clarity + slight change to `exclude` logic * fix benchmarks for Python 3.14+ * remove "dead code" * remove more "dead code" * remove more "dead code" * update benchmark results * don't assign to `v1` if not needed * fix type errors in serial_json.pyi (#237) * update benchmarks and catch_all.png * update docs & HISTORY.rst
1 parent 1ac6846 commit 64d595a

File tree

16 files changed

+495
-313
lines changed

16 files changed

+495
-313
lines changed

HISTORY.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22
History
33
=======
44

5+
0.39.1 (2026-01-05)
6+
-------------------
7+
8+
**Maintenance and Fixes**
9+
10+
- General code cleanup and minor internal refactoring.
11+
- Minor optimization in v1 code generation for ``dump``:
12+
13+
- For fields with defaults, the generated code now uses the field name directly
14+
when the value is unchanged.
15+
- Avoids assigning to the temporary ``v1`` variable when it is not required,
16+
resulting in slightly leaner generated output.
17+
18+
- Updated results under ``benchmarks/`` to reflect the latest changes.
19+
- Fixed **mypy** errors in ``serial_json.py`` (fixes :issue:`237`).
20+
521
0.39.0 (2026-01-01)
622
-------------------
723

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ BROWSER := python -c "$$BROWSER_PYSCRIPT"
2626
help:
2727
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
2828

29-
init: ## install all dev dependencies for this project
30-
pip install -e .[dev]
29+
init: ## install all dev + test dependencies for this project
30+
pip install -e .[bench,dev,docs,test]
3131
python -m pip install pre-commit build twine
3232
pre-commit install
3333

benchmarks/catch_all.png

-10.8 KB
Loading

benchmarks/catch_all.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import pytest
66

77
from dataclasses_json import (dataclass_json, Undefined, CatchAll as CatchAllDJ)
8-
from dataclass_wizard import (JSONWizard, CatchAll as CatchAllWizard)
8+
from dataclass_wizard import (DataclassWizard, CatchAll as CatchAllWizard)
99

1010

1111
log = logging.getLogger(__name__)
@@ -23,11 +23,11 @@ class DontCareAPIDumpDJ(DontCareAPIDump):
2323
unknown_things: CatchAllDJ
2424

2525

26-
@dataclass()
27-
class DontCareAPIDumpWizard(DontCareAPIDump, JSONWizard):
26+
class DontCareAPIDumpWizard(DontCareAPIDump, DataclassWizard):
2827

29-
class _(JSONWizard.Meta):
30-
v1 = True
28+
# `v1=True` is the default with `DataclassWizard`
29+
# class _(JSONWizard.Meta):
30+
# v1 = True
3131

3232
unknown_things: CatchAllWizard
3333

benchmarks/complex.py

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from dataclass_wizard import JSONWizard, LoadMeta
2020
from dataclass_wizard.class_helper import create_new_class
21+
from dataclass_wizard.constants import PY314_OR_ABOVE
2122
from dataclass_wizard.utils.string_conv import to_snake_case
2223
from dataclass_wizard.utils.type_conv import as_datetime
2324

@@ -217,25 +218,27 @@ def parse_datetime(value: str) -> datetime:
217218

218219
def test_load(request, data, data_2, data_dacite, n):
219220
"""
220-
[ RESULTS ON MAC OS X ]
221-
222-
benchmarks.complex.complex - [INFO] dataclass-wizard 0.317641
223-
benchmarks.complex.complex - [INFO] dataclass-factory 0.751124
224-
benchmarks.complex.complex - [INFO] dacite 6.350958
225-
benchmarks.complex.complex - [INFO] mashumaro 0.343612
226-
benchmarks.complex.complex - [INFO] pydantic 0.538801
227-
benchmarks.complex.complex - [INFO] dataclasses-json 28.214992
228-
benchmarks.complex.complex - [INFO] jsons 31.735730
229-
benchmarks.complex.complex - [INFO] jsons (strict) 34.855084
221+
[ RESULTS]
222+
platform darwin -- Python 3.13.11, pytest-8.3.4, pluggy-1.6.0
223+
224+
benchmarks.complex.complex - [INFO] dataclass-wizard 0.312827
225+
benchmarks.complex.complex - [INFO] dataclass-factory 0.759241
226+
benchmarks.complex.complex - [INFO] dacite 7.918317
227+
benchmarks.complex.complex - [INFO] mashumaro 0.344386
228+
benchmarks.complex.complex - [INFO] pydantic 0.510155
229+
benchmarks.complex.complex - [INFO] dataclasses-json 28.365398
230+
benchmarks.complex.complex - [INFO] jsons 29.601413
231+
benchmarks.complex.complex - [INFO] jsons (strict) 34.682349
230232
"""
231233
g = globals().copy()
232234
g.update(locals())
233235

234236
log.info('dataclass-wizard %f',
235237
timeit('MyClassWizard.from_dict(data)', globals=g, number=n))
236238

237-
log.info('dataclass-factory %f',
238-
timeit('factory.load(data_2, MyClass)', globals=g, number=n))
239+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
240+
log.info('dataclass-factory %f',
241+
timeit('factory.load(data_2, MyClass)', globals=g, number=n))
239242

240243
log.info('dacite %f',
241244
timeit('dacite_from_dict(MyClassDacite, data_dacite, config=dacite_cfg)',
@@ -249,7 +252,8 @@ def test_load(request, data, data_2, data_dacite, n):
249252

250253
# Assert the dataclass instances have the same values for all fields.
251254
c1 = MyClassWizard.from_dict(data)
252-
c2 = factory.load(data_2, MyClass)
255+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
256+
c2 = factory.load(data_2, MyClass)
253257
c3 = MyClassDJ.from_dict(data_2)
254258
c4 = MyClassJsons.load(data)
255259
c5 = MyClassMashumaro.from_dict(data)
@@ -274,19 +278,21 @@ def test_load(request, data, data_2, data_dacite, n):
274278

275279
def test_dump(request, data, data_2, data_dacite, n):
276280
"""
277-
[ RESULTS ON MAC OS X ]
278-
279-
benchmarks.complex.complex - [INFO] dataclass-wizard 0.405688
280-
benchmarks.complex.complex - [INFO] asdict (dataclasses) 1.727631
281-
benchmarks.complex.complex - [INFO] dataclass-factory 0.831178
282-
benchmarks.complex.complex - [INFO] dataclasses-json 11.072727
283-
benchmarks.complex.complex - [INFO] mashumaro 0.248298
284-
benchmarks.complex.complex - [INFO] pydantic 0.316203
285-
benchmarks.complex.complex - [INFO] jsons 37.361450
286-
benchmarks.complex.complex - [INFO] jsons (strict) 31.578708
281+
[ RESULTS]
282+
platform darwin -- Python 3.13.11, pytest-8.3.4, pluggy-1.6.0
283+
284+
benchmarks.complex.complex - [INFO] dataclass-wizard 0.272646
285+
benchmarks.complex.complex - [INFO] asdict (dataclasses) 1.726522
286+
benchmarks.complex.complex - [INFO] dataclass-factory 0.802548
287+
benchmarks.complex.complex - [INFO] dataclasses-json 11.040730
288+
benchmarks.complex.complex - [INFO] mashumaro 0.246202
289+
benchmarks.complex.complex - [INFO] pydantic 0.267089
290+
benchmarks.complex.complex - [INFO] jsons 35.417559
291+
benchmarks.complex.complex - [INFO] jsons (strict) 30.918420
287292
"""
288293
c1 = MyClassWizard.from_dict(data)
289-
c2 = factory.load(data_2, MyClass)
294+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
295+
c2 = factory.load(data_2, MyClass)
290296
c3 = MyClassDJ.from_dict(data_2)
291297
c4 = MyClassJsons.load(data)
292298
c5 = MyClassMashumaro.from_dict(data)
@@ -301,8 +307,9 @@ def test_dump(request, data, data_2, data_dacite, n):
301307
log.info('asdict (dataclasses) %f',
302308
timeit('asdict(c1)', globals=g, number=n))
303309

304-
log.info('dataclass-factory %f',
305-
timeit('factory.dump(c2, MyClass)', globals=g, number=n))
310+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
311+
log.info('dataclass-factory %f',
312+
timeit('factory.dump(c2, MyClass)', globals=g, number=n))
306313

307314
log.info('dataclasses-json %f',
308315
timeit('c3.to_dict()', globals=g, number=n))

benchmarks/nested.py

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from dataclass_wizard import JSONWizard, LoadMeta
1717
from dataclass_wizard.class_helper import create_new_class
18+
from dataclass_wizard.constants import PY314_OR_ABOVE
1819
from dataclass_wizard.utils.string_conv import to_snake_case
1920
from dataclass_wizard.utils.type_conv import as_datetime, as_date
2021

@@ -212,15 +213,16 @@ def data():
212213

213214
def test_load(request, data, n):
214215
"""
215-
[ RESULTS ON MAC OS X ]
216+
[ RESULTS]
217+
platform darwin -- Python 3.13.11, pytest-8.3.4, pluggy-1.6.0
216218
217-
benchmarks.nested.nested - [INFO] dataclass-wizard 0.130734
218-
benchmarks.nested.nested - [INFO] dataclass-factory 0.404371
219-
benchmarks.nested.nested - [INFO] dataclasses-json 11.315233
220-
benchmarks.nested.nested - [INFO] mashumaro 0.158986
221-
benchmarks.nested.nested - [INFO] pydantic 0.330295
222-
benchmarks.nested.nested - [INFO] jsons 25.084872
223-
benchmarks.nested.nested - [INFO] jsons (strict) 28.306646
219+
benchmarks.nested.nested - [INFO] dataclass-wizard 0.128877
220+
benchmarks.nested.nested - [INFO] dataclass-factory 0.405885
221+
benchmarks.nested.nested - [INFO] dataclasses-json 11.878780
222+
benchmarks.nested.nested - [INFO] mashumaro 0.154879
223+
benchmarks.nested.nested - [INFO] pydantic 0.286836
224+
benchmarks.nested.nested - [INFO] jsons 24.753070
225+
benchmarks.nested.nested - [INFO] jsons (strict) 26.192690
224226
225227
"""
226228
g = globals().copy()
@@ -231,8 +233,9 @@ def test_load(request, data, n):
231233
log.info('dataclass-wizard %f',
232234
timeit('MyClassWizard.from_dict(data)', globals=g, number=n))
233235

234-
log.info('dataclass-factory %f',
235-
timeit('factory.load(data, Data1)', globals=g, number=n))
236+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
237+
log.info('dataclass-factory %f',
238+
timeit('factory.load(data, Data1)', globals=g, number=n))
236239

237240
log.info('dataclasses-json %f',
238241
timeit('MyClassDJ.from_dict(data)', globals=g, number=n))
@@ -257,31 +260,37 @@ def test_load(request, data, n):
257260
timeit('MyClassJsons.load(data, strict=True)', globals=g, number=n))
258261

259262
c1 = MyClassWizard.from_dict(data)
260-
c2 = factory.load(data, Data1)
263+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
264+
c2 = factory.load(data, Data1)
261265
c3 = MyClassDJ.from_dict(data)
262266
c4 = MyClassJsons.load(data)
263267
c5 = MyClassMashumaro.from_dict(data)
264268
# c6 = dacite_from_dict(MyClass, data)
265269
c7 = MyClassPydantic(**data)
266270

267-
assert c1.__dict__ == c2.__dict__ == c3.__dict__ == c4.__dict__ == c5.__dict__ == c7.__dict__ # == c6.__dict__
271+
assert c1.__dict__ == c3.__dict__ == c4.__dict__ == c5.__dict__ == c7.__dict__ # == c6.__dict__
272+
273+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
274+
assert c1.__dict__ == c2.__dict__
268275

269276

270277
def test_dump(request, data, n):
271278
"""
272-
[ RESULTS ON MAC OS X ]
273-
274-
INFO benchmarks.nested:nested.py:258 dataclass-wizard 0.460812
275-
INFO benchmarks.nested:nested.py:261 asdict (dataclasses) 0.674034
276-
INFO benchmarks.nested:nested.py:264 dataclass-factory 0.233023
277-
INFO benchmarks.nested:nested.py:267 dataclasses-json 5.717344
278-
INFO benchmarks.nested:nested.py:270 mashumaro 0.086356
279-
INFO benchmarks.nested:nested.py:273 pydantic 0.209953
280-
INFO benchmarks.nested:nested.py:279 jsons 49.321013
281-
INFO benchmarks.nested:nested.py:282 jsons (strict) 44.051063
279+
[ RESULTS]
280+
platform darwin -- Python 3.13.11, pytest-8.3.4, pluggy-1.6.0
281+
282+
benchmarks.nested.nested - [INFO] dataclass-wizard 0.093090
283+
benchmarks.nested.nested - [INFO] asdict (dataclasses) 0.621261
284+
benchmarks.nested.nested - [INFO] dataclass-factory 0.209332
285+
benchmarks.nested.nested - [INFO] dataclasses-json 5.172026
286+
benchmarks.nested.nested - [INFO] mashumaro 0.074544
287+
benchmarks.nested.nested - [INFO] pydantic 0.174419
288+
benchmarks.nested.nested - [INFO] jsons 40.467886
289+
benchmarks.nested.nested - [INFO] jsons (strict) 36.541698
282290
"""
283291
c1 = MyClassWizard.from_dict(data)
284-
c2 = factory.load(data, Data1)
292+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
293+
c2 = factory.load(data, Data1)
285294
c3 = MyClassDJ.from_dict(data)
286295
c4 = MyClassJsons.load(data)
287296
c5 = MyClassMashumaro.from_dict(data)
@@ -296,8 +305,9 @@ def test_dump(request, data, n):
296305
log.info('asdict (dataclasses) %f',
297306
timeit('asdict(c1)', globals=g, number=n))
298307

299-
log.info('dataclass-factory %f',
300-
timeit('factory.dump(c2, Data1)', globals=g, number=n))
308+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
309+
log.info('dataclass-factory %f',
310+
timeit('factory.dump(c2, Data1)', globals=g, number=n))
301311

302312
log.info('dataclasses-json %f',
303313
timeit('c3.to_dict()', globals=g, number=n))

benchmarks/simple.py

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from dataclass_wizard import JSONWizard, LoadMeta
1717
from dataclass_wizard.class_helper import create_new_class
18+
from dataclass_wizard.constants import PY314_OR_ABOVE
1819
from dataclass_wizard.utils.string_conv import to_snake_case
1920

2021
log = logging.getLogger(__name__)
@@ -79,26 +80,28 @@ def data():
7980

8081
def test_load(data, n):
8182
"""
82-
[ RESULTS ON MAC OS X ]
83-
84-
benchmarks.simple.simple - [INFO] dataclass-wizard 0.030784
85-
benchmarks.simple.simple - [INFO] dataclass-factory 0.103156
86-
benchmarks.simple.simple - [INFO] dataclasses-json 3.512702
87-
benchmarks.simple.simple - [INFO] jsons 4.709339
88-
benchmarks.simple.simple - [INFO] dacite 0.468830
89-
benchmarks.simple.simple - [INFO] pydantic 0.071347
90-
benchmarks.simple.simple - [INFO] marshmallow 2.155037
91-
benchmarks.simple.simple - [INFO] attrs 0.020167
92-
benchmarks.simple.simple - [INFO] mashumaro 0.041291
83+
[ RESULTS]
84+
platform darwin -- Python 3.13.11, pytest-8.3.4, pluggy-1.6.0
85+
86+
benchmarks.simple.simple - [INFO] dataclass-wizard 0.029298
87+
benchmarks.simple.simple - [INFO] dataclass-factory 0.100123
88+
benchmarks.simple.simple - [INFO] dataclasses-json 3.530623
89+
benchmarks.simple.simple - [INFO] jsons 4.920980
90+
benchmarks.simple.simple - [INFO] dacite 0.612328
91+
benchmarks.simple.simple - [INFO] pydantic 0.063677
92+
benchmarks.simple.simple - [INFO] marshmallow 2.197097
93+
benchmarks.simple.simple - [INFO] attrs 0.020192
94+
benchmarks.simple.simple - [INFO] mashumaro 0.040619
9395
"""
9496
g = globals().copy()
9597
g.update(locals())
9698

9799
# Add dacite and pydantic benchmarks
98100
log.info("dataclass-wizard %f",
99101
timeit("MyClassWizard.from_dict(data)", globals=g, number=n))
100-
log.info("dataclass-factory %f",
101-
timeit("factory.load(data, MyClass)", globals=g, number=n))
102+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
103+
log.info("dataclass-factory %f",
104+
timeit("factory.load(data, MyClass)", globals=g, number=n))
102105
log.info("dataclasses-json %f",
103106
timeit("MyClassDJ.from_dict(data)", globals=g, number=n))
104107
log.info("jsons %f",
@@ -116,7 +119,8 @@ def test_load(data, n):
116119

117120
# Assert the dataclass instances have the same values for all fields.
118121
c1 = MyClassWizard.from_dict(data)
119-
c2 = factory.load(data, MyClass)
122+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
123+
c2 = factory.load(data, MyClass)
120124
c3 = MyClassDJ.from_dict(data)
121125
c4 = MyClassJsons.load(data)
122126
c5 = dacite_from_dict(MyClass, data)
@@ -125,26 +129,32 @@ def test_load(data, n):
125129
c8 = MyClassAttrs(**data)
126130
c9 = MyClassMashumaro.from_dict(data)
127131

128-
assert c1.__dict__ == c2.__dict__ == c3.__dict__ == c4.__dict__ == c5.__dict__ == c6.model_dump() == c7 == c8.__dict__ == c9.to_dict()
132+
assert c1.__dict__ == c3.__dict__ == c4.__dict__ == c5.__dict__ == c6.model_dump() == c7 == c8.__dict__ == c9.to_dict()
133+
134+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
135+
assert c1.__dict__ == c2.__dict__
136+
129137

130138
def test_dump(data, n):
131139
"""
132-
[ RESULTS ON MAC OS X ]
133-
134-
benchmarks.simple.simple - [INFO] dataclass-wizard 0.024619
135-
benchmarks.simple.simple - [INFO] asdict (dataclasses) 0.093137
136-
benchmarks.simple.simple - [INFO] dataclass-factory 0.188235
137-
benchmarks.simple.simple - [INFO] dataclasses-json 1.294685
138-
benchmarks.simple.simple - [INFO] jsons 6.913666
140+
[ RESULTS]
141+
platform darwin -- Python 3.13.11, pytest-8.3.4, pluggy-1.6.0
142+
143+
benchmarks.simple.simple - [INFO] dataclass-wizard 0.010870
144+
benchmarks.simple.simple - [INFO] asdict (dataclasses) 0.085224
145+
benchmarks.simple.simple - [INFO] dataclass-factory 0.070084
146+
benchmarks.simple.simple - [INFO] dataclasses-json 1.272380
147+
benchmarks.simple.simple - [INFO] jsons 5.980036
139148
benchmarks.simple.simple - [INFO] dacite (not applicable) -- skipped
140-
benchmarks.simple.simple - [INFO] pydantic 0.066996
141-
benchmarks.simple.simple - [INFO] marshmallow 0.000519
142-
benchmarks.simple.simple - [INFO] attrs 0.122752
143-
benchmarks.simple.simple - [INFO] mashumaro 0.008702
149+
benchmarks.simple.simple - [INFO] pydantic 0.079050
150+
benchmarks.simple.simple - [INFO] marshmallow 0.000489
151+
benchmarks.simple.simple - [INFO] attrs 0.054118
152+
benchmarks.simple.simple - [INFO] mashumaro 0.008982
144153
"""
145154

146155
c1 = MyClassWizard.from_dict(data)
147-
c2 = factory.load(data, MyClass)
156+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
157+
c2 = factory.load(data, MyClass)
148158
c3 = MyClassDJ.from_dict(data)
149159
c4 = MyClassJsons.load(data)
150160
c5 = dacite_from_dict(MyClass, data)
@@ -160,8 +170,9 @@ def test_dump(data, n):
160170
timeit("c1.to_dict()", globals=g, number=n))
161171
log.info("asdict (dataclasses) %f",
162172
timeit("asdict(c1)", globals=g, number=n))
163-
log.info("dataclass-factory %f",
164-
timeit("factory.dump(c2, MyClass)", globals=g, number=n))
173+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
174+
log.info("dataclass-factory %f",
175+
timeit("factory.dump(c2, MyClass)", globals=g, number=n))
165176
log.info("dataclasses-json %f",
166177
timeit("c3.to_dict()", globals=g, number=n))
167178
log.info("jsons %f",
@@ -179,4 +190,7 @@ def test_dump(data, n):
179190
# Assert the dict objects which are the result of `to_dict` are all equal.
180191
c1_dict = {to_snake_case(f): fval for f, fval in c1.to_dict().items()}
181192

182-
assert c1_dict == factory.dump(c2, MyClass) == c3.to_dict() == c4.dump() == c6.model_dump() == attr.asdict(c8) == c9.to_dict()
193+
assert c1_dict == c3.to_dict() == c4.dump() == c6.model_dump() == attr.asdict(c8) == c9.to_dict()
194+
195+
if not PY314_OR_ABOVE: # breaks on Python 3.14+
196+
assert c1_dict == factory.dump(c2, MyClass)

0 commit comments

Comments
 (0)