Skip to content

Commit 6a1cb09

Browse files
Merge pull request #253 from phenobarbital/new-exports
Test cache on model
2 parents 87d6105 + 4c1c474 commit 6a1cb09

File tree

4 files changed

+48
-14
lines changed

4 files changed

+48
-14
lines changed

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ recursive-include datamodel/rs_parsers *
1313

1414
# Exclude tests, settings, env, examples, and bin folders
1515
global-exclude *.pyc
16+
recursive-exclude datamodel/rs_parsers/target *
17+
1618
prune docs
1719
prune settings
1820
prune env

datamodel/abstract.py

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from collections.abc import Callable
1717
import types
1818
from inspect import isclass
19+
from functools import lru_cache
1920
from dataclasses import dataclass, InitVar
2021
from .parsers.json import JSONContent
2122
from .converters import encoders, parse_basic
@@ -155,7 +156,25 @@ class ModelMeta(type):
155156
__aliases__: Dict
156157
__primary_keys__: List
157158
# 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
159178

160179
@staticmethod
161180
def _initialize_fields(attrs, annotations, strict):
@@ -206,7 +225,7 @@ def _initialize_fields(attrs, annotations, strict):
206225
df.type = _type
207226

208227
# 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:
210229
primary_keys.append(field)
211230

212231
# Cache reflection info so we DON’T need to call
@@ -292,20 +311,27 @@ def _initialize_fields(attrs, annotations, strict):
292311

293312
def __new__(cls, name, bases, attrs, **kwargs): # noqa
294313
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
297321

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:
299326
# Check the Cache First:
300-
cached = cls._base_class_cache[base_key]
327+
# cached = cls._base_class_cache[base_key]
301328
cols = cached['cols'].copy()
302329
_types = cached['types'].copy()
303330
_typing_args = cached['_typing_args'].copy()
304331
aliases = cached['aliases'].copy()
305332
primary_keys = cached['primary_keys'].copy()
306333
else:
307334
# Compute field from Bases:
308-
cols = OrderedDict()
309335
_types = {}
310336
_typing_args = {}
311337
aliases = {}
@@ -325,7 +351,7 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
325351

326352
# Now initialize subclass-specific fields
327353
new_cols, new_types, new_typing_args, new_aliases, nw_primary_keys = cls._initialize_fields( # noqa
328-
attrs, annotations, strict
354+
attrs, annotations, _strict_
329355
)
330356

331357
# Merge new fields with inherited fields
@@ -336,13 +362,21 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
336362
primary_keys.extend(nw_primary_keys)
337363

338364
# 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 = {
340373
'cols': cols.copy(),
341374
'types': _types.copy(),
342375
'_typing_args': _typing_args.copy(),
343376
'aliases': aliases.copy(),
344377
'primary_keys': primary_keys.copy(),
345378
}
379+
cls._cache_set(base_key, cache_entry)
346380

347381
_columns = cols.keys()
348382
cls.__slots__ = tuple(_columns)
@@ -388,7 +422,7 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
388422

389423
# Now that fields are in attrs, decorate the class as a dataclass
390424
dc = dataclass(
391-
unsafe_hash=strict,
425+
unsafe_hash=_strict_,
392426
repr=False,
393427
init=True,
394428
order=False,
@@ -402,7 +436,7 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa
402436
dc.__valid__ = False
403437
dc.__errors__ = {}
404438
dc.__values__ = {}
405-
dc.__frozen__ = strict
439+
dc.__frozen__ = _strict_
406440
dc.__initialised__ = False
407441
dc.__field_types__ = _types
408442
dc.__aliases__ = aliases

datamodel/rs_parsers/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ use pyo3::wrap_pyfunction;
55
use pyo3::types::{PyDate, PyDateTime, PyAny, PyString, PyBool, PyBytes, PyInt, PyFloat, PyList};
66
use chrono::{Datelike, Timelike, NaiveDate, NaiveDateTime, DateTime, Utc};
77
use speedate::Date as SpeeDate;
8-
use std::sync::Mutex;
98
use speedate::DateTime as SpeeDateTime;
109
use uuid::Uuid;
1110
use rust_decimal::Decimal; // Rust Decimal crate
1211
use rust_decimal::prelude::FromStr;
13-
use rayon::prelude::*;
1412
// use speedate::{Date, DateTime, ParseError};
1513
// use std::collections::HashMap;
1614
// NaiveTime

datamodel/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
'simple library based on python +3.8 to use Dataclass-syntax'
77
'for interacting with Data'
88
)
9-
__version__ = '0.10.11'
9+
__version__ = '0.10.13'
1010
__copyright__ = 'Copyright (c) 2020-2024 Jesus Lara'
1111
__author__ = 'Jesus Lara'
1212
__author_email__ = '[email protected]'

0 commit comments

Comments
 (0)