16
16
from collections .abc import Callable
17
17
import types
18
18
from inspect import isclass
19
+ from functools import lru_cache
19
20
from dataclasses import dataclass , InitVar
20
21
from .parsers .json import JSONContent
21
22
from .converters import encoders , parse_basic
@@ -155,7 +156,25 @@ class ModelMeta(type):
155
156
__aliases__ : Dict
156
157
__primary_keys__ : List
157
158
# Class-level cache
158
- _base_class_cache = {}
159
+ _base_class_cache = OrderedDict ()
160
+ _MAX_CACHE_SIZE = 512 # size limit
161
+
162
+ @classmethod
163
+ def _cache_get (cls , key ):
164
+ """Get item from cache with LRU behavior."""
165
+ if key not in cls ._base_class_cache :
166
+ return None
167
+ value = cls ._base_class_cache .pop (key )
168
+ cls ._base_class_cache [key ] = value
169
+ return value
170
+
171
+ @classmethod
172
+ def _cache_set (cls , key , value ):
173
+ """Set item in cache with LRU eviction."""
174
+ # If cache is full, remove oldest item (first in OrderedDict)
175
+ if len (cls ._base_class_cache ) >= cls ._MAX_CACHE_SIZE :
176
+ cls ._base_class_cache .popitem (last = False )
177
+ cls ._base_class_cache [key ] = value
159
178
160
179
@staticmethod
161
180
def _initialize_fields (attrs , annotations , strict ):
@@ -206,7 +225,7 @@ def _initialize_fields(attrs, annotations, strict):
206
225
df .type = _type
207
226
208
227
# Check for primary_key in field metadata
209
- if df .metadata .get ("primary_key " , False ):
228
+ if df .metadata .get ("primary " , False ) or df . primary_key is True :
210
229
primary_keys .append (field )
211
230
212
231
# Cache reflection info so we DON’T need to call
@@ -292,20 +311,27 @@ def _initialize_fields(attrs, annotations, strict):
292
311
293
312
def __new__ (cls , name , bases , attrs , ** kwargs ): # noqa
294
313
annotations = attrs .get ('__annotations__' , {})
295
- base_key = (tuple (bases ), tuple (sorted (annotations .items ())))
296
- strict = getattr (attrs .get ('Meta' , Meta ), 'strict' , False )
314
+ _strict_ = False
315
+ cols = OrderedDict ()
316
+
317
+ # Base class constructor
318
+ base_key = (name , tuple (bases ), tuple (sorted (annotations .items ())))
319
+ with contextlib .suppress (TypeError , AttributeError , KeyError ):
320
+ _strict_ = attrs ['Meta' ].strict
297
321
298
- if base_key in cls ._base_class_cache :
322
+ # Use LRU get method
323
+ cached = cls ._cache_get (base_key )
324
+ if cached :
325
+ # if base_key in cls._base_class_cache:
299
326
# Check the Cache First:
300
- cached = cls ._base_class_cache [base_key ]
327
+ # cached = cls._base_class_cache[base_key]
301
328
cols = cached ['cols' ].copy ()
302
329
_types = cached ['types' ].copy ()
303
330
_typing_args = cached ['_typing_args' ].copy ()
304
331
aliases = cached ['aliases' ].copy ()
305
332
primary_keys = cached ['primary_keys' ].copy ()
306
333
else :
307
334
# Compute field from Bases:
308
- cols = OrderedDict ()
309
335
_types = {}
310
336
_typing_args = {}
311
337
aliases = {}
@@ -325,7 +351,7 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
325
351
326
352
# Now initialize subclass-specific fields
327
353
new_cols , new_types , new_typing_args , new_aliases , nw_primary_keys = cls ._initialize_fields ( # noqa
328
- attrs , annotations , strict
354
+ attrs , annotations , _strict_
329
355
)
330
356
331
357
# Merge new fields with inherited fields
@@ -336,13 +362,21 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
336
362
primary_keys .extend (nw_primary_keys )
337
363
338
364
# Store computed results in cache
339
- cls ._base_class_cache [base_key ] = {
365
+ # cls._base_class_cache[base_key] = {
366
+ # 'cols': cols.copy(),
367
+ # 'types': _types.copy(),
368
+ # '_typing_args': _typing_args.copy(),
369
+ # 'aliases': aliases.copy(),
370
+ # 'primary_keys': primary_keys.copy(),
371
+ # }
372
+ cache_entry = {
340
373
'cols' : cols .copy (),
341
374
'types' : _types .copy (),
342
375
'_typing_args' : _typing_args .copy (),
343
376
'aliases' : aliases .copy (),
344
377
'primary_keys' : primary_keys .copy (),
345
378
}
379
+ cls ._cache_set (base_key , cache_entry )
346
380
347
381
_columns = cols .keys ()
348
382
cls .__slots__ = tuple (_columns )
@@ -388,7 +422,7 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
388
422
389
423
# Now that fields are in attrs, decorate the class as a dataclass
390
424
dc = dataclass (
391
- unsafe_hash = strict ,
425
+ unsafe_hash = _strict_ ,
392
426
repr = False ,
393
427
init = True ,
394
428
order = False ,
@@ -402,7 +436,7 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
402
436
dc .__valid__ = False
403
437
dc .__errors__ = {}
404
438
dc .__values__ = {}
405
- dc .__frozen__ = strict
439
+ dc .__frozen__ = _strict_
406
440
dc .__initialised__ = False
407
441
dc .__field_types__ = _types
408
442
dc .__aliases__ = aliases
0 commit comments