Skip to content

Commit

Permalink
added support for caching objects attributes to Type_Safe__Cache
Browse files Browse the repository at this point in the history
  • Loading branch information
DinisCruz committed Jan 20, 2025
1 parent 6bf8b78 commit adac08e
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 24 deletions.
19 changes: 18 additions & 1 deletion osbot_utils/type_safe/shared/Type_Safe__Cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@
from typing import get_origin
from weakref import WeakKeyDictionary
from osbot_utils.type_safe.shared.Type_Safe__Shared__Variables import IMMUTABLE_TYPES
from osbot_utils.utils.Objects import all_annotations__in_class
from osbot_utils.utils.Objects import all_annotations__in_class, all_annotations


class Type_Safe__Cache:

_cls__annotations_cache : WeakKeyDictionary
_cls__immutable_vars : WeakKeyDictionary
_cls__kwargs_cache : WeakKeyDictionary
_obj__annotations_cache : WeakKeyDictionary
_get_origin_cache : WeakKeyDictionary
_mro_cache : WeakKeyDictionary
_valid_vars_cache : WeakKeyDictionary

cache_hit__cls__annotations : int = 0
cache_hit__cls__kwargs : int = 0
cache_hit__cls__immutable_vars: int = 0
cache_hit__obj__annotations : int = 0
cache_hit__get_origin : int = 0
cache_hit__mro : int = 0
cache_hit__valid_vars : int = 0
Expand All @@ -28,6 +30,7 @@ def __init__(self):
self._cls__annotations_cache = WeakKeyDictionary() # Cache for class annotations
self._cls__immutable_vars = WeakKeyDictionary() # Cache for class immutable vars
self._cls__kwargs_cache = WeakKeyDictionary() # Cache for class kwargs
self._obj__annotations_cache = WeakKeyDictionary() # Cache for object annotations
self._get_origin_cache = WeakKeyDictionary() # Cache for get_origin results
self._mro_cache = WeakKeyDictionary() # Cache for Method Resolution Order
self._valid_vars_cache = WeakKeyDictionary()
Expand All @@ -39,6 +42,19 @@ def get_cls_kwargs(self, cls):
self.cache_hit__cls__kwargs += 1
return self._cls__kwargs_cache.get(cls)

def get_annotations(self, target):
if target is None:
return {}
annotations_key = target.__class__
annotations = self._obj__annotations_cache.get(annotations_key) # this is a more efficient cache retrieval pattern (we only get the data from the dict once)
if not annotations: # todo: apply this to the other cache getters
if self.skip_cache or annotations_key not in self._obj__annotations_cache:
annotations = dict(all_annotations(target).items())
self._obj__annotations_cache[annotations_key] = annotations
else:
self.cache_hit__obj__annotations += 1
return annotations

def get_class_annotations(self, cls):
annotations = self._cls__annotations_cache.get(cls) # this is a more efficient cache retrieval pattern (we only get the data from the dict once)
if not annotations: # todo: apply this to the other cache getters
Expand Down Expand Up @@ -97,6 +113,7 @@ def print_cache_hits(self):
print(f" annotations : {self.cache_hit__cls__annotations }")
print(f" cls__kwargs : {self.cache_hit__cls__kwargs }")
print(f" cls__immutable_vars: {self.cache_hit__cls__immutable_vars }")
print(f" obj__annotations : {self.cache_hit__obj__annotations }")
print(f" get_origin : {self.cache_hit__get_origin }")
print(f" mro : {self.cache_hit__mro }")
print(f" valid_vars : {self.cache_hit__valid_vars }")
Expand Down
19 changes: 2 additions & 17 deletions osbot_utils/type_safe/steps/Type_Safe__Step__Init.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,21 @@
from typing import get_origin, get_args, ForwardRef, Annotated, List, Tuple, Dict, Any, Optional, Type
from osbot_utils.type_safe.shared.Type_Safe__Validation import type_safe_validation
from osbot_utils.utils.Objects import all_annotations, obj_attribute_annotation


class Type_Safe__Step__Init:

# def check_obj_type_annotation_mismatch(self, target, attr_name, value):
# if self.value_type_matches_obj_annotation_for_attr(target, attr_name, value) is False: # handle case with normal types
# if self.value_type_matches_obj_annotation_for_union_and_annotated(target, attr_name, value) is False: # this is done like this because value_type_matches_obj_annotation_for_union_attr will return None when there is no Union objects
# # todo: check if this is still needed since there is no code coverage here
# type_safe_raise_exception.type_mismatch_error(attr_name, target.__annotations__.get(attr_name), type(value))
# #raise TypeError(f"Invalid type for attribute '{attr_name}'. Expected '{target.__annotations__.get(attr_name)}' but got '{type(value)}'")


def init(self, __self ,
__class_kwargs ,
**kwargs
) -> None:

for (key, value) in __class_kwargs.items(): # assign all default values to target
# if value is not None: # when the value is explicitly set to None on the class static vars, we can't check for type safety
# self.check_obj_type_annotation_mismatch(__self, key, value)
if hasattr(__self, key):
existing_value = getattr(__self, key)
if existing_value is not None:
setattr(__self, key, existing_value)
continue
setattr(__self, key, value)

for (key, value) in kwargs.items(): # overwrite with values provided in ctor
for (key, value) in kwargs.items(): # overwrite with values provided in ctor
if hasattr(__self, key):
if value is not None: # prevent None values from overwriting existing values, which is quite common in default constructors
if value is not None: # prevent None values from overwriting existing values, which is quite common in default constructors
setattr(__self, key, value)
else:
raise ValueError(f"{__self.__class__.__name__} has no attribute '{key}' and cannot be assigned the value '{value}'. "
Expand Down
4 changes: 2 additions & 2 deletions osbot_utils/type_safe/steps/Type_Safe__Step__Set_Attr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from osbot_utils.type_safe.shared.Type_Safe__Cache import type_safe_cache
from osbot_utils.type_safe.shared.Type_Safe__Convert import type_safe_convert
from osbot_utils.type_safe.shared.Type_Safe__Validation import type_safe_validation
from osbot_utils.utils.Objects import all_annotations
from osbot_utils.type_safe.validators.Type_Safe__Validator import Type_Safe__Validator

class Type_Safe__Step__Set_Attr:
Expand Down Expand Up @@ -74,7 +73,8 @@ def setattr(self, _super, _self, name, value):
if self.handle_special_generic_alias(_super, _self, name, value):
return

annotations = all_annotations(_self)
annotations = dict(type_safe_cache.get_annotations(_self))

if not annotations: # can't do type safety checks if the class does not have annotations
return _super.__setattr__(name, value)

Expand Down
2 changes: 0 additions & 2 deletions osbot_utils/utils/Status.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# todo refactor into Status class
import traceback

from osbot_utils.utils.Python_Logger import Python_Logger

class Status:
Expand Down
3 changes: 1 addition & 2 deletions tests/unit/helpers/ssh/test_SSH__Cache__Requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
from osbot_utils.base_classes.Kwargs_To_Self import Kwargs_To_Self
from osbot_utils.helpers.sqlite.cache.Sqlite__Cache__Requests import Sqlite__Cache__Requests
from osbot_utils.helpers.sqlite.cache.Sqlite__Cache__Requests__Patch import Sqlite__Cache__Requests__Patch
from osbot_utils.helpers.ssh.SSH__Cache__Requests import SSH__Cache__Requests, SQLITE_DB_NAME__SSH_REQUESTS_CACHE, \
SQLITE_TABLE_NAME__SSH_REQUESTS
from osbot_utils.helpers.ssh.SSH__Cache__Requests import SSH__Cache__Requests, SQLITE_DB_NAME__SSH_REQUESTS_CACHE, SQLITE_TABLE_NAME__SSH_REQUESTS
from osbot_utils.helpers.ssh.SSH__Execute import ENV_VAR__SSH__HOST, SSH__Execute
from osbot_utils.utils.Env import get_env
from osbot_utils.utils.Files import temp_file, current_temp_folder, parent_folder, file_extension, file_name
Expand Down

0 comments on commit adac08e

Please sign in to comment.