Added ModifyConcept function, and fixed 'isa' not working

This commit is contained in:
2020-02-20 11:30:53 +01:00
parent 87f232b527
commit 7cd94e888f
17 changed files with 750 additions and 228 deletions
+27 -2
View File
@@ -1,6 +1,6 @@
import hashlib import hashlib
from collections import namedtuple from collections import namedtuple
from dataclasses import dataclass, field from dataclasses import dataclass
from enum import Enum from enum import Enum
from core.sheerka_logger import get_logger from core.sheerka_logger import get_logger
@@ -15,6 +15,7 @@ PROPERTIES_FOR_DIGEST = ("name", "key",
PROPERTIES_TO_SERIALIZE = PROPERTIES_FOR_DIGEST + tuple(["id"]) PROPERTIES_TO_SERIALIZE = PROPERTIES_FOR_DIGEST + tuple(["id"])
PROPERTIES_FOR_NEW = ("where", "pre", "post", "body", "desc") PROPERTIES_FOR_NEW = ("where", "pre", "post", "body", "desc")
VARIABLE_PREFIX = "__var__" VARIABLE_PREFIX = "__var__"
ORIGIN = "##origin##" # same as Serializer.ORIGIN but I don't want to include the reference
class ConceptParts(Enum): class ConceptParts(Enum):
@@ -97,6 +98,7 @@ class Concept:
self.bnf = None self.bnf = None
self.log = get_logger("core." + self.__class__.__name__) self.log = get_logger("core." + self.__class__.__name__)
self.init_log = get_logger("init.core." + self.__class__.__name__) self.init_log = get_logger("init.core." + self.__class__.__name__)
self.original_definition_hash = None
def __repr__(self): def __repr__(self):
return f"({self.metadata.id}){self.metadata.name}" return f"({self.metadata.id}){self.metadata.name}"
@@ -243,7 +245,19 @@ class Concept:
def body(self): def body(self):
return self.values[ConceptParts.BODY] if ConceptParts.BODY in self.values else None return self.values[ConceptParts.BODY] if ConceptParts.BODY in self.values else None
def get_digest(self): def get_origin(self):
"""
Return the digest used to save the concept if it exists
:return:
"""
if hasattr(self, ORIGIN):
return getattr(self, ORIGIN)
return None
def set_origin(self, origin):
setattr(self, ORIGIN, origin)
def get_definition_hash(self):
""" """
Returns the digest of the event Returns the digest of the event
:return: hexa form of the sha256 :return: hexa form of the sha256
@@ -301,6 +315,11 @@ class Concept:
for k, v in other.props.items(): for k, v in other.props.items():
self.set_prop(k, v.value) self.set_prop(k, v.value)
# origin
from sdp.sheerkaSerializer import Serializer
if hasattr(other, Serializer.ORIGIN):
setattr(self, Serializer.ORIGIN, getattr(other, Serializer.ORIGIN))
return self return self
def set_prop(self, prop_name, prop_value): def set_prop(self, prop_name, prop_value):
@@ -362,6 +381,12 @@ class Concept:
self.metadata.is_evaluated = True self.metadata.is_evaluated = True
return self return self
def freeze_definition_hash(self):
self.original_definition_hash = self.get_definition_hash()
def get_original_definition_hash(self):
return self.original_definition_hash
class Property: class Property:
""" """
@@ -1,6 +1,6 @@
from core.builtin_concepts import BuiltinConcepts, ErrorConcept from core.builtin_concepts import BuiltinConcepts, ErrorConcept
from core.concept import Concept from core.concept import Concept
from sdp.sheerkaDataProvider import SheerkaDataProviderDuplicateKeyError from sdp.sheerkaDataProvider import SheerkaDataProviderDuplicateKeyError, SheerkaDataProviderRef
CONCEPT_LEXER_PARSER_CLASS = "parsers.ConceptLexerParser.ConceptLexerParser" CONCEPT_LEXER_PARSER_CLASS = "parsers.ConceptLexerParser.ConceptLexerParser"
@@ -30,7 +30,7 @@ class SheerkaCreateNewConcept:
# checks for duplicate concepts # checks for duplicate concepts
# TODO checks if it exists in cache first # TODO checks if it exists in cache first
if self.sheerka.sdp.exists(self.sheerka.CONCEPTS_ENTRY, concept.key, concept.get_digest()): if self.sheerka.sdp.exists(self.sheerka.CONCEPTS_BY_HASH_ENTRY, concept.get_definition_hash()):
error = SheerkaDataProviderDuplicateKeyError(self.sheerka.CONCEPTS_ENTRY + "." + concept.key, concept) error = SheerkaDataProviderDuplicateKeyError(self.sheerka.CONCEPTS_ENTRY + "." + concept.key, concept)
return self.sheerka.ret( return self.sheerka.ret(
self.logger_name, self.logger_name,
@@ -55,22 +55,35 @@ class SheerkaCreateNewConcept:
if not init_ret_value.status: if not init_ret_value.status:
return self.sheerka.ret(self.logger_name, False, ErrorConcept(init_ret_value.value)) return self.sheerka.ret(self.logger_name, False, ErrorConcept(init_ret_value.value))
concept.freeze_definition_hash()
# save the new concept in sdp # save the new concept in sdp
concept.metadata.full_serialization = True
try: try:
# TODO : needs to make these calls atomic (or at least one single call) # TODO : needs to make these calls atomic (or at least one single call)
# save the new concept # save the new concept
self.sheerka.sdp.add( concept.metadata.full_serialization = True
result = self.sheerka.sdp.add(
context.event.get_digest(), context.event.get_digest(),
self.sheerka.CONCEPTS_ENTRY, self.sheerka.CONCEPTS_ENTRY,
concept, concept,
use_ref=True) use_ref=True)
concept.metadata.full_serialization = False
# update the concept (I hope that it's enough)
concept.set_origin(result.digest)
# save it by id # save it by id
self.sheerka.sdp.add( self.sheerka.sdp.add(
context.event.get_digest(), context.event.get_digest(),
self.sheerka.CONCEPTS_BY_ID_ENTRY, self.sheerka.CONCEPTS_BY_ID_ENTRY,
{concept.id: concept.get_digest()}, SheerkaDataProviderRef(concept.id, result.digest))
is_ref=True)
# records the hash
self.sheerka.sdp.add(
context.event.get_digest(),
self.sheerka.CONCEPTS_BY_HASH_ENTRY,
SheerkaDataProviderRef(concept.get_definition_hash(), result.digest))
# update the definition table # update the definition table
if concepts_definitions is not None: if concepts_definitions is not None:
self.sheerka.sdp.set( self.sheerka.sdp.set(
@@ -88,7 +101,7 @@ class SheerkaCreateNewConcept:
error.args[0]) error.args[0])
# Updates the caches # Updates the caches
concept.metadata.full_serialization = False
self.sheerka.cache_by_key[concept.key] = self.sheerka.sdp.get_safe(self.sheerka.CONCEPTS_ENTRY, concept.key) self.sheerka.cache_by_key[concept.key] = self.sheerka.sdp.get_safe(self.sheerka.CONCEPTS_ENTRY, concept.key)
self.sheerka.cache_by_id[concept.id] = concept self.sheerka.cache_by_id[concept.id] = concept
if init_ret_value is not None and init_ret_value.status: if init_ret_value is not None and init_ret_value.status:
+1 -1
View File
@@ -63,7 +63,7 @@ class SheerkaDump:
else: else:
self.sheerka.log.info("No property") self.sheerka.log.info("No property")
self.sheerka.log.info(f"digest : {c.get_digest()}") self.sheerka.log.info(f"digest : {c.get_origin()}")
if self.sheerka.isaset(context, c): if self.sheerka.isaset(context, c):
items = self.sheerka.get_set_elements(context, c) items = self.sheerka.get_set_elements(context, c)
@@ -1,4 +1,5 @@
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from sdp.sheerkaDataProvider import SheerkaDataProviderRef
class SheerkaModifyConcept: class SheerkaModifyConcept:
@@ -8,7 +9,41 @@ class SheerkaModifyConcept:
def modify_concept(self, context, concept): def modify_concept(self, context, concept):
self.sheerka.sdp.modify(context.event.get_digest(), self.sheerka.CONCEPTS_ENTRY, concept.key, concept) try:
# modify the entry
concept.metadata.full_serialization = True
result = self.sheerka.sdp.modify(
context.event.get_digest(),
self.sheerka.CONCEPTS_ENTRY,
concept.key,
concept)
concept.metadata.full_serialization = False
# update its reference
self.sheerka.sdp.modify(
context.event.get_digest(),
self.sheerka.CONCEPTS_BY_ID_ENTRY,
concept.id,
SheerkaDataProviderRef(concept.id, result.digest, concept.get_origin()))
# update the hash entry
self.sheerka.sdp.modify(
context.event.get_digest(),
self.sheerka.CONCEPTS_BY_HASH_ENTRY,
concept.get_original_definition_hash(),
SheerkaDataProviderRef(concept.get_definition_hash(), result.digest, concept.get_origin()))
except IndexError as error:
context.log_error(f"Failed to update concept '{concept}'.", who=self.logger_name)
return self.sheerka.ret(
self.logger_name,
False,
self.sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body=concept),
error.args[0])
# update cache
self.sheerka.cache_by_key[concept.key] = self.sheerka.sdp.get_safe(self.sheerka.CONCEPTS_ENTRY, concept.key)
self.sheerka.cache_by_id[concept.id] = concept
ret = self.sheerka.ret(self.logger_name, True, self.sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=concept)) ret = self.sheerka.ret(self.logger_name, True, self.sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=concept))
return ret return ret
@@ -48,8 +48,8 @@ class SheerkaSetsManager:
assert concept_set.id assert concept_set.id
try: try:
ret = self.sheerka.sdp.add_unique(context.event.get_digest(), GROUP_PREFIX + concept_set.id, concept.id) result = self.sheerka.sdp.add_unique(context.event.get_digest(), GROUP_PREFIX + concept_set.id, concept.id)
if ret == (None, None): # concept already in set if result.already_exists: # concept already in set
return self.sheerka.ret( return self.sheerka.ret(
self.logger_name, self.logger_name,
False, False,
+2
View File
@@ -34,6 +34,7 @@ class Sheerka(Concept):
CONCEPTS_ENTRY = "All_Concepts" # to store all the concepts CONCEPTS_ENTRY = "All_Concepts" # to store all the concepts
CONCEPTS_BY_ID_ENTRY = "Concepts_By_ID" CONCEPTS_BY_ID_ENTRY = "Concepts_By_ID"
CONCEPTS_BY_HASH_ENTRY = "Concepts_By_Hash" # store hash of concepts definitions (not values)
CONCEPTS_DEFINITIONS_ENTRY = "Concepts_Definitions" # to store definitions (bnf) of concepts CONCEPTS_DEFINITIONS_ENTRY = "Concepts_Definitions" # to store definitions (bnf) of concepts
BUILTIN_CONCEPTS_KEYS = "Builtins_Concepts" # sequential key for builtin concepts BUILTIN_CONCEPTS_KEYS = "Builtins_Concepts" # sequential key for builtin concepts
USER_CONCEPTS_KEYS = "User_Concepts" # sequential key for user defined concepts USER_CONCEPTS_KEYS = "User_Concepts" # sequential key for user defined concepts
@@ -482,6 +483,7 @@ class Sheerka(Concept):
# otherwise, create another instance # otherwise, create another instance
concept = self.builtin_cache[key]() if key in self.builtin_cache else Concept() concept = self.builtin_cache[key]() if key in self.builtin_cache else Concept()
concept.update_from(template) concept.update_from(template)
concept.freeze_definition_hash()
if len(kwargs) == 0: if len(kwargs) == 0:
return concept return concept
+93 -28
View File
@@ -1,3 +1,4 @@
from dataclasses import dataclass
from datetime import datetime, date from datetime import datetime, date
import hashlib import hashlib
import json import json
@@ -206,12 +207,32 @@ class State:
def modify_in_list(self, entry, key, obj, obj_key, obj_origin, load_ref_if_needed, save_ref_if_needed): def modify_in_list(self, entry, key, obj, obj_key, obj_origin, load_ref_if_needed, save_ref_if_needed):
found = False found = False
to_remove = None to_remove = None
new_digest = None
def _get_item_origin(o):
if hasattr(o, Serializer.ORIGIN):
return getattr(o, Serializer.ORIGIN)
if isinstance(o, dict) and Serializer.ORIGIN in o:
return o[Serializer.ORIGIN]
if hasattr(o, "get_digest"):
return o.get_digest()
if isinstance(o, str):
return o
return None
for i in range(len(self.data[entry][key])): for i in range(len(self.data[entry][key])):
item, is_ref = load_ref_if_needed(self.data[entry][key][i]) item, is_ref = load_ref_if_needed(self.data[entry][key][i])
if not hasattr(item, "get_digest"): item_origin = _get_item_origin(item)
if item_origin is None:
continue continue
if item.get_digest() == obj_origin: if item_origin == obj_origin:
obj = save_ref_if_needed(is_ref, obj) obj = save_ref_if_needed(is_ref, obj)
if is_ref:
new_digest = obj[len(SheerkaDataProvider.REF_PREFIX):]
if obj_key == key: if obj_key == key:
self.data[entry][key][i] = obj self.data[entry][key][i] = obj
else: else:
@@ -226,6 +247,8 @@ class State:
if to_remove is not None: if to_remove is not None:
del self.data[entry][key][to_remove] del self.data[entry][key][to_remove]
return new_digest
def remove(self, entry, filter): def remove(self, entry, filter):
if filter is None: if filter is None:
del (self.data[entry]) del (self.data[entry])
@@ -282,6 +305,28 @@ class SheerkaDataProviderDuplicateKeyError(Exception):
self.obj = obj self.obj = obj
@dataclass
class SheerkaDataProviderResult:
obj: object
entry: str
key: str
digest: str
already_exists: bool = False
@dataclass
class SheerkaDataProviderRef:
key: str
target: str
original_target: str = None
def get_digest(self):
return self.original_target
def get_key(self):
return self.key
class SheerkaDataProvider: class SheerkaDataProvider:
"""Manages the state of the system""" """Manages the state of the system"""
@@ -305,7 +350,6 @@ class SheerkaDataProvider:
self.serializer = Serializer() self.serializer = Serializer()
@staticmethod @staticmethod
def get_obj_key(obj): def get_obj_key(obj):
""" """
@@ -344,6 +388,9 @@ class SheerkaDataProvider:
if hasattr(obj, Serializer.ORIGIN): if hasattr(obj, Serializer.ORIGIN):
return getattr(obj, Serializer.ORIGIN) return getattr(obj, Serializer.ORIGIN)
if isinstance(obj, SheerkaDataProviderRef):
return obj.original_target
return None return None
@staticmethod @staticmethod
@@ -359,7 +406,7 @@ class SheerkaDataProvider:
def is_reference(obj): def is_reference(obj):
return isinstance(obj, str) and obj.startswith(SheerkaDataProvider.REF_PREFIX) return isinstance(obj, str) and obj.startswith(SheerkaDataProvider.REF_PREFIX)
def add(self, event_digest: str, entry, obj, allow_multiple=True, use_ref=False, is_ref=False): def add(self, event_digest: str, entry, obj, allow_multiple=True, use_ref=False):
""" """
Adds obj to the entry 'entry' Adds obj to the entry 'entry'
:param event_digest: digest of the event that triggers the modification of the state :param event_digest: digest of the event that triggers the modification of the state
@@ -372,11 +419,7 @@ class SheerkaDataProvider:
:return: (entry, key) to retrieve the object :return: (entry, key) to retrieve the object
""" """
if use_ref and is_ref: original_obj = obj.copy() if isinstance(obj, dict) else obj
raise SheerkaDataProviderError("Cannot use use_ref and is_ref at the same time", None)
if is_ref and not isinstance(obj, dict):
raise SheerkaDataProviderError("is_ref can only be used with dictionaries", obj)
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile) snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot) state = self.load_state(snapshot)
@@ -406,15 +449,11 @@ class SheerkaDataProvider:
obj.set_digest(self.save_obj(obj.obj)) obj.set_digest(self.save_obj(obj.obj))
obj.obj = self.REF_PREFIX + obj.get_digest() obj.obj = self.REF_PREFIX + obj.get_digest()
if is_ref:
for k, v in obj.obj.items():
obj.obj[k] = self.REF_PREFIX + v
state.update(entry, obj) state.update(entry, obj)
new_snapshot = self.save_state(state) new_snapshot = self.save_state(state)
self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot) self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot)
return entry, key return SheerkaDataProviderResult(original_obj, entry, obj.get_key(), obj.get_digest())
def add_with_auto_key(self, event_digest: str, entry, obj): def add_with_auto_key(self, event_digest: str, entry, obj):
""" """
@@ -424,14 +463,20 @@ class SheerkaDataProvider:
:param obj: :param obj:
:return: :return:
""" """
original_obj = obj.copy() if isinstance(obj, dict) else obj
next_key = self.get_next_key(entry) next_key = self.get_next_key(entry)
if hasattr(obj, "set_key"): if hasattr(obj, "set_key"):
obj.set_key(next_key) obj.set_key(next_key)
self.add(event_digest, entry, ObjToUpdate(obj, next_key)) res = self.add(event_digest, entry, ObjToUpdate(obj, next_key))
return entry, next_key return SheerkaDataProviderResult(original_obj, res.entry, res.key, res.digest)
def add_unique(self, event_digest: str, entry, obj): def add_unique(self, event_digest: str, entry, obj):
"""Add an entry and make sure it's unique""" """Add an entry and make sure it's unique"""
original_obj = obj.copy() if isinstance(obj, dict) else obj
snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile) snapshot = self.get_snapshot(SheerkaDataProvider.HeadFile)
state = self.load_state(snapshot) state = self.load_state(snapshot)
@@ -448,7 +493,12 @@ class SheerkaDataProvider:
new_snapshot = self.save_state(state) new_snapshot = self.save_state(state)
self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot) self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot)
return (None if already_exist else entry), None return SheerkaDataProviderResult(
original_obj,
entry,
None,
None,
already_exist)
def set(self, event_digest, entry, obj, use_ref=False, is_ref=False): def set(self, event_digest, entry, obj, use_ref=False, is_ref=False):
""" """
@@ -462,6 +512,8 @@ class SheerkaDataProvider:
:return: :return:
""" """
original_obj = obj.copy() if isinstance(obj, dict) else obj
if use_ref and is_ref: if use_ref and is_ref:
raise SheerkaDataProviderError("Cannot use use_ref and is_ref at the same time", None) raise SheerkaDataProviderError("Cannot use use_ref and is_ref at the same time", None)
@@ -486,7 +538,7 @@ class SheerkaDataProvider:
new_snapshot = self.save_state(state) new_snapshot = self.save_state(state)
self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot) self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot)
return entry, key return SheerkaDataProviderResult(original_obj, entry, key, self.get_obj_digest(obj))
def modify(self, event_digest, entry, key, obj): def modify(self, event_digest, entry, key, obj):
""" """
@@ -499,6 +551,8 @@ class SheerkaDataProvider:
:return: :return:
""" """
original_obj = obj.copy() if isinstance(obj, dict) else obj
if key is None: if key is None:
raise SheerkaDataProviderError("Key is mandatory.", None) raise SheerkaDataProviderError("Key is mandatory.", None)
@@ -517,21 +571,33 @@ class SheerkaDataProvider:
# Gets obj original key, it will help to know if the key has changed # Gets obj original key, it will help to know if the key has changed
obj_key = self.get_obj_key(obj) or key obj_key = self.get_obj_key(obj) or key
digest = None
if isinstance(state.data[entry][key], list): if isinstance(state.data[entry][key], list):
obj_origin = self.get_obj_origin(obj) obj_origin = self.get_obj_origin(obj)
if obj_origin is None: if obj_origin is None:
raise (SheerkaDataProviderError(f"Multiple entries under '{entry}.{key}'", obj)) raise (SheerkaDataProviderError(f"Multiple entries under '{entry}.{key}'", obj))
state.modify_in_list(entry, key, obj, obj_key, obj_origin, self.load_ref_if_needed, self.save_ref_if_needed) digest = state.modify_in_list(
entry,
key,
obj,
obj_key,
obj_origin,
self.load_ref_if_needed,
self.save_ref_if_needed)
else: else:
obj = self.save_ref_if_needed(self.is_reference(state.data[entry][key]), obj) was_saved_as_reference = self.is_reference(state.data[entry][key])
if was_saved_as_reference:
obj = self.save_ref_if_needed(True, obj)
digest = self.get_obj_digest(obj)
state.modify(entry, key, obj, obj_key) state.modify(entry, key, obj, obj_key)
new_snapshot = self.save_state(state) new_snapshot = self.save_state(state)
self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot) self.set_snapshot(SheerkaDataProvider.HeadFile, new_snapshot)
return entry, obj_key return SheerkaDataProviderResult(original_obj, entry, obj_key, digest)
def list(self, entry, filter=None): def list(self, entry, filter=None):
""" """
@@ -814,16 +880,15 @@ class SheerkaDataProvider:
return obj return obj
def load_ref_if_needed(self, obj, load_origin=True): def load_ref_if_needed(self, obj, load_origin=True):
if not isinstance(obj, str): if isinstance(obj, SheerkaDataProviderRef):
return obj, False resolved = self.load_obj(obj.target, load_origin)
if not obj.startswith(SheerkaDataProvider.REF_PREFIX): return resolved, False
if not isinstance(obj, str) or not obj.startswith(SheerkaDataProvider.REF_PREFIX):
return obj, False return obj, False
resolved = self.load_obj(obj[len(SheerkaDataProvider.REF_PREFIX):], load_origin) resolved = self.load_obj(obj[len(SheerkaDataProvider.REF_PREFIX):], load_origin)
if resolved is None: return (obj, False) if resolved is None else (resolved, True)
return obj, False
return resolved, True
def save_ref_if_needed(self, save_ref, obj): def save_ref_if_needed(self, save_ref, obj):
if not save_ref: if not save_ref:
+2 -1
View File
@@ -5,7 +5,8 @@ from sheerkapickle import tags, utils, handlers
def decode(sheerka, obj): def decode(sheerka, obj):
return SheerkaUnpickler(sheerka).restore(json.loads(obj)) decoded = SheerkaUnpickler(sheerka).restore(json.loads(obj))
return decoded
class SheerkaUnpickler: class SheerkaUnpickler:
+3
View File
@@ -74,6 +74,7 @@ class ConceptHandler(BaseHandler):
# get value # get value
instance.set_metadata_value(ConceptParts(key), resolved_value) instance.set_metadata_value(ConceptParts(key), resolved_value)
instance.freeze_definition_hash()
return instance return instance
@@ -92,6 +93,7 @@ class UserInputHandler(ConceptHandler):
instance.__init__(data["text"], data["user_name"]) instance.__init__(data["text"], data["user_name"])
instance.metadata.key = data[CONCEPT_ID][0] instance.metadata.key = data[CONCEPT_ID][0]
instance.metadata.id = data[CONCEPT_ID][1] instance.metadata.id = data[CONCEPT_ID][1]
instance.freeze_definition_hash()
return instance return instance
@@ -122,6 +124,7 @@ class ReturnValueHandler(BaseHandler):
instance.metadata.key = data[CONCEPT_ID][0] instance.metadata.key = data[CONCEPT_ID][0]
instance.metadata.id = data[CONCEPT_ID][1] instance.metadata.id = data[CONCEPT_ID][1]
instance.freeze_definition_hash()
return instance return instance
+7 -4
View File
@@ -36,23 +36,26 @@ class BaseTest:
def init_concepts(self, *concepts, **kwargs): def init_concepts(self, *concepts, **kwargs):
sheerka = self.get_sheerka(**kwargs) sheerka = self.get_sheerka(**kwargs)
context = self.get_context(sheerka) context = self.get_context(sheerka)
create_new = kwargs.get("create_new", False)
result = [] result = []
for c in concepts: for c in concepts:
if isinstance(c, str): if isinstance(c, str):
c = Concept(c) c = Concept(c)
c.init_key()
sheerka.set_id_if_needed(c, False)
# manage concepts with bnf definitions
if c.metadata.definition: if c.metadata.definition:
bnf_parser = BnfParser() bnf_parser = BnfParser()
res = bnf_parser.parse(context, c.metadata.definition) res = bnf_parser.parse(context, c.metadata.definition)
if res.status: if res.status:
c.bnf = res.value.value c.bnf = res.value.value
sheerka.create_new_concept(context, c) sheerka.create_new_concept(context, c)
elif create_new:
sheerka.create_new_concept(context, c)
else:
c.init_key()
sheerka.set_id_if_needed(c, False)
sheerka.add_in_cache(c)
sheerka.add_in_cache(c)
result.append(c) result.append(c)
return sheerka, context, *result return sheerka, context, *result
+18 -2
View File
@@ -1,5 +1,6 @@
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import PROPERTIES_TO_SERIALIZE, Concept from core.concept import PROPERTIES_TO_SERIALIZE, Concept
from core.sheerka.Sheerka import Sheerka
from sdp.sheerkaDataProvider import SheerkaDataProvider from sdp.sheerkaDataProvider import SheerkaDataProvider
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@@ -25,7 +26,10 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka):
assert concept.key in sheerka.cache_by_key assert concept.key in sheerka.cache_by_key
assert concept.id in sheerka.cache_by_id assert concept.id in sheerka.cache_by_id
assert sheerka.sdp.io.exists( assert sheerka.sdp.io.exists(
sheerka.sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_found.get_digest())) sheerka.sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_found.get_origin()))
assert sheerka.sdp.exists(Sheerka.CONCEPTS_BY_HASH_ENTRY, concept.get_definition_hash())
assert sheerka.sdp.exists(Sheerka.CONCEPTS_BY_ID_ENTRY, concept.id)
assert sheerka.sdp.exists(Sheerka.CONCEPTS_ENTRY, concept.key)
def test_i_cannot_add_the_same_concept_twice(self): def test_i_cannot_add_the_same_concept_twice(self):
""" """
@@ -77,7 +81,7 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka):
concept = self.get_default_concept() concept = self.get_default_concept()
sheerka.create_new_concept(self.get_context(sheerka), concept) sheerka.create_new_concept(self.get_context(sheerka), concept)
sheerka.cache_by_key = {} # reset the cache sheerka.reset_cache()
loaded = sheerka.get(concept.key) loaded = sheerka.get(concept.key)
assert loaded == concept assert loaded == concept
@@ -86,6 +90,16 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka):
loaded = sheerka.sdp.get(sheerka.CONCEPTS_BY_ID_ENTRY, concept.id) loaded = sheerka.sdp.get(sheerka.CONCEPTS_BY_ID_ENTRY, concept.id)
assert loaded == concept assert loaded == concept
def test_i_can_instantiate_a_concept_from_sdp(self):
sheerka = self.get_sheerka()
concept = Concept("foo")
sheerka.create_new_concept(self.get_context(sheerka), concept)
sheerka.reset_cache()
loaded = sheerka.new("foo")
assert loaded == concept
def test_i_can_get_a_concept_by_its_id(self): def test_i_can_get_a_concept_by_its_id(self):
sheerka = self.get_sheerka() sheerka = self.get_sheerka()
concept = self.get_default_concept() concept = self.get_default_concept()
@@ -105,6 +119,8 @@ class TestSheerkaCreateNewConcept(TestUsingMemoryBasedSheerka):
res1 = sheerka.create_new_concept(self.get_context(sheerka), concept1) res1 = sheerka.create_new_concept(self.get_context(sheerka), concept1)
res2 = sheerka.create_new_concept(self.get_context(sheerka), concept2) res2 = sheerka.create_new_concept(self.get_context(sheerka), concept2)
assert res1.status
assert res2.status
assert res1.value.body.key == res2.value.body.key # same key assert res1.value.body.key == res2.value.body.key # same key
sheerka.cache_by_key = {} # reset the cache sheerka.cache_by_key = {} # reset the cache
+104
View File
@@ -0,0 +1,104 @@
import pytest
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept, ConceptParts
from core.sheerka.Sheerka import Sheerka
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestSheerkaModifyConcept(TestUsingMemoryBasedSheerka):
def test_i_can_modify_a_concept(self):
sheerka, context, foo, bar = self.init_concepts("foo", "bar", create_new=True)
foo_instance = sheerka.new("foo")
foo_instance.metadata.body = "value"
foo_instance.set_prop(BuiltinConcepts.ISA, bar)
foo_instance.set_metadata_value(ConceptParts.BODY, "body value")
res = sheerka.modify_concept(context, foo_instance)
assert res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.NEW_CONCEPT)
assert res.body.body.metadata.body == "value"
assert res.body.body.get_prop(BuiltinConcepts.ISA) == bar
assert res.body.body.body == "body value"
# test that object
sheerka.reset_cache()
foo_from_sheerka = sheerka.new("foo")
assert foo_from_sheerka.metadata.body == "value"
assert foo_from_sheerka.get_prop(BuiltinConcepts.ISA) == bar
assert foo_from_sheerka.body == "body value"
# test that ref by id is updated
sheerka.reset_cache()
foo_from_sheerka = sheerka.get_by_id(foo.id)
assert foo_from_sheerka.metadata.body == "value"
assert foo_from_sheerka.get_prop(BuiltinConcepts.ISA) == bar
assert foo_from_sheerka.body == "body value"
# test that ref by hash is updated
foo_from_sdp = sheerka.sdp.get(Sheerka.CONCEPTS_BY_HASH_ENTRY, foo_instance.get_definition_hash())
assert foo_from_sdp.metadata.body == "value"
assert foo_from_sdp.get_prop(BuiltinConcepts.ISA) == bar
assert foo_from_sdp.body == "body value"
# previous ref by hash is removed (since that definition hash has changed)
with pytest.raises(IndexError):
sheerka.sdp.get(Sheerka.CONCEPTS_BY_HASH_ENTRY, foo_instance.get_original_definition_hash())
def test_i_can_modify_concept_modifying_only_properties_and_body(self):
sheerka, context, foo, bar = self.init_concepts("foo", "bar", create_new=True)
foo_instance = sheerka.new("foo")
foo_instance.set_prop(BuiltinConcepts.ISA, bar)
foo_instance.set_metadata_value(ConceptParts.BODY, "body value")
res = sheerka.modify_concept(context, foo_instance)
assert res.status
foo_from_sheerka = sheerka.new("foo")
assert foo_from_sheerka.get_prop(BuiltinConcepts.ISA) == bar
assert foo_from_sheerka.body == "body value"
def test_cache_is_updated_when_a_concept_is_modified(self):
sheerka, context, foo = self.init_concepts("foo", create_new=True)
foo_instance = sheerka.new("foo")
foo_instance.metadata.body = "value"
res = sheerka.modify_concept(context, foo_instance)
assert res.status
foo_from_sheerka = sheerka.get("foo")
assert foo_from_sheerka.metadata.body == "value"
foo_by_id_from_sheerka = sheerka.get_by_id(foo.id)
assert foo_by_id_from_sheerka.metadata.body == "value"
def test_i_cannot_modify_a_concept_that_does_not_exists(self):
sheerka, context, foo = self.init_concepts("foo", create_new=False)
foo_instance = sheerka.new("foo")
foo_instance.metadata.body = "value"
res = sheerka.modify_concept(context, foo_instance)
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.UNKNOWN_CONCEPT)
assert res.body.body.key == foo.key
def test_i_can_modify_a_concept_that_is_in_a_list(self):
sheerka, context, foo1, foo2 = self.init_concepts(
Concept("foo", body="1"),
Concept("foo", body="2"), create_new=True)
foo2_instance = sheerka.new("foo")[1]
foo2_instance.metadata.body = "value"
res = sheerka.modify_concept(context, foo2_instance)
assert res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.NEW_CONCEPT)
assert res.body.body.metadata.body == "value"
sheerka.reset_cache()
foo_from_sheerka = sheerka.new("foo")
assert foo_from_sheerka[0].metadata.body == "1"
assert foo_from_sheerka[1].metadata.body == "value"
+4 -2
View File
@@ -200,8 +200,10 @@ class TestSheerkaSetsManager(TestUsingFileBasedSheerka):
assert sheerka.isinset(twenty_one, number) assert sheerka.isinset(twenty_one, number)
def test_i_can_set_isa(self): def test_i_can_set_isa(self):
sheerka, context, foo, all_foos = self.init_concepts(Concept("foo"), Concept("all_foo"), use_dict=False) sheerka, context, foo, all_foos = self.init_concepts(
sheerka.create_new_concept(context, foo) "foo", "all_foo",
create_new=True,
use_dict=False)
assert BuiltinConcepts.ISA not in foo.props assert BuiltinConcepts.ISA not in foo.props
+4
View File
@@ -198,6 +198,9 @@ def test_i_can_update_from():
id="123456" id="123456"
).def_prop("a", "10").def_prop("b", None) ).def_prop("a", "10").def_prop("b", None)
# make sure origin is preserved
setattr(template, "##origin##", "digest")
template.values[ConceptParts.BODY] = "value in body" template.values[ConceptParts.BODY] = "value in body"
template.values[ConceptParts.WHERE] = "value in where" template.values[ConceptParts.WHERE] = "value in where"
template.values[ConceptParts.PRE] = "value in pre" template.values[ConceptParts.PRE] = "value in pre"
@@ -208,3 +211,4 @@ def test_i_can_update_from():
concept = Concept().update_from(template) concept = Concept().update_from(template)
assert concept == template assert concept == template
assert getattr(concept, "##origin##") == "digest"
@@ -51,18 +51,20 @@ class TestAddConceptInSetEvaluator(TestUsingMemoryBasedSheerka):
assert res.value.body == "bar" assert res.value.body == "bar"
def test_i_can_add_concept_to_a_set_of_concept(self): def test_i_can_add_concept_to_a_set_of_concept(self):
context = self.get_context() sheerka, context, foo, bar = self.init_concepts("foo", "bar", create_new=True)
foo = Concept("foo")
context.sheerka.create_new_concept(context, foo)
bar = Concept("bar")
context.sheerka.create_new_concept(context, bar)
ret_val = get_ret_val("foo", "bar") ret_val = get_ret_val("foo", "bar")
res = AddConceptInSetEvaluator().eval(context, ret_val) res = AddConceptInSetEvaluator().eval(context, ret_val)
foo = sheerka.new("foo") # reload it
assert res.status assert res.status
assert context.sheerka.isinstance(res.value, BuiltinConcepts.SUCCESS) assert context.sheerka.isinstance(res.value, BuiltinConcepts.SUCCESS)
assert context.sheerka.isaset(context, bar)
assert context.sheerka.isinset(foo, bar)
assert context.sheerka.isa(foo, bar)
foo_from_sheerka = context.sheerka.get("foo")
assert foo_from_sheerka.get_prop(BuiltinConcepts.ISA) == [bar]
def test_i_can_add_concept_with_a_body_to_a_set_of_concept(self): def test_i_can_add_concept_with_a_body_to_a_set_of_concept(self):
context = self.get_context() context = self.get_context()
+2 -2
View File
@@ -105,7 +105,7 @@ as:
assert concept_saved.key in sheerka.cache_by_key assert concept_saved.key in sheerka.cache_by_key
assert concept_saved.id in sheerka.cache_by_id assert concept_saved.id in sheerka.cache_by_id
assert sheerka.sdp.io.exists( assert sheerka.sdp.io.exists(
sheerka.sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_digest())) sheerka.sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_origin()))
def test_i_can_eval_def_concept_part_when_one_part_is_a_ref_of_another_concept(self): def test_i_can_eval_def_concept_part_when_one_part_is_a_ref_of_another_concept(self):
""" """
@@ -135,7 +135,7 @@ as:
assert concept_saved.key in sheerka.cache_by_key assert concept_saved.key in sheerka.cache_by_key
assert concept_saved.id in sheerka.cache_by_id assert concept_saved.id in sheerka.cache_by_id
assert sheerka.sdp.io.exists( assert sheerka.sdp.io.exists(
sheerka.sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_digest())) sheerka.sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_origin()))
def test_i_cannot_eval_the_same_def_concept_twice(self): def test_i_cannot_eval_the_same_def_concept_twice(self):
text = """ text = """
+416 -169
View File
@@ -4,7 +4,7 @@ import pytest
import os import os
from os import path from os import path
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderError, \ from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderError, \
SheerkaDataProviderDuplicateKeyError SheerkaDataProviderDuplicateKeyError, SheerkaDataProviderResult, SheerkaDataProviderRef
from datetime import date, datetime from datetime import date, datetime
import shutil import shutil
import json import json
@@ -123,6 +123,36 @@ class ObjDumpJson:
self.key = as_dict["key"] self.key = as_dict["key"]
class ObjDumpJsonNoDigest:
"""
Object where the key can be resolved using get_key()
that can be used to dump as Json,
But with no builtin digest computation
"""
def __init__(self, key=None, value=None):
self.key = key
self.value = value
def __eq__(self, obj):
return isinstance(obj, ObjDumpJsonNoDigest) and \
self.key == obj.key and \
self.value == obj.value
def __repr__(self):
return f"ObjDumpJsonNoDigest({self.key}, {self.value})"
def get_key(self):
return self.key
def to_dict(self):
return self.__dict__
def from_dict(self, as_dict):
self.value = as_dict["value"]
self.key = as_dict["key"]
class ObjWithDigestNoKey: class ObjWithDigestNoKey:
""" """
Object that can compute its digest. Object that can compute its digest.
@@ -301,13 +331,15 @@ def test_i_can_add_an_string(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
obj = "foo => bar" obj = "foo => bar"
entry, key = sdp.add(evt_digest, "entry", obj) result = sdp.add(evt_digest, "entry", obj)
last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile) last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile)
state = sdp.load_state(last_commit) state = sdp.load_state(last_commit)
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
assert entry == "entry" assert result.obj == obj
assert key is None assert result.entry == "entry"
assert result.key is None
assert result.digest is None
assert loaded == obj assert loaded == obj
assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.StateFolder, last_commit[0:24], last_commit)) assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.StateFolder, last_commit[0:24], last_commit))
@@ -330,11 +362,13 @@ def test_i_can_add_several_strings_if_allow_multiple_is_true(root):
sdp.add(evt_digest, "entry", "foo") sdp.add(evt_digest, "entry", "foo")
sdp.add(evt_digest, "entry", "foo") sdp.add(evt_digest, "entry", "foo")
entry, key = sdp.add(evt_digest, "entry", "bar") result = sdp.add(evt_digest, "entry", "bar")
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
assert entry == "entry" assert result.obj == "bar"
assert key is None assert result.entry == "entry"
assert result.key is None
assert result.digest is None
assert loaded == ["foo", "foo", "bar"] assert loaded == ["foo", "foo", "bar"]
@@ -359,14 +393,15 @@ def test_i_can_add_an_object_with_no_key(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
obj = ObjNoKey("a", "b") obj = ObjNoKey("a", "b")
entry, key = sdp.add(evt_digest, "entry", obj) result = sdp.add(evt_digest, "entry", obj)
last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile) last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile)
state = sdp.load_state(last_commit) state = sdp.load_state(last_commit)
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
assert entry == "entry" assert result.obj == obj
assert key is None assert result.entry == "entry"
assert loaded == obj assert result.key is None
assert result.digest is None
assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.StateFolder, last_commit[0:24], last_commit)) assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.StateFolder, last_commit[0:24], last_commit))
assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.HeadFile)) assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.HeadFile))
@@ -388,11 +423,13 @@ def test_i_can_add_several_obj_no_key_if_allow_multiple_is_true(root):
sdp.add(evt_digest, "entry", ObjNoKey("a", "b")) sdp.add(evt_digest, "entry", ObjNoKey("a", "b"))
sdp.add(evt_digest, "entry", ObjNoKey("a", "b")) sdp.add(evt_digest, "entry", ObjNoKey("a", "b"))
entry, key = sdp.add(evt_digest, "entry", ObjNoKey("c", "d")) result = sdp.add(evt_digest, "entry", ObjNoKey("c", "d"))
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
assert entry == "entry" assert result.obj == ObjNoKey("c", "d")
assert key is None assert result.entry == "entry"
assert result.key is None
assert result.digest is None
assert loaded == [ObjNoKey("a", "b"), ObjNoKey("a", "b"), ObjNoKey("c", "d")] assert loaded == [ObjNoKey("a", "b"), ObjNoKey("a", "b"), ObjNoKey("c", "d")]
@@ -429,15 +466,18 @@ def test_i_can_add_a_dict(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
obj = {"my_key": "my_value"} obj = {"my_key": "my_value"}
entry, key = sdp.add(evt_digest, "entry", obj) result = sdp.add(evt_digest, "entry", obj)
last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile) last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile)
state = sdp.load_state(last_commit) state = sdp.load_state(last_commit)
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
loaded_value = sdp.get(entry, "my_key") # we can retrieve by key loaded_value = sdp.get(result.entry, "my_key") # we can retrieve by key
assert result.obj == obj
assert result.entry == "entry"
assert result.key is None # we return None as dict may contains several entries
assert result.digest is None
assert entry == "entry"
assert key is None # we return None as dict may contains several entries
assert loaded == obj assert loaded == obj
assert loaded_value == "my_value" assert loaded_value == "my_value"
@@ -458,11 +498,17 @@ def test_i_can_add_a_dict(root):
]) ])
def test_i_can_add_multiple_entries_at_once_with_dict(root): def test_i_can_add_multiple_entries_at_once_with_dict(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
obj = {"my_key1": "value1", "my_key2": "value2"}
entry, key = sdp.add(evt_digest, "entry", {"my_key1": "value1", "my_key2": "value2"}) result = sdp.add(evt_digest, "entry", obj)
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
loaded_value1 = sdp.get(entry, "my_key1") loaded_value1 = sdp.get(result.entry, "my_key1")
loaded_value2 = sdp.get(entry, "my_key2") loaded_value2 = sdp.get(result.entry, "my_key2")
assert result.obj == obj
assert result.entry == "entry"
assert result.key is None # we return None as dict may contains several entries
assert result.digest is None
assert loaded == {"my_key1": "value1", "my_key2": "value2"} assert loaded == {"my_key1": "value1", "my_key2": "value2"}
assert loaded_value1 == "value1" assert loaded_value1 == "value1"
@@ -477,14 +523,14 @@ def test_i_can_add_same_key_with_dict_if_allow_multiple_is_true(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
sdp.add(evt_digest, "entry", {"my_key": "my_value"}) sdp.add(evt_digest, "entry", {"my_key": "my_value"})
entry, key = sdp.add(evt_digest, "entry", {"my_key": "my_value"}) result = sdp.add(evt_digest, "entry", {"my_key": "my_value"})
loaded1 = sdp.get(entry, key) loaded1 = sdp.get(result.entry, result.key)
entry, key = sdp.add(evt_digest, "entry", {"my_key": "my_value2"}) result = sdp.add(evt_digest, "entry", {"my_key": "my_value2"})
loaded2 = sdp.get(entry, key) loaded2 = sdp.get(result.entry, result.key)
assert entry == "entry" assert result.entry == "entry"
assert key is None assert result.key is None
assert loaded1 == {"my_key": ["my_value", "my_value"]} assert loaded1 == {"my_key": ["my_value", "my_value"]}
assert loaded2 == {"my_key": ["my_value", "my_value", "my_value2"]} assert loaded2 == {"my_key": ["my_value", "my_value", "my_value2"]}
@@ -525,19 +571,25 @@ def test_i_can_add_obj_with_key(root):
obj1 = ObjWithKey("key1", "b") obj1 = ObjWithKey("key1", "b")
obj2 = ObjSetKey("c", key="key2") obj2 = ObjSetKey("c", key="key2")
entry1, key1 = sdp.add(evt_digest, "entry", obj1) # test when key is taken from obj.get_key() result1 = sdp.add(evt_digest, "entry", obj1) # test when key is taken from obj.get_key()
entry2, key2 = sdp.add(evt_digest, "entry2", obj2) # test when key is taken from obj.key result2 = sdp.add(evt_digest, "entry2", obj2) # test when key is taken from obj.key
last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile) last_commit = sdp.get_snapshot(SheerkaDataProvider.HeadFile)
state = sdp.load_state(last_commit) state = sdp.load_state(last_commit)
loaded1 = sdp.get(entry1, key1) loaded1 = sdp.get(result1.entry, result1.key)
loaded2 = sdp.get(entry2, key2) loaded2 = sdp.get(result2.entry, result2.key)
assert result1.obj == obj1
assert result1.entry == "entry"
assert result1.key == "key1"
assert result1.digest is None
assert result2.obj == obj2
assert result2.entry == "entry2"
assert result2.key == "key2"
assert result2.digest is None
assert entry1 == "entry"
assert key1 == "key1"
assert loaded1 == ObjWithKey("key1", "b") assert loaded1 == ObjWithKey("key1", "b")
assert entry2 == "entry2"
assert key2 == "key2"
assert loaded2 == ObjSetKey("c", key="key2") assert loaded2 == ObjSetKey("c", key="key2")
assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.StateFolder, last_commit[0:24], last_commit)) assert sdp.io.exists(path.join(sdp.io.root, SheerkaDataProvider.StateFolder, last_commit[0:24], last_commit))
@@ -559,15 +611,13 @@ def test_i_can_add_objects_with_same_key_if_allow_multiple_is_true(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
sdp.add(evt_digest, "entry", ObjWithKey("my_key", "b")) sdp.add(evt_digest, "entry", ObjWithKey("my_key", "b"))
entry, key = sdp.add(evt_digest, "entry", ObjSetKey("c", key="my_key")) result = sdp.add(evt_digest, "entry", ObjSetKey("c", key="my_key"))
loaded1 = sdp.get(entry, key) loaded1 = sdp.get(result.entry, result.key)
entry, key = sdp.add(evt_digest, "entry", ObjSetKey("c", key="my_key")) result = sdp.add(evt_digest, "entry", ObjSetKey("c", key="my_key"))
sdp.add(evt_digest, "entry", ObjSetKey("c", key="my_key2")) # to prove that it does not melt everything sdp.add(evt_digest, "entry", ObjSetKey("c", key="my_key2")) # to prove that it does not melt everything
loaded2 = sdp.get(entry, key) loaded2 = sdp.get(result.entry, result.key)
assert entry == "entry"
assert key == "my_key"
assert loaded1 == [ObjWithKey("my_key", "b"), ObjSetKey("c", key="my_key")] assert loaded1 == [ObjWithKey("my_key", "b"), ObjSetKey("c", key="my_key")]
assert loaded2 == [ObjWithKey("my_key", "b"), ObjSetKey("c", key="my_key"), ObjSetKey("c", key="my_key")] assert loaded2 == [ObjWithKey("my_key", "b"), ObjSetKey("c", key="my_key"), ObjSetKey("c", key="my_key")]
@@ -608,13 +658,23 @@ def test_i_can_add_a_reference(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithDigestWithKey))) sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithDigestWithKey)))
obj1 = ObjWithDigestWithKey(1, "foo") obj1 = ObjWithDigestWithKey(1, "foo")
sdp.add(evt_digest, "entry", obj1, use_ref=True) result1 = sdp.add(evt_digest, "entry", obj1, use_ref=True)
sdp.add(evt_digest, "entry_by_value", {obj1.b: obj1.get_digest()}, is_ref=True) result3 = sdp.add(evt_digest, "entry_by_ref", SheerkaDataProviderRef(obj1.b, obj1.get_digest()))
# another object # another object
obj2 = ObjWithDigestWithKey(2, "bar") obj2 = ObjWithDigestWithKey(2, "bar")
sdp.add(evt_digest, "entry", obj2, use_ref=True) sdp.add(evt_digest, "entry", obj2, use_ref=True)
sdp.add(evt_digest, "entry_by_value", {obj2.b: obj2.get_digest()}, is_ref=True) sdp.add(evt_digest, "entry_by_ref", SheerkaDataProviderRef(obj2.b, obj2.get_digest()))
assert result1.obj == obj1
assert result1.entry == "entry"
assert result1.key == str(obj1.get_key())
assert result1.digest == obj1.get_digest()
assert result3.obj == SheerkaDataProviderRef(obj1.b, obj1.get_digest())
assert result3.entry == "entry_by_ref"
assert result3.key == "foo"
assert result3.digest is None
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == { assert state.data == {
@@ -622,22 +682,47 @@ def test_i_can_add_a_reference(root):
"1": '##REF##:' + obj1.get_digest(), "1": '##REF##:' + obj1.get_digest(),
"2": '##REF##:' + obj2.get_digest(), "2": '##REF##:' + obj2.get_digest(),
}, },
"entry_by_value": { "entry_by_ref": {
"foo": '##REF##:' + obj1.get_digest(), "foo": SheerkaDataProviderRef(obj1.b, obj1.get_digest()),
"bar": '##REF##:' + obj2.get_digest() "bar": SheerkaDataProviderRef(obj2.b, obj2.get_digest())
}, },
} }
# sanity check, make sure that I can load back # make sure that I can load back
loaded1 = sdp.get("entry_by_value", "foo") loaded1 = sdp.get("entry_by_ref", "foo")
assert loaded1 == ObjWithDigestWithKey(1, "foo") assert loaded1 == ObjWithDigestWithKey(1, "foo")
assert getattr(loaded1, Serializer.ORIGIN) == obj1.get_digest() assert getattr(loaded1, Serializer.ORIGIN) == obj1.get_digest()
loaded2 = sdp.get("entry_by_value", "bar") loaded2 = sdp.get("entry_by_ref", "bar")
assert loaded2 == ObjWithDigestWithKey(2, "bar") assert loaded2 == ObjWithDigestWithKey(2, "bar")
assert getattr(loaded2, Serializer.ORIGIN) == obj2.get_digest() assert getattr(loaded2, Serializer.ORIGIN) == obj2.get_digest()
@pytest.mark.parametrize("root", [
".sheerka",
"mem://"
])
def test_i_can_have_multiple_is_ref_to_the_same_key(root):
sdp = SheerkaDataProvider(root)
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithDigestWithKey)))
ref_result1 = sdp.add(evt_digest, "entry", ObjWithDigestWithKey(1, "foo"), use_ref=True)
ref_result2 = sdp.add(evt_digest, "entry", ObjWithDigestWithKey(2, "bar"), use_ref=True)
sdp.add(evt_digest, "entry_ref", SheerkaDataProviderRef("1", ref_result1.digest))
sdp.add(evt_digest, "entry_ref", SheerkaDataProviderRef("1", ref_result2.digest))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {'entry': {'1': '##REF##:1foo', '2': '##REF##:2bar'},
'entry_ref': {'1': [SheerkaDataProviderRef("1", ref_result1.digest),
SheerkaDataProviderRef("1", ref_result2.digest)]},
}
loaded = sdp.get("entry_ref", "1")
assert len(loaded) == 2
assert loaded[0] == ObjWithDigestWithKey(1, "foo")
assert loaded[1] == ObjWithDigestWithKey(2, "bar")
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
".sheerka", ".sheerka",
"mem://" "mem://"
@@ -660,21 +745,24 @@ def test_i_can_add_string_using_auto_generated_key(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
key_file = path.join(sdp.io.root, SheerkaDataProvider.KeysFile) key_file = path.join(sdp.io.root, SheerkaDataProvider.KeysFile)
entry1, key1 = sdp.add_with_auto_key(evt_digest, "entry1", "foo") result1 = sdp.add_with_auto_key(evt_digest, "entry1", "foo")
entry2, key2 = sdp.add_with_auto_key(evt_digest, "entry1", "bar") result2 = sdp.add_with_auto_key(evt_digest, "entry1", "bar")
entry3, key3 = sdp.add_with_auto_key(evt_digest, "entry2", "baz") result3 = sdp.add_with_auto_key(evt_digest, "entry2", "baz")
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert sdp.io.exists(key_file) assert sdp.io.exists(key_file)
assert read_json_file(sdp, key_file) == {"entry1": 2, "entry2": 1} assert read_json_file(sdp, key_file) == {"entry1": 2, "entry2": 1}
assert state.data == {"entry1": {"1": "foo", "2": "bar"}, "entry2": {"1": "baz"}} assert state.data == {"entry1": {"1": "foo", "2": "bar"}, "entry2": {"1": "baz"}}
assert entry1 == "entry1" assert result1.obj == "foo"
assert entry2 == "entry1" assert result2.obj == "bar"
assert entry3 == "entry2" assert result3.obj == "baz"
assert key1 == "1" assert result1.entry == "entry1"
assert key2 == "2" assert result2.entry == "entry1"
assert key3 == "1" assert result3.entry == "entry2"
assert result1.digest is None
assert result2.digest is None
assert result3.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -759,20 +847,6 @@ def test_i_cannot_add_the_same_digest_twice_in_the_same_entry4(root):
assert error.value.args[0] == "Duplicate object." assert error.value.args[0] == "Duplicate object."
def test_i_cannot_add_using_use_ref_and_is_ref():
sdp = SheerkaDataProvider("mem://")
with pytest.raises(SheerkaDataProviderError) as error:
sdp.add(evt_digest, "entry", ObjWithDigestWithKey("a", "b"), use_ref=True, is_ref=True)
def test_i_cannot_add_using_is_ref_if_obj_is_not_a_dictionary():
sdp = SheerkaDataProvider("mem://")
with pytest.raises(SheerkaDataProviderError) as error:
sdp.add(evt_digest, "entry", ObjWithDigestWithKey("a", "b"), is_ref=True)
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
".sheerka", ".sheerka",
"mem://" "mem://"
@@ -800,18 +874,23 @@ def test_i_can_add_object_using_auto_generated_key(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
key_file = path.join(sdp.io.root, SheerkaDataProvider.KeysFile) key_file = path.join(sdp.io.root, SheerkaDataProvider.KeysFile)
entry1, key1 = sdp.add_with_auto_key(evt_digest, "entry1", ObjNoKey("a", "b")) result1 = sdp.add_with_auto_key(evt_digest, "entry1", ObjNoKey("a", "b"))
entry2, key2 = sdp.add_with_auto_key(evt_digest, "entry1", ObjNoKey("a", "b")) result2 = sdp.add_with_auto_key(evt_digest, "entry1", ObjNoKey("a", "b"))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert sdp.io.exists(key_file) assert sdp.io.exists(key_file)
assert read_json_file(sdp, key_file) == {"entry1": 2} assert read_json_file(sdp, key_file) == {"entry1": 2}
assert state.data == {"entry1": {"1": ObjNoKey("a", "b"), "2": ObjNoKey("a", "b")}} assert state.data == {"entry1": {"1": ObjNoKey("a", "b"), "2": ObjNoKey("a", "b")}}
assert entry1 == "entry1"
assert entry2 == "entry1" assert result1.obj == ObjNoKey("a", "b")
assert key1 == "1" assert result2.obj == ObjNoKey("a", "b")
assert key2 == "2" assert result1.entry == "entry1"
assert result2.entry == "entry1"
assert result1.key == "1"
assert result2.key == "2"
assert result1.digest is None
assert result2.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -822,18 +901,23 @@ def test_object_key_is_updated_when_possible_using_auto_generated_key(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
key_file = path.join(sdp.io.root, SheerkaDataProvider.KeysFile) key_file = path.join(sdp.io.root, SheerkaDataProvider.KeysFile)
entry1, key1 = sdp.add_with_auto_key(evt_digest, "entry1", ObjSetKey("foo")) result1 = sdp.add_with_auto_key(evt_digest, "entry1", ObjSetKey("foo"))
entry2, key2 = sdp.add_with_auto_key(evt_digest, "entry1", ObjSetKey("foo")) result2 = sdp.add_with_auto_key(evt_digest, "entry1", ObjSetKey("foo"))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert sdp.io.exists(key_file) assert sdp.io.exists(key_file)
assert read_json_file(sdp, key_file) == {"entry1": 2} assert read_json_file(sdp, key_file) == {"entry1": 2}
assert state.data == {"entry1": {"1": ObjSetKey("foo", "1"), "2": ObjSetKey("foo", "2")}} assert state.data == {"entry1": {"1": ObjSetKey("foo", "1"), "2": ObjSetKey("foo", "2")}}
assert entry1 == "entry1"
assert entry2 == "entry1" assert result1.obj == ObjSetKey("foo", "1")
assert key1 == "1" assert result2.obj == ObjSetKey("foo", "2")
assert key2 == "2" assert result1.entry == "entry1"
assert result2.entry == "entry1"
assert result1.key == "1"
assert result2.key == "2"
assert result1.digest is None
assert result2.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -843,12 +927,13 @@ def test_object_key_is_updated_when_possible_using_auto_generated_key(root):
def test_i_can_set_objects_with_key(root): def test_i_can_set_objects_with_key(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
sdp.add(evt_digest, "entry", ObjWithKey(1, "foo")) sdp.add(evt_digest, "entry", ObjWithKey(1, "foo"))
entry, key = sdp.set(evt_digest, "entry", ObjWithKey(2, "foo")) result = sdp.set(evt_digest, "entry", ObjWithKey(2, "foo"))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": {"2": ObjWithKey(2, "foo")}} assert state.data == {"entry": {"2": ObjWithKey(2, "foo")}}
assert entry == "entry" assert result.entry == "entry"
assert key == "2" assert result.key == "2"
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -858,12 +943,13 @@ def test_i_can_set_objects_with_key(root):
def test_i_can_set_objects_with_no_key(root): def test_i_can_set_objects_with_no_key(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
sdp.add(evt_digest, "entry", ObjNoKey(1, "foo")) sdp.add(evt_digest, "entry", ObjNoKey(1, "foo"))
entry, key = sdp.set(evt_digest, "entry", ObjNoKey(2, "foo")) result = sdp.set(evt_digest, "entry", ObjNoKey(2, "foo"))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": ObjNoKey(2, "foo")} assert state.data == {"entry": ObjNoKey(2, "foo")}
assert entry == "entry" assert result.entry == "entry"
assert key is None assert result.key is None
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -873,12 +959,13 @@ def test_i_can_set_objects_with_no_key(root):
def test_i_can_set_from_list_to_dict(root): def test_i_can_set_from_list_to_dict(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
sdp.set(evt_digest, "entry", [ObjNoKey(1, "foo"), ObjNoKey(2, "foo")]) sdp.set(evt_digest, "entry", [ObjNoKey(1, "foo"), ObjNoKey(2, "foo")])
entry, key = sdp.set(evt_digest, "entry", {"1": ObjNoKey(1, "foo"), "2": ObjNoKey(2, "foo")}) result = sdp.set(evt_digest, "entry", {"1": ObjNoKey(1, "foo"), "2": ObjNoKey(2, "foo")})
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": {"1": ObjNoKey(1, "foo"), "2": ObjNoKey(2, "foo")}} assert state.data == {"entry": {"1": ObjNoKey(1, "foo"), "2": ObjNoKey(2, "foo")}}
assert entry == "entry" assert result.entry == "entry"
assert key is None assert result.key is None
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -889,18 +976,21 @@ def test_i_can_set_using_reference(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithKey))) sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithKey)))
sdp.add(evt_digest, "entry", ObjWithKey(1, "foo")) sdp.add(evt_digest, "entry", ObjWithKey(1, "foo"))
entry, key = sdp.set(evt_digest, "entry", ObjWithKey(2, "foo"), use_ref=True) result = sdp.set(evt_digest, "entry", ObjWithKey(2, "foo"), use_ref=True)
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": {"2": '##REF##:43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9'}} assert state.data == {"entry": {"2": '##REF##:43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9'}}
assert entry == "entry"
assert key == "2" assert result.obj == ObjWithKey(2, "foo")
assert result.entry == "entry"
assert result.key == "2"
assert result.digest == "43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9"
assert sdp.io.exists(sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, assert sdp.io.exists(sdp.io.get_obj_path(SheerkaDataProvider.ObjectsFolder,
"43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9")) "43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9"))
# sanity check, make sure that I can load back # sanity check, make sure that I can load back
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
assert loaded == ObjWithKey(2, "foo") assert loaded == ObjWithKey(2, "foo")
assert getattr(loaded, Serializer.ORIGIN) == "43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9" assert getattr(loaded, Serializer.ORIGIN) == "43f07065c7bad051cdd726bdfa4de7f8d754c31486c65ddb31d6b6548dec3db9"
@@ -952,12 +1042,15 @@ def test_i_can_add_an_object_with_a_key_as_a_reference(root):
obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj)) obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj))
sdp.serializer.register(obj_serializer) sdp.serializer.register(obj_serializer)
entry, key = sdp.add(evt_digest, "entry", obj, use_ref=True) result = sdp.add(evt_digest, "entry", obj, use_ref=True)
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
digest = state.data["entry"]["my_key"][len(SheerkaDataProvider.REF_PREFIX):] digest = state.data["entry"]["my_key"][len(SheerkaDataProvider.REF_PREFIX):]
assert key == obj.key assert result.obj == obj
assert entry == "entry" assert result.entry == "entry"
assert result.key == obj.key
assert result.digest == obj.get_digest()
assert digest == result.digest
assert state.data == {'entry': {'my_key': f"{SheerkaDataProvider.REF_PREFIX}{digest}"}} assert state.data == {'entry': {'my_key': f"{SheerkaDataProvider.REF_PREFIX}{digest}"}}
loaded = sdp.load_obj(digest) loaded = sdp.load_obj(digest)
@@ -973,15 +1066,18 @@ def test_i_can_add_a_dictionary_as_a_reference(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
obj = {"my_key": "value1"} obj = {"my_key": "value1"}
obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj)) # No need to register a serializer for dictionaries
sdp.serializer.register(obj_serializer)
entry, key = sdp.add(evt_digest, "entry", obj, use_ref=True) result = sdp.add(evt_digest, "entry", obj, use_ref=True)
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
digest = state.data["entry"][len(SheerkaDataProvider.REF_PREFIX):] digest = state.data["entry"][len(SheerkaDataProvider.REF_PREFIX):]
assert key is None assert result.obj == obj
assert entry == "entry" assert result.entry == "entry"
assert result.key is None # we return None as dict may contains several entries
assert result.digest == "1790cae3f354ecb6b419faaa2ee2c374ff33efb8cddafda9960924036ac04c1f" # a digest is created
assert digest == result.digest
assert state.data == {'entry': f"{SheerkaDataProvider.REF_PREFIX}{digest}"} assert state.data == {'entry': f"{SheerkaDataProvider.REF_PREFIX}{digest}"}
loaded = sdp.load_obj(digest) loaded = sdp.load_obj(digest)
@@ -990,23 +1086,50 @@ def test_i_can_add_a_dictionary_as_a_reference(root):
assert len(loaded) == 2 assert len(loaded) == 2
@pytest.mark.parametrize("root", [
".sheerka",
"mem://"
])
def test_i_can_add_an_object_with_no_builtin_digest_as_a_reference(root):
sdp = SheerkaDataProvider(root)
obj = ObjDumpJsonNoDigest("a", "b")
obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj))
sdp.serializer.register(obj_serializer)
result = sdp.add(evt_digest, "entry", obj, use_ref=True)
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
digest = state.data["entry"][obj.get_key()][len(SheerkaDataProvider.REF_PREFIX):]
assert result.obj == obj
assert result.entry == "entry"
assert result.key == obj.get_key()
assert result.digest is not None
assert digest == result.digest
assert state.data == {'entry': {obj.key: f"{SheerkaDataProvider.REF_PREFIX}{result.digest}"}}
loaded = sdp.load_obj(digest)
assert getattr(loaded, Serializer.ORIGIN) == digest
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
".sheerka", ".sheerka",
"mem://" "mem://"
]) ])
def test_i_can_add_unique(root): def test_i_can_add_unique(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo")) result = sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo"))
assert (entry, key) == ("entry", None) assert result == SheerkaDataProviderResult(ObjNoKey(1, "foo"), "entry", None, None, False)
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo")) result = sdp.add_unique(evt_digest, "entry", ObjNoKey(1, "foo"))
assert (entry, key) == (None, None) assert result == SheerkaDataProviderResult(ObjNoKey(1, "foo"), "entry", None, None, True)
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(2, "bar")) result = sdp.add_unique(evt_digest, "entry", ObjNoKey(2, "bar"))
assert (entry, key) == ("entry", None) assert result == SheerkaDataProviderResult(ObjNoKey(2, "bar"), "entry", None, None, False)
entry, key = sdp.add_unique(evt_digest, "entry", ObjNoKey(2, "bar")) result = sdp.add_unique(evt_digest, "entry", ObjNoKey(2, "bar"))
assert (entry, key) == (None, None) assert result == SheerkaDataProviderResult(ObjNoKey(2, "bar"), "entry", None, None, True)
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": {ObjNoKey(1, "foo"), ObjNoKey(2, "bar")}} assert state.data == {"entry": {ObjNoKey(1, "foo"), ObjNoKey(2, "bar")}}
@@ -1338,12 +1461,14 @@ def test_i_can_modify_dict_with_a_key(root):
sdp.add(evt_digest, "entry", {"key1": "foo"}) sdp.add(evt_digest, "entry", {"key1": "foo"})
sdp.add(evt_digest, "entry", {"key2": "bar"}) sdp.add(evt_digest, "entry", {"key2": "bar"})
entry, key = sdp.modify(evt_digest, "entry", "key1", "baz") result = sdp.modify(evt_digest, "entry", "key1", "baz")
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": {"key1": "baz", "key2": "bar"}} assert state.data == {"entry": {"key1": "baz", "key2": "bar"}}
assert entry == "entry" assert result.obj == "baz"
assert key == "key1" assert result.entry == "entry"
assert result.key == "key1"
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -1355,12 +1480,15 @@ def test_i_can_modify_an_object_with_a_key(root):
sdp.add(evt_digest, "entry", ObjWithKey("key1", "foo")) sdp.add(evt_digest, "entry", ObjWithKey("key1", "foo"))
sdp.add(evt_digest, "entry", ObjWithKey("key2", "bar")) sdp.add(evt_digest, "entry", ObjWithKey("key2", "bar"))
entry, key = sdp.modify(evt_digest, "entry", "key1", ObjWithKey("key1", "baz")) result = sdp.modify(evt_digest, "entry", "key1", ObjWithKey("key1", "baz"))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": {"key1": ObjWithKey("key1", "baz"), "key2": ObjWithKey("key2", "bar")}} assert state.data == {"entry": {"key1": ObjWithKey("key1", "baz"), "key2": ObjWithKey("key2", "bar")}}
assert entry == "entry" assert result.obj == ObjWithKey("key1", "baz")
assert key == "key1" assert result.entry == "entry"
assert result.key == "key1"
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -1372,12 +1500,14 @@ def test_i_can_modify_an_object_while_changing_the_key(root):
sdp.add(evt_digest, "entry", ObjWithKey("key1", "foo")) sdp.add(evt_digest, "entry", ObjWithKey("key1", "foo"))
sdp.add(evt_digest, "entry", ObjWithKey("key2", "bar")) sdp.add(evt_digest, "entry", ObjWithKey("key2", "bar"))
entry, key = sdp.modify(evt_digest, "entry", "key1", ObjWithKey("key3", "baz")) result = sdp.modify(evt_digest, "entry", "key1", ObjWithKey("key3", "baz"))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": {"key2": ObjWithKey("key2", "bar"), "key3": ObjWithKey("key3", "baz")}} assert state.data == {"entry": {"key2": ObjWithKey("key2", "bar"), "key3": ObjWithKey("key3", "baz")}}
assert entry == "entry" assert result.obj == ObjWithKey("key3", "baz")
assert key == "key3" assert result.entry == "entry"
assert result.key == "key3"
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -1389,12 +1519,14 @@ def test_i_can_modify_an_object_while_changing_the_key_to_an_existing_key(root):
sdp.add(evt_digest, "entry", ObjWithKey("key1", "foo")) sdp.add(evt_digest, "entry", ObjWithKey("key1", "foo"))
sdp.add(evt_digest, "entry", ObjWithKey("key2", "bar")) sdp.add(evt_digest, "entry", ObjWithKey("key2", "bar"))
entry, key = sdp.modify(evt_digest, "entry", "key2", ObjWithKey("key1", "bar")) result = sdp.modify(evt_digest, "entry", "key2", ObjWithKey("key1", "bar"))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": {"key1": [ObjWithKey("key1", "foo"), ObjWithKey("key1", "bar")]}} assert state.data == {"entry": {"key1": [ObjWithKey("key1", "foo"), ObjWithKey("key1", "bar")]}}
assert entry == "entry" assert result.obj == ObjWithKey("key1", "bar")
assert key == "key1" assert result.entry == "entry"
assert result.key == "key1"
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -1408,7 +1540,6 @@ def test_i_can_modify_an_object_while_changing_the_key_to_an_existing_when_list(
:return: :return:
""" """
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjDumpJson)))
sdp.add(evt_digest, "entry", ObjDumpJson("key1", "value11")) sdp.add(evt_digest, "entry", ObjDumpJson("key1", "value11"))
sdp.add(evt_digest, "entry", ObjDumpJson("key1", "value12")) sdp.add(evt_digest, "entry", ObjDumpJson("key1", "value12"))
@@ -1417,15 +1548,17 @@ def test_i_can_modify_an_object_while_changing_the_key_to_an_existing_when_list(
new_value = ObjDumpJson("key1", "value13") new_value = ObjDumpJson("key1", "value13")
setattr(new_value, Serializer.ORIGIN, ObjDumpJson("key2", "value21").get_digest()) setattr(new_value, Serializer.ORIGIN, ObjDumpJson("key2", "value21").get_digest())
entry, key = sdp.modify(evt_digest, "entry", "key2", new_value) result = sdp.modify(evt_digest, "entry", "key2", new_value)
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": { assert state.data == {"entry": {
"key1": [ObjDumpJson("key1", "value11"), ObjDumpJson("key1", "value12"), ObjDumpJson("key1", "value13")], "key1": [ObjDumpJson("key1", "value11"), ObjDumpJson("key1", "value12"), ObjDumpJson("key1", "value13")],
"key2": [ObjDumpJson("key2", "value22")] "key2": [ObjDumpJson("key2", "value22")]
}} }}
assert entry == "entry" assert result.obj == new_value
assert key == "key1" assert result.entry == "entry"
assert result.key == "key1"
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -1446,15 +1579,17 @@ def test_i_can_modify_an_object_while_changing_the_key_to_an_existing_when_nothi
new_value = ObjDumpJson("key1", "value13") new_value = ObjDumpJson("key1", "value13")
setattr(new_value, Serializer.ORIGIN, ObjDumpJson("key2", "value21").get_digest()) setattr(new_value, Serializer.ORIGIN, ObjDumpJson("key2", "value21").get_digest())
entry, key = sdp.modify(evt_digest, "entry", "key2", new_value) result = sdp.modify(evt_digest, "entry", "key2", new_value)
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": { assert state.data == {"entry": {
"key1": ObjDumpJson("key1", "value13"), "key1": ObjDumpJson("key1", "value13"),
"key2": [ObjDumpJson("key2", "value22")] "key2": [ObjDumpJson("key2", "value22")]
}} }}
assert entry == "entry" assert result.obj == new_value
assert key == "key1" assert result.entry == "entry"
assert result.key == "key1"
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -1476,34 +1611,143 @@ def test_i_can_modify_an_object_while_changing_the_key_to_an_existing_when_one_i
new_value = ObjDumpJson("key1", "value13") new_value = ObjDumpJson("key1", "value13")
setattr(new_value, Serializer.ORIGIN, ObjDumpJson("key2", "value21").get_digest()) setattr(new_value, Serializer.ORIGIN, ObjDumpJson("key2", "value21").get_digest())
entry, key = sdp.modify(evt_digest, "entry", "key2", new_value) result = sdp.modify(evt_digest, "entry", "key2", new_value)
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": { assert state.data == {"entry": {
"key1": [ObjDumpJson("key1", "value11"), ObjDumpJson("key1", "value13")], "key1": [ObjDumpJson("key1", "value11"), ObjDumpJson("key1", "value13")],
"key2": [ObjDumpJson("key2", "value22")] "key2": [ObjDumpJson("key2", "value22")]
}} }}
assert entry == "entry" assert result.obj == new_value
assert key == "key1" assert result.entry == "entry"
assert result.key == "key1"
assert result.digest is None
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
".sheerka", ".sheerka",
"mem://" "mem://"
]) ])
def test_i_can_modify_a_ref(root): def test_i_can_modify_a_object_saved_by_ref(root):
sdp = SheerkaDataProvider(root) sdp = SheerkaDataProvider(root)
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithKey))) sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithKey)))
sdp.add(evt_digest, "entry", ObjWithKey("key1", "foo")) sdp.add(evt_digest, "entry", ObjWithKey("key1", "foo"))
entry, key = sdp.add(evt_digest, "entry", ObjWithKey("key2", "bar"), use_ref=True) sdp.add(evt_digest, "entry", ObjWithKey("key2", "bar"), use_ref=True)
sdp.modify(evt_digest, "entry", "key2", ObjWithKey("key2", "baz")) result = sdp.modify(evt_digest, "entry", "key2", ObjWithKey("key2", "baz"))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile)) state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": { assert state.data == {"entry": {
"key1": ObjWithKey("key1", "foo"), "key1": ObjWithKey("key1", "foo"),
"key2": "##REF##:041d3cca905b51bc2c66251e73e56b836aae7b9435ee3d7eb05d44bb67ff575e"}} "key2": "##REF##:041d3cca905b51bc2c66251e73e56b836aae7b9435ee3d7eb05d44bb67ff575e"}}
assert entry == "entry" assert result.obj == ObjWithKey("key2", "baz")
assert key == "key2" assert result.entry == "entry"
assert result.key == "key2"
assert result.digest == "041d3cca905b51bc2c66251e73e56b836aae7b9435ee3d7eb05d44bb67ff575e"
@pytest.mark.parametrize("root", [
".sheerka",
"mem://"
])
def test_i_can_modify_an_object_saved_by_ref_in_a_list(root):
sdp = SheerkaDataProvider(root)
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjDumpJsonNoDigest)))
sdp.add(evt_digest, "entry", ObjDumpJsonNoDigest("key1", "value11"), use_ref=True)
sdp.add(evt_digest, "entry", ObjDumpJsonNoDigest("key1", "value12"), use_ref=True)
result = sdp.add(evt_digest, "entry", ObjDumpJsonNoDigest("key2", "value21"), use_ref=True)
sdp.add(evt_digest, "entry", ObjDumpJsonNoDigest("key2", "value22"), use_ref=True)
new_value = ObjDumpJsonNoDigest("key1", "value13")
setattr(new_value, Serializer.ORIGIN, result.digest)
result = sdp.modify(evt_digest, "entry", "key2", new_value)
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {"entry": {
'key1': ['##REF##:f80a0c0aceb1a7a3d238c0cff2d86d6bd3a62e0c1a65c5b505f43b10c4604bd8',
'##REF##:239a8238d188c37afa10b1bcc312ca8a0e78f6e75d688ca65d08e16717ff68b0',
'##REF##:9d0a2bf9d4081de0b14837ea46bc7a1cfb6b7562f7ae86255ea9bd0ac53a6437'],
'key2': ['##REF##:df8a38b07f469f2ff8001ea6a70f77f4f9ce85d69c530091fcaf4b380f1500d3']
}}
assert result.obj == new_value
assert result.entry == "entry"
assert result.key == "key1"
assert result.digest is not None
@pytest.mark.parametrize("root", [
".sheerka",
"mem://"
])
def test_i_can_modify_a_data_provider_ref(root):
# first, create a valid entry
sdp = SheerkaDataProvider(root)
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithDigestWithKey)))
obj = ObjWithDigestWithKey("1", "foo")
sdp.add(evt_digest, "entry", obj, use_ref=True)
sdp.add(evt_digest, "entry_ref", SheerkaDataProviderRef(obj.b, obj.get_digest()))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {
"entry": {"1": "##REF##:1foo"},
"entry_ref": {"foo": SheerkaDataProviderRef(obj.b, obj.get_digest())}}
# modify this entry
obj_new = ObjWithDigestWithKey("1", "bar")
sdp.modify(evt_digest, "entry", obj_new.a, obj_new)
result = sdp.modify(evt_digest, "entry_ref", "foo", SheerkaDataProviderRef(obj.b, obj_new.get_digest()))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {
"entry": {"1": "##REF##:1bar"},
"entry_ref": {"foo": SheerkaDataProviderRef(obj.b, obj_new.get_digest())}}
assert result.obj == SheerkaDataProviderRef(obj.b, obj_new.get_digest())
assert result.entry == "entry_ref"
assert result.key == "foo"
assert result.digest is None # digest is not set as what is saved (the digest) is not saved by ref
# sanity check, I can load the modified entry
loaded = sdp.get("entry_ref", "foo")
assert loaded == ObjWithDigestWithKey("1", "bar")
@pytest.mark.parametrize("root", [
".sheerka",
"mem://"
])
def test_i_can_modify_is_ref_when_in_list(root):
sdp = SheerkaDataProvider(root)
sdp.serializer.register(PickleSerializer(lambda o: isinstance(o, ObjWithDigestWithKey)))
ref_result1 = sdp.add(evt_digest, "entry", ObjWithDigestWithKey(1, "foo"), use_ref=True)
ref_result2 = sdp.add(evt_digest, "entry", ObjWithDigestWithKey(2, "bar"), use_ref=True)
sdp.add(evt_digest, "entry_ref", SheerkaDataProviderRef("1", ref_result1.digest))
sdp.add(evt_digest, "entry_ref", SheerkaDataProviderRef("1", ref_result2.digest))
ref_result3 = sdp.add(evt_digest, "entry", ObjWithDigestWithKey(3, "baz"), use_ref=True)
result = sdp.modify(
evt_digest,
"entry_ref",
"1",
SheerkaDataProviderRef("1", ref_result3.digest, ref_result2.digest))
state = sdp.load_state(sdp.get_snapshot(SheerkaDataProvider.HeadFile))
assert state.data == {'entry': {'1': '##REF##:1foo', '2': '##REF##:2bar', '3': '##REF##:3baz'},
'entry_ref': {'1': [
SheerkaDataProviderRef("1", ref_result1.digest),
SheerkaDataProviderRef("1", ref_result3.digest, ref_result2.digest)]}}
loaded = sdp.get("entry_ref", "1")
assert len(loaded) == 2
assert loaded[0] == ObjWithDigestWithKey(1, "foo")
assert loaded[1] == ObjWithDigestWithKey(3, "baz")
assert result.obj == SheerkaDataProviderRef("1", ref_result3.digest, ref_result2.digest)
assert result.entry == "entry_ref"
assert result.key == "1"
assert result.digest is None # digest is not set as what is saved (the digest) is not saved by ref
@pytest.mark.parametrize("root", [ @pytest.mark.parametrize("root", [
@@ -1661,8 +1905,8 @@ def test_i_can_get_object_saved_by_reference(root):
obj = ObjDumpJson("my_key", "value1") obj = ObjDumpJson("my_key", "value1")
sdp.serializer.register(JsonSerializer(core.utils.get_full_qualified_name(obj))) sdp.serializer.register(JsonSerializer(core.utils.get_full_qualified_name(obj)))
entry, key = sdp.add(evt_digest, "entry", obj, use_ref=True) result = sdp.add(evt_digest, "entry", obj, use_ref=True)
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
assert loaded == obj assert loaded == obj
@@ -1876,12 +2120,15 @@ def test_i_can_save_and_load_object_ref_with_history(root):
obj = ObjDumpJson("my_key", "value1") obj = ObjDumpJson("my_key", "value1")
sdp.serializer.register(JsonSerializer(core.utils.get_full_qualified_name(obj))) sdp.serializer.register(JsonSerializer(core.utils.get_full_qualified_name(obj)))
entry, key = sdp.add(evt_digest, "entry", obj, use_ref=True) result = sdp.add(evt_digest, "entry", obj, use_ref=True)
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
history = getattr(loaded, Serializer.HISTORY) history = getattr(loaded, Serializer.HISTORY)
assert key == obj.key assert result.obj == obj
assert entry == "entry" assert result.entry == "entry"
assert result.key == obj.key
assert result.digest == obj.get_digest()
assert loaded.key == obj.key assert loaded.key == obj.key
assert loaded.value == obj.value assert loaded.value == obj.value
@@ -1895,8 +2142,8 @@ def test_i_can_save_and_load_object_ref_with_history(root):
previous_modification_time = history[Serializer.MODIFICATION_DATE] previous_modification_time = history[Serializer.MODIFICATION_DATE]
previous_parents = history[Serializer.PARENTS] previous_parents = history[Serializer.PARENTS]
sdp.modify(evt_digest, "entry", key, loaded) sdp.modify(evt_digest, "entry", result.key, loaded)
loaded = sdp.get(entry, key) loaded = sdp.get(result.entry, result.key)
history = getattr(loaded, Serializer.HISTORY) history = getattr(loaded, Serializer.HISTORY)
assert history[Serializer.MODIFICATION_DATE] == previous_modification_time assert history[Serializer.MODIFICATION_DATE] == previous_modification_time
@@ -1906,8 +2153,8 @@ def test_i_can_save_and_load_object_ref_with_history(root):
previous_digest = loaded.get_digest() previous_digest = loaded.get_digest()
loaded.value = "value2" loaded.value = "value2"
sdp.modify(evt_digest, "entry", key, loaded) sdp.modify(evt_digest, "entry", result.key, loaded)
loaded2 = sdp.get(entry, key) loaded2 = sdp.get(result.entry, result.key)
history2 = getattr(loaded2, Serializer.HISTORY) history2 = getattr(loaded2, Serializer.HISTORY)
assert loaded2.key == loaded.key assert loaded2.key == loaded.key
@@ -1932,10 +2179,10 @@ def test_i_can_add_obj_with_same_key_and_get_them_back(root):
obj2 = ObjDumpJson("key", "value2") obj2 = ObjDumpJson("key", "value2")
sdp.serializer.register(JsonSerializer(core.utils.get_full_qualified_name(obj1))) sdp.serializer.register(JsonSerializer(core.utils.get_full_qualified_name(obj1)))
entry1, key1 = sdp.add(evt_digest, "entry", obj1, use_ref=True) result = sdp.add(evt_digest, "entry", obj1, use_ref=True)
entry2, key2 = sdp.add(evt_digest, "entry", obj2, use_ref=True) sdp.add(evt_digest, "entry", obj2, use_ref=True)
loaded = sdp.get_safe(entry1, key1) loaded = sdp.get(result.entry, result.key)
assert len(loaded) == 2 assert len(loaded) == 2
assert loaded[0] == obj1 assert loaded[0] == obj1
@@ -1953,14 +2200,14 @@ def test_i_get_safe_dictionary_without_origin(root):
obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj)) obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj))
sdp.serializer.register(obj_serializer) sdp.serializer.register(obj_serializer)
entry, key = sdp.add(evt_digest, "entry", obj, use_ref=True) result = sdp.add(evt_digest, "entry", obj, use_ref=True)
from_db = sdp.get_safe(entry, key) from_db = sdp.get(result.entry, result.key)
assert len(from_db) == 2 assert len(from_db) == 2
assert from_db["my_key"] == obj["my_key"] assert from_db["my_key"] == obj["my_key"]
assert Serializer.ORIGIN in from_db assert Serializer.ORIGIN in from_db
from_db_no_origin = sdp.get_safe(entry, key, load_origin=False) from_db_no_origin = sdp.get_safe(result.entry, result.key, load_origin=False)
assert len(from_db_no_origin) == 1 assert len(from_db_no_origin) == 1
assert from_db_no_origin["my_key"] == obj["my_key"] assert from_db_no_origin["my_key"] == obj["my_key"]
assert Serializer.ORIGIN not in from_db_no_origin assert Serializer.ORIGIN not in from_db_no_origin
@@ -1977,14 +2224,14 @@ def test_i_get_dictionary_without_origin(root):
obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj)) obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj))
sdp.serializer.register(obj_serializer) sdp.serializer.register(obj_serializer)
entry, key = sdp.add(evt_digest, "entry", obj, use_ref=True) result = sdp.add(evt_digest, "entry", obj, use_ref=True)
from_db = sdp.get(entry, key) from_db = sdp.get(result.entry, result.key)
assert len(from_db) == 2 assert len(from_db) == 2
assert from_db["my_key"] == obj["my_key"] assert from_db["my_key"] == obj["my_key"]
assert Serializer.ORIGIN in from_db assert Serializer.ORIGIN in from_db
from_db_no_origin = sdp.get(entry, key, load_origin=False) from_db_no_origin = sdp.get(result.entry, result.key, load_origin=False)
assert len(from_db_no_origin) == 1 assert len(from_db_no_origin) == 1
assert from_db_no_origin["my_key"] == obj["my_key"] assert from_db_no_origin["my_key"] == obj["my_key"]
assert Serializer.ORIGIN not in from_db_no_origin assert Serializer.ORIGIN not in from_db_no_origin
@@ -2001,12 +2248,12 @@ def test_i_get_safe_object_without_origin(root):
obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj)) obj_serializer = JsonSerializer(core.utils.get_full_qualified_name(obj))
sdp.serializer.register(obj_serializer) sdp.serializer.register(obj_serializer)
entry, key = sdp.add(evt_digest, "entry", obj, use_ref=True) result = sdp.add(evt_digest, "entry", obj, use_ref=True)
from_db = sdp.get_safe(entry, key) from_db = sdp.get(result.entry, result.key)
assert from_db == obj assert from_db == obj
assert hasattr(from_db, Serializer.ORIGIN) assert hasattr(from_db, Serializer.ORIGIN)
from_db_no_origin = sdp.get_safe(entry, key, load_origin=False) from_db_no_origin = sdp.get_safe(result.entry, result.key, load_origin=False)
assert from_db_no_origin == obj assert from_db_no_origin == obj
assert not hasattr(from_db_no_origin, Serializer.ORIGIN) assert not hasattr(from_db_no_origin, Serializer.ORIGIN)