Skip to content

Commit f156c3d

Browse files
authored
Merge pull request #71 from DavidCEllis/simplify-make-prefab
Simplify _make_prefab by splitting it into pre/post process components
2 parents 7919b75 + eeeac2b commit f156c3d

File tree

11 files changed

+455
-238
lines changed

11 files changed

+455
-238
lines changed

perf/hyperfine_testmaker.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ class C{n}:
149149
e: int
150150
'''
151151

152+
prefab_baseclass_template = '''
153+
class C{n}(Prefab):
154+
a: int
155+
b: int
156+
c: int
157+
d: int
158+
e: int
159+
'''
160+
152161
prefab_attribute_template = '''
153162
@prefab
154163
class C{n}:
@@ -183,6 +192,15 @@ class C{n}:
183192
C{n}.__init__, C{n}.__repr__, C{n}.__eq__
184193
'''
185194

195+
msgspec_template = '''
196+
class C{n}(Struct):
197+
a: int
198+
b: int
199+
c: int
200+
d: int
201+
e: int
202+
'''
203+
186204

187205
# Import Headings #
188206

@@ -194,7 +212,8 @@ class C{n}:
194212
cluegen_header = "from cluegen import Datum"
195213
dataklass_header = "from dataklasses import dataklass"
196214
slotclass_header = "from ducktools.classbuilder import slotclass, SlotFields, Field"
197-
prefab_header = "from ducktools.classbuilder.prefab import prefab, attribute, SlotFields"
215+
prefab_header = "from ducktools.classbuilder.prefab import prefab, attribute, Prefab, SlotFields"
216+
msgspec_header = "from msgspec import Struct"
198217

199218

200219
def write_perf_file(outpath, count, template, setup):
@@ -235,6 +254,8 @@ def write_classdef_file(self, count=100):
235254
TestData('native_classes', '', standard_template),
236255
TestData('slotclasses', slotclass_header, slotclass_template),
237256
TestData('prefab', prefab_header, prefab_template),
257+
TestData('prefab_baseclass', prefab_header, prefab_baseclass_template),
258+
TestData('prefab_attributes', prefab_header, prefab_attribute_template),
238259
TestData('prefab_slots', prefab_header, prefab_slots_template),
239260
TestData('prefab_eval', prefab_header, prefab_eval_template),
240261
TestData('namedtuples', namedtuple_header, namedtuple_template),
@@ -243,8 +264,7 @@ def write_classdef_file(self, count=100):
243264
TestData('attrs_noslots', attr_header, attr_noslots_template),
244265
TestData('attrs_slots', attr_header, attr_slots_template),
245266
TestData('pydantic', pydantic_header, pydantic_template),
246-
# TestData('cluegen', cluegen_header, cluegen_template),
247-
# TestData('cluegen_eval', cluegen_header, cluegen_eval_template),
267+
TestData('msgspec', msgspec_header, msgspec_template),
248268
]
249269

250270

@@ -297,6 +317,7 @@ def write_tests(*, runs=100, warmup=20, includes_pass=True):
297317
'''"python -c \\"from dataclasses import dataclass\\"" '''
298318
'''"python -c \\"from attrs import define\\"" '''
299319
'''"python -c \\"from pydantic import BaseModel\\"" '''
320+
'''"python -c \\"from msgspec import Struct\\"" '''
300321
)
301322

302323
import_script = (

perf/perf_profile.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ class C{n}:
6363
c: int
6464
d: int
6565
e: int
66-
67-
C{n}.__init__, C{n}.__repr__, C{n}.__eq__
6866
'''
6967

7068
attr_template = '''
@@ -75,8 +73,6 @@ class C{n}:
7573
c: int
7674
d: int
7775
e: int
78-
79-
C{n}.__init__, C{n}.__repr__, C{n}.__eq__
8076
'''
8177

8278
pydantic_template = '''
@@ -86,8 +82,15 @@ class C{n}(BaseModel):
8682
c: int
8783
d: int
8884
e: int
85+
'''
8986

90-
C{n}.__init__, C{n}.__repr__, C{n}.__eq__
87+
msgspec_template = '''
88+
class C{n}(Struct):
89+
a: int
90+
b: int
91+
c: int
92+
d: int
93+
e: int
9194
'''
9295

9396
cluegen_template = '''
@@ -262,6 +265,13 @@ def run_all_tests(reps, test_everything=False):
262265
except ImportError:
263266
print("pydantic not installed")
264267

268+
try:
269+
import msgspec # type: ignore
270+
write_perftemp(100, msgspec_template, "from msgspec import Struct")
271+
run_test(f"msgspec {msgspec.__version__}", reps)
272+
except ImportError:
273+
print("msgspec not installed")
274+
265275
write_perftemp(100, cluegen_template, 'from cluegen import Datum\n')
266276
run_test('dabeaz/cluegen', reps)
267277

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ dev = [
4343
performance = [
4444
"attrs>=25.0",
4545
"pydantic>=2.11",
46+
"msgspec>=0.20",
4647
]
4748

4849
[tool.setuptools.packages.find]

src/ducktools/classbuilder/__init__.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,6 @@ def build_completed(ns):
167167
return False
168168

169169

170-
def _get_inst_fields(inst):
171-
# This is an internal helper for constructing new
172-
# 'Field' instances from existing ones.
173-
return {
174-
k: getattr(inst, k)
175-
for k in get_fields(type(inst))
176-
}
177-
178-
179170
# As 'None' can be a meaningful value we need a sentinel value
180171
# to use to show no value has been provided.
181172
class _NothingType:
@@ -580,7 +571,7 @@ def frozen_delattr_generator(cls, funcname="__delattr__"):
580571
)
581572

582573

583-
def builder(cls=None, /, *, gatherer, methods, flags=None, fix_signature=True):
574+
def builder(cls=None, /, *, gatherer, methods, flags=None, fix_signature=True, field_getter=get_fields):
584575
"""
585576
The main builder for class generation
586577
@@ -598,6 +589,8 @@ def builder(cls=None, /, *, gatherer, methods, flags=None, fix_signature=True):
598589
:param fix_signature: Add a __signature__ attribute to work-around an issue with
599590
inspect.signature incorrectly handling __init__ descriptors.
600591
:type fix_signature: bool
592+
:param field_getter: function to use to retrieve fields from parent classes
593+
:type field_getter: Callable[[type], dict[str, Field]]
601594
:return: The modified class (the class itself is modified, but this is expected).
602595
"""
603596
# Handle `None` to make wrapping with a decorator easier.
@@ -642,7 +635,7 @@ def builder(cls=None, /, *, gatherer, methods, flags=None, fix_signature=True):
642635
fields = {}
643636
for c in reversed(mro):
644637
try:
645-
fields |= get_fields(c, local=True)
638+
fields |= field_getter(c, local=True)
646639
except (TypeError, KeyError):
647640
pass
648641

@@ -923,7 +916,11 @@ def from_field(cls, fld, /, **kwargs):
923916
:param kwargs: Additional keyword arguments for subclasses
924917
:return: new field subclass instance
925918
"""
926-
argument_dict = {**_get_inst_fields(fld), **kwargs}
919+
inst_fields = {
920+
k: getattr(fld, k)
921+
for k in get_fields(type(fld))
922+
}
923+
argument_dict = {**inst_fields, **kwargs}
927924

928925
return cls(**argument_dict)
929926

0 commit comments

Comments
 (0)