Implemented ConceptManager with concept creation, modification and deletion
This commit is contained in:
+87
-248
@@ -8,10 +8,7 @@ from cache.Cache import Cache
|
||||
from cache.CacheManager import CacheManager
|
||||
from cache.DictionaryCache import DictionaryCache
|
||||
from cache.IncCache import IncCache
|
||||
from cache.ListIfNeededCache import ListIfNeededCache
|
||||
from cache.SetCache import SetCache
|
||||
from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept, BuiltinErrors, BuiltinUnique, \
|
||||
UnknownConcept, AllBuiltinConcepts
|
||||
from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept, BuiltinErrors, UnknownConcept
|
||||
from core.concept import Concept, ConceptParts, NotInit, get_concept_attrs
|
||||
from core.error import ErrorObj
|
||||
from core.global_symbols import EVENT_USER_INPUT_EVALUATED
|
||||
@@ -49,12 +46,8 @@ class Sheerka(Concept):
|
||||
Main controller for the project
|
||||
"""
|
||||
|
||||
CONCEPTS_BY_ID_ENTRY = "Concepts_By_ID" # to store all the concepts
|
||||
CONCEPTS_BY_KEY_ENTRY = "Concepts_By_Key"
|
||||
CONCEPTS_BY_NAME_ENTRY = "Concepts_By_Name"
|
||||
CONCEPTS_BY_HASH_ENTRY = "Concepts_By_Hash" # store hash of concepts definitions (not values)
|
||||
|
||||
CONCEPTS_REFERENCES_ENTRY = "Concepts_References" # tracks references between concepts
|
||||
CONCEPTS_BY_ID_ENTRY = "ConceptManager:Concepts_By_ID"
|
||||
CONCEPTS_BY_NAME_ENTRY = "ConceptManager:Concepts_By_Name"
|
||||
|
||||
CONCEPTS_BY_FIRST_KEYWORD_ENTRY = "Concepts_By_First_Keyword"
|
||||
RESOLVED_CONCEPTS_BY_FIRST_KEYWORD_ENTRY = "Resolved_Concepts_By_First_Keyword"
|
||||
@@ -63,13 +56,10 @@ class Sheerka(Concept):
|
||||
CONCEPTS_GRAMMARS_ENTRY = "Concepts_Grammars"
|
||||
CHICKEN_AND_EGG_CONCEPTS_ENTRY = "Chicken_And_Egg_Concepts"
|
||||
|
||||
CONCEPTS_KEYS_ENTRY = "Concepts_Keys"
|
||||
OBJECTS_IDS_ENTRY = "Objects_Ids"
|
||||
BUILTIN_CONCEPTS_KEYS = "Builtins_Concepts" # sequential key for builtin concepts
|
||||
USER_CONCEPTS_KEYS = "User_Concepts" # sequential key for user defined concepts
|
||||
|
||||
MAX_EXECUTION_HISTORY = 100
|
||||
MAX_RETURN_VALUES_HISTORY = 100
|
||||
|
||||
ALL_ATTRIBUTES = []
|
||||
|
||||
def __init__(self, cache_only=False, debug=False, loggers=None):
|
||||
@@ -191,6 +181,8 @@ class Sheerka(Concept):
|
||||
initialize_pickle_handlers()
|
||||
|
||||
self.sdp = SheerkaDataProvider(root_folder, self)
|
||||
self.builtin_cache = self.get_builtins_classes_as_dict()
|
||||
|
||||
self.initialize_caching()
|
||||
self.get_builtin_parsers()
|
||||
self.get_builtin_evaluators()
|
||||
@@ -233,31 +225,8 @@ class Sheerka(Concept):
|
||||
|
||||
def initialize_caching(self):
|
||||
|
||||
def params(cache_name):
|
||||
return {
|
||||
'default': lambda k: self.sdp.get(cache_name, k),
|
||||
'extend_exists': lambda k: self.sdp.exists(cache_name, k)
|
||||
}
|
||||
|
||||
cache = IncCache(default=lambda k: self.sdp.get(self.CONCEPTS_KEYS_ENTRY, k))
|
||||
self.cache_manager.register_cache(self.CONCEPTS_KEYS_ENTRY, cache)
|
||||
|
||||
register_concept_cache = self.cache_manager.register_concept_cache
|
||||
|
||||
cache = Cache(**params(self.CONCEPTS_BY_ID_ENTRY))
|
||||
register_concept_cache(self.CONCEPTS_BY_ID_ENTRY, cache, lambda c: c.id, True)
|
||||
|
||||
cache = ListIfNeededCache(**params(self.CONCEPTS_BY_KEY_ENTRY))
|
||||
register_concept_cache(self.CONCEPTS_BY_KEY_ENTRY, cache, lambda c: c.key, True)
|
||||
|
||||
cache = ListIfNeededCache(**params(self.CONCEPTS_BY_NAME_ENTRY))
|
||||
register_concept_cache(self.CONCEPTS_BY_NAME_ENTRY, cache, lambda c: c.name, True)
|
||||
|
||||
cache = ListIfNeededCache(**params(self.CONCEPTS_BY_HASH_ENTRY))
|
||||
register_concept_cache(self.CONCEPTS_BY_HASH_ENTRY, cache, lambda c: c.get_definition_hash(), True)
|
||||
|
||||
cache = SetCache(default=lambda k: self.sdp.get(self.CONCEPTS_REFERENCES_ENTRY, k))
|
||||
self.cache_manager.register_cache(self.CONCEPTS_REFERENCES_ENTRY, cache)
|
||||
cache = IncCache(default=lambda k: self.sdp.get(self.OBJECTS_IDS_ENTRY, k))
|
||||
self.cache_manager.register_cache(self.OBJECTS_IDS_ENTRY, cache)
|
||||
|
||||
cache = DictionaryCache(default=lambda k: self.sdp.get(self.CONCEPTS_BY_FIRST_KEYWORD_ENTRY, k))
|
||||
self.cache_manager.register_cache(self.CONCEPTS_BY_FIRST_KEYWORD_ENTRY, cache)
|
||||
@@ -307,8 +276,6 @@ class Sheerka(Concept):
|
||||
service.initialize_deferred(context, is_first_time)
|
||||
|
||||
def first_time_initialisation(self, context):
|
||||
|
||||
self.cache_manager.put(self.CONCEPTS_KEYS_ENTRY, self.USER_CONCEPTS_KEYS, 1000)
|
||||
self.record_var(context, self.name, "save_execution_context", True)
|
||||
|
||||
def initialize_builtin_concepts(self):
|
||||
@@ -317,38 +284,11 @@ class Sheerka(Concept):
|
||||
:return: None
|
||||
"""
|
||||
# self.init_log.debug("Initializing builtin concepts")
|
||||
builtins_classes = self.get_builtins_classes_as_dict()
|
||||
|
||||
# this all initialization of the builtins seems to be little bit complicated
|
||||
# why do we need to update it from DB ?
|
||||
for key in AllBuiltinConcepts:
|
||||
concept = self if key == BuiltinConcepts.SHEERKA \
|
||||
else builtins_classes[str(key)]() if str(key) in builtins_classes \
|
||||
else Concept(key, True, False, key)
|
||||
|
||||
if key in BuiltinUnique:
|
||||
concept._metadata.is_unique = True
|
||||
concept._metadata.is_evaluated = True
|
||||
|
||||
if not concept._metadata.is_unique and str(key) in builtins_classes:
|
||||
self.builtin_cache[key] = builtins_classes[str(key)]
|
||||
|
||||
from_db = self.cache_manager.get(self.CONCEPTS_BY_KEY_ENTRY, concept._metadata.key)
|
||||
if from_db is None:
|
||||
# self.init_log.debug(f"'{concept.name}' concept is not found in db. Adding.")
|
||||
self.set_id_if_needed(concept, True)
|
||||
self.cache_manager.add_concept(concept)
|
||||
|
||||
if key == BuiltinConcepts.RETURN_VALUE:
|
||||
self.return_value_concept_id = concept.id
|
||||
elif key == BuiltinConcepts.ERROR:
|
||||
self.error_concept_id = concept.id
|
||||
|
||||
else:
|
||||
# self.init_log.debug(f"Found concept '{from_db}' in db. Updating.")
|
||||
concept.update_from(from_db)
|
||||
|
||||
return
|
||||
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
|
||||
concept_service = self.services[SheerkaConceptManager.NAME]
|
||||
concepts_ids = concept_service.initialize_builtin_concepts()
|
||||
self.return_value_concept_id = concepts_ids[BuiltinConcepts.RETURN_VALUE]
|
||||
self.error_concept_id = concepts_ids[BuiltinConcepts.ERROR]
|
||||
|
||||
def get_builtin_parsers(self):
|
||||
"""
|
||||
@@ -479,114 +419,6 @@ class Sheerka(Concept):
|
||||
"""
|
||||
self.printer_handler.print(result, instructions)
|
||||
|
||||
def set_id_if_needed(self, obj: Concept, is_builtin: bool):
|
||||
"""
|
||||
Set the key for the concept if needed
|
||||
:param obj:
|
||||
:param is_builtin:
|
||||
:return:
|
||||
"""
|
||||
if obj._metadata.id is not None:
|
||||
return
|
||||
|
||||
key = self.BUILTIN_CONCEPTS_KEYS if is_builtin else self.USER_CONCEPTS_KEYS
|
||||
obj._metadata.id = str(self.cache_manager.get(self.CONCEPTS_KEYS_ENTRY, key))
|
||||
# self.log.debug(f"Setting id '{obj.metadata.id}' to concept '{obj.metadata.name}'.")
|
||||
|
||||
def force_sya_def(self, context, list_of_def):
|
||||
"""
|
||||
Set the precedence and/or the associativity of a concept
|
||||
FOR TESTS PURPOSE. TO REMOVE EVENTUALLY
|
||||
:param context:
|
||||
:param list_of_def list of tuple(concept_id, precedence (int), SyaAssociativity)
|
||||
:return:
|
||||
"""
|
||||
|
||||
# validate the entries
|
||||
# If one entry is an invalid concept, rollback everything
|
||||
for concept_id, precedence, associativity in list_of_def:
|
||||
if concept_id == BuiltinConcepts.UNKNOWN_CONCEPT:
|
||||
return self.ret(self.name,
|
||||
False,
|
||||
self.new(BuiltinConcepts.ERROR, body=f"Concept {concept_id} is not known"))
|
||||
|
||||
sya_def = self.cache_manager.copy(self.RESOLVED_CONCEPTS_SYA_DEFINITION_ENTRY) or {}
|
||||
|
||||
# update the definitions
|
||||
for concept_id, precedence, associativity in list_of_def:
|
||||
if precedence is None and associativity is None:
|
||||
try:
|
||||
del self.sya_definitions[concept_id]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
sya_def[concept_id] = (precedence, associativity)
|
||||
|
||||
# put in cache
|
||||
self.cache_manager.put(self.RESOLVED_CONCEPTS_SYA_DEFINITION_ENTRY, False, sya_def)
|
||||
|
||||
return self.ret(self.name, True, self.new(BuiltinConcepts.SUCCESS))
|
||||
|
||||
def add_in_cache(self, concept: Concept):
|
||||
"""
|
||||
Adds a concept template in cache.
|
||||
The cache is used as a proxy before looking at sdp
|
||||
:param concept:
|
||||
:return:
|
||||
"""
|
||||
|
||||
# sanity check
|
||||
if concept.key is None:
|
||||
concept.init_key()
|
||||
|
||||
if concept.key is None:
|
||||
raise KeyError()
|
||||
|
||||
self.cache_manager.add_concept(concept)
|
||||
|
||||
return concept
|
||||
|
||||
def get_by_key(self, concept_key, concept_id=None):
|
||||
concept_key = str(concept_key) if isinstance(concept_key, BuiltinConcepts) else concept_key
|
||||
return self.internal_get("key", concept_key, self.CONCEPTS_BY_KEY_ENTRY, concept_id)
|
||||
|
||||
def get_by_name(self, concept_name, concept_id=None):
|
||||
return self.internal_get("name", concept_name, self.CONCEPTS_BY_NAME_ENTRY, concept_id)
|
||||
|
||||
def get_by_hash(self, concept_hash, concept_id=None):
|
||||
return self.internal_get("hash", concept_hash, self.CONCEPTS_BY_HASH_ENTRY, concept_id)
|
||||
|
||||
def get_by_id(self, concept_id):
|
||||
return self.internal_get("id", concept_id, self.CONCEPTS_BY_ID_ENTRY, None)
|
||||
|
||||
def internal_get(self, index_name, key, cache_name, concept_id=None):
|
||||
"""
|
||||
Tries to find an entry
|
||||
:param index_name: name of the index (ex by_id, by_key...)
|
||||
:param key: index value
|
||||
:param cache_name: name of the cache (ex Concepts_By_ID...)
|
||||
:param concept_id: id of the concept if none, in case where there are multiple results
|
||||
:return:
|
||||
"""
|
||||
|
||||
if key is None:
|
||||
return ErrorConcept(f"Concept '{key}' is undefined.")
|
||||
|
||||
concepts = self.cache_manager.get(cache_name, key)
|
||||
if concepts:
|
||||
if concept_id is None:
|
||||
return concepts
|
||||
|
||||
if not hasattr(concepts, "__iter__"):
|
||||
return concepts
|
||||
|
||||
for c in concepts:
|
||||
if c.id == concept_id:
|
||||
return c
|
||||
|
||||
metadata = [(index_name, key), ("id", concept_id)] if concept_id else (index_name, key)
|
||||
return self.get_unknown(metadata)
|
||||
|
||||
def resolve(self, concept):
|
||||
"""
|
||||
Try to find a concept by its name, id, or c:: definition
|
||||
@@ -655,12 +487,19 @@ class Sheerka(Concept):
|
||||
if isinstance(key, Token):
|
||||
if key.type == TokenKind.RULE: # do not recognize rules !!!
|
||||
return None
|
||||
|
||||
if key.value[1]:
|
||||
concept = self.cache_manager.get(self.CONCEPTS_BY_ID_ENTRY, key.value[1])
|
||||
else:
|
||||
concept = self.cache_manager.get(self.CONCEPTS_BY_NAME_ENTRY, key.value[0])
|
||||
key = key.value
|
||||
elif isinstance(key, str) and key.startswith("c:"):
|
||||
key = core.utils.unstr_concept(key)
|
||||
|
||||
if isinstance(key, tuple):
|
||||
if key == (None, None):
|
||||
return None
|
||||
|
||||
if key[1]:
|
||||
concept = self.cache_manager.get(self.CONCEPTS_BY_ID_ENTRY, key[1])
|
||||
else:
|
||||
concept = self.cache_manager.get(self.CONCEPTS_BY_NAME_ENTRY, key[0])
|
||||
else:
|
||||
concept = self.cache_manager.get(self.CONCEPTS_BY_NAME_ENTRY, key)
|
||||
|
||||
@@ -668,44 +507,6 @@ class Sheerka(Concept):
|
||||
return None
|
||||
return new_instances(concept) if return_new else concept
|
||||
|
||||
def has_id(self, concept_id):
|
||||
"""
|
||||
Returns True if a concept with this id exists in cache
|
||||
It does not search in the remote repository
|
||||
:param concept_id:
|
||||
:return:
|
||||
"""
|
||||
if concept_id is None:
|
||||
return False
|
||||
return self.cache_manager.has(self.CONCEPTS_BY_ID_ENTRY, concept_id)
|
||||
|
||||
def has_key(self, concept_key):
|
||||
"""
|
||||
Returns True if concept(s) with this key exist in cache
|
||||
It does not search in the remote repository
|
||||
:param concept_key:
|
||||
:return:
|
||||
"""
|
||||
return self.cache_manager.has(self.CONCEPTS_BY_KEY_ENTRY, concept_key)
|
||||
|
||||
def has_name(self, concept_name):
|
||||
"""
|
||||
Returns True if concept(s) with this name exist in cache
|
||||
It does not search in the remote repository
|
||||
:param concept_name:
|
||||
:return:
|
||||
"""
|
||||
return self.cache_manager.has(self.CONCEPTS_BY_NAME_ENTRY, concept_name)
|
||||
|
||||
def has_hash(self, concept_hash):
|
||||
"""
|
||||
Returns True if concept(s) with this hash exist in cache
|
||||
It does not search in the remote repository
|
||||
:param concept_hash:
|
||||
:return:
|
||||
"""
|
||||
return self.cache_manager.has(self.CONCEPTS_BY_HASH_ENTRY, concept_hash)
|
||||
|
||||
def new(self, concept_key, **kwargs):
|
||||
"""
|
||||
Returns an instance of a new concept
|
||||
@@ -816,21 +617,6 @@ class Sheerka(Concept):
|
||||
|
||||
return (self.objvalue(obj) for obj in objs.body)
|
||||
|
||||
def value_by_concept(self, obj, concept):
|
||||
if obj is None:
|
||||
return None
|
||||
|
||||
if not isinstance(obj, Concept):
|
||||
return None
|
||||
|
||||
if isinstance(concept, tuple) and obj.key in [str(key) for key in concept]:
|
||||
return obj
|
||||
|
||||
if obj.key == str(concept):
|
||||
return obj
|
||||
|
||||
return self.value_by_concept(obj.body, concept)
|
||||
|
||||
def get_error(self, obj):
|
||||
if isinstance(obj, Concept) and obj._metadata.is_builtin and obj.key in BuiltinErrors:
|
||||
return obj
|
||||
@@ -863,16 +649,6 @@ class Sheerka(Concept):
|
||||
|
||||
return self.parsers_prefix + name
|
||||
|
||||
def test(self):
|
||||
return f"I have access to Sheerka !"
|
||||
|
||||
def test_using_context(self, context, param):
|
||||
event = context.event.get_digest()
|
||||
return f"I have access to Sheerka ! {param=}, {event=}."
|
||||
|
||||
def test_error(self):
|
||||
raise Exception("I can raise an error")
|
||||
|
||||
@staticmethod
|
||||
def is_success(obj):
|
||||
if isinstance(obj, bool): # quick win
|
||||
@@ -1014,6 +790,69 @@ class Sheerka(Concept):
|
||||
}
|
||||
return self.new(BuiltinConcepts.TO_DICT, body=bag)
|
||||
|
||||
def test(self):
|
||||
return f"I have access to Sheerka !"
|
||||
|
||||
def test_using_context(self, context, param):
|
||||
event = context.event.get_digest()
|
||||
return f"I have access to Sheerka ! {param=}, {event=}."
|
||||
|
||||
def test_error(self):
|
||||
raise Exception("I can raise an error")
|
||||
|
||||
def test_only_force_sya_def(self, context, list_of_def):
|
||||
"""
|
||||
Set the precedence and/or the associativity of a concept
|
||||
FOR TESTS PURPOSE. TO REMOVE EVENTUALLY
|
||||
:param context:
|
||||
:param list_of_def list of tuple(concept_id, precedence (int), SyaAssociativity)
|
||||
:return:
|
||||
"""
|
||||
|
||||
# validate the entries
|
||||
# If one entry is an invalid concept, rollback everything
|
||||
for concept_id, precedence, associativity in list_of_def:
|
||||
if concept_id == BuiltinConcepts.UNKNOWN_CONCEPT:
|
||||
return self.ret(self.name,
|
||||
False,
|
||||
self.new(BuiltinConcepts.ERROR, body=f"Concept {concept_id} is not known"))
|
||||
|
||||
sya_def = self.cache_manager.copy(self.RESOLVED_CONCEPTS_SYA_DEFINITION_ENTRY) or {}
|
||||
|
||||
# update the definitions
|
||||
for concept_id, precedence, associativity in list_of_def:
|
||||
if precedence is None and associativity is None:
|
||||
try:
|
||||
del self.sya_definitions[concept_id]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
sya_def[concept_id] = (precedence, associativity)
|
||||
|
||||
# put in cache
|
||||
self.cache_manager.put(self.RESOLVED_CONCEPTS_SYA_DEFINITION_ENTRY, False, sya_def)
|
||||
|
||||
return self.ret(self.name, True, self.new(BuiltinConcepts.SUCCESS))
|
||||
|
||||
def test_only_add_in_cache(self, concept: Concept):
|
||||
"""
|
||||
Adds a concept template in cache.
|
||||
The cache is used as a proxy before looking at sdp
|
||||
:param concept:
|
||||
:return:
|
||||
"""
|
||||
|
||||
# sanity check
|
||||
if concept.key is None:
|
||||
concept.init_key()
|
||||
|
||||
if concept.key is None:
|
||||
raise KeyError()
|
||||
|
||||
self.cache_manager.add_concept(concept)
|
||||
|
||||
return concept
|
||||
|
||||
|
||||
def to_profile():
|
||||
sheerka = Sheerka()
|
||||
|
||||
Reference in New Issue
Block a user