Initialized logging

This commit is contained in:
2019-11-05 19:56:00 +01:00
parent b12204360e
commit 0d2adf1b6c
10 changed files with 448 additions and 249 deletions
+24 -11
View File
@@ -1,5 +1,8 @@
import hashlib
from enum import Enum
import logging
log = logging.getLogger(__name__)
class ConceptParts(Enum):
@@ -15,11 +18,9 @@ class Concept:
A concept is a the base object of our universe
Everything is a concept
"""
props_to_serialize = ("id", "name", "where", "pre", "post", "body", "desc")
props_to_serialize = ("id", "is_builtin", "name", "where", "pre", "post", "body", "desc")
key_name = "concepts"
def __init__(self, name=None, is_builtin=False, where=None, pre=None, post=None, body=None, desc=None):
def __init__(self, name=None, is_builtin=False, where=None, pre=None, post=None, body=None, desc=None, key=None):
self.name = name
self.is_builtin = is_builtin
self.where = where # condition to recognize variables in name
@@ -27,16 +28,16 @@ class Concept:
self.post = post # list of post conditions after calling the main function
self.body = body # main method, can also be the value of the concept
self.desc = desc
self.key = None
self.parent = None
self.id = None
self.key = key
self.props = [] # list of Property for this concept
self.functions = {} # list of helper functions
self.codes = {}
self.codes = {} # cached ast for the where, pre, post and body parts
def __repr__(self):
return f"({self.key}){self.name}"
return f"({self.id}){self.name}"
def __eq__(self, other):
if not isinstance(other, Concept):
@@ -50,6 +51,9 @@ class Concept:
def __hash__(self):
return hash(self.name)
def get_key(self):
return self.key
def add_codes(self, codes):
"""
From a dict of <ConceptParts, AST>
@@ -73,13 +77,22 @@ class Concept:
def to_dict(self):
props_as_dict = dict((prop, getattr(self, prop)) for prop in self.props_to_serialize)
props_as_dict["props"] = [(p.name, p.value) for p in self.props]
return props_as_dict
def from_dict(self, as_dict):
for prop in self.props_to_serialize:
setattr(self, prop, as_dict[prop])
if prop in as_dict:
setattr(self, prop, as_dict[prop])
if "props" in as_dict:
for n, v in as_dict["props"]:
self.props.append(Property(n, v))
return self
def update_from(self, other):
for prop in self.props_to_serialize:
setattr(self, prop, getattr(other, prop))
class ErrorConcept(Concept):
def __init__(self, where=None, pre=None, post=None, body=None, desc=None):
@@ -94,6 +107,6 @@ class Property:
Defines a behaviour of Concept
"""
def __init__(self, concept, value):
self.concept = concept
def __init__(self, name, value):
self.name = name
self.value = value
+93 -27
View File
@@ -1,10 +1,14 @@
from dataclasses import dataclass
from core.concept import Concept, ErrorConcept
from parsers.PythonParser import PythonParser
from core.concept import Concept, ErrorConcept, Property
from parsers.PythonParser import PythonParser, PythonGetNamesVisitor, PythonNode
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
from parsers.DefaultParser import DefaultParser, DefConceptNode
import logging
log = logging.getLogger(__name__)
class Singleton(type):
_instances = {}
@@ -27,6 +31,14 @@ class ReturnValue:
message: str = None
@dataclass
class ExecutionContext:
"""
To keep track of the execution of a request
"""
event_digest: str
class Sheerka(Concept, metaclass=Singleton):
"""
Main controller for the project
@@ -37,11 +49,16 @@ class Sheerka(Concept, metaclass=Singleton):
ERROR_CONCEPT_NAME = "Error"
SUCCESS_CONCEPT_NAME = "Success"
CONCEPTS_ENTRY = "Concepts"
BUILTIN_CONCEPTS_KEYS = "Builtins"
USER_CONCEPTS_KEYS = "Concepts"
def __init__(self):
log.debug("Starting Sheerka.")
super().__init__(Sheerka.NAME)
# list of all concepts known be the system
self.concepts = []
# cache of the most used concepts
self.concepts_cache = []
# a concept can be instantiated
# ex: File is a concept, but File('foo.txt') is an instance
@@ -52,20 +69,10 @@ class Sheerka(Concept, metaclass=Singleton):
# ex: hello => say('hello')
self.rules = []
self.create_builtin_concepts()
self.sdp = None
self.parsers = []
def create_builtin_concepts(self):
"""
Initializes the builtin concepts
:return: None
"""
self.concepts.append(self)
self.concepts.append(Concept(Sheerka.UNKNOWN_CONCEPT_NAME))
self.concepts.append(Concept(Sheerka.SUCCESS_CONCEPT_NAME))
self.concepts.append(Concept(Sheerka.ERROR_CONCEPT_NAME))
self.key = self.NAME
def initialize(self, root_folder=None):
"""
@@ -79,13 +86,54 @@ class Sheerka(Concept, metaclass=Singleton):
try:
self.sdp = SheerkaDataProvider(root_folder)
self.parsers.append(lambda text: DefaultParser(text, PythonParser))
except IOError as e:
return ReturnValue(False, self.get_concept(Sheerka.ERROR_CONCEPT_NAME, True), e)
return ReturnValue(True, self.get_concept(Sheerka.SUCCESS_CONCEPT_NAME, True))
if self.sdp.first_time:
self.sdp.set_key(self.USER_CONCEPTS_KEYS, 1000)
self.create_builtin_concepts()
except IOError as e:
return ReturnValue(False, self.get_concept(Sheerka.ERROR_CONCEPT_NAME), e)
return ReturnValue(True, self.get_concept(Sheerka.SUCCESS_CONCEPT_NAME))
def set_id_if_needed(self, obj, is_builtin):
"""
Set the key for the concept if needed
:param obj:
:param is_builtin:
:return:
"""
if obj.id is not None:
return
obj.id = self.sdp.get_next_key(self.BUILTIN_CONCEPTS_KEYS if is_builtin else self.USER_CONCEPTS_KEYS)
log.debug(f"Setting id '{obj.id}' to concept '{obj.name}'.")
def create_builtin_concepts(self):
"""
Initializes the builtin concepts
:return: None
"""
log.debug("Initializing builtin concepts")
builtins = [
self,
Concept(Sheerka.UNKNOWN_CONCEPT_NAME, key=Sheerka.UNKNOWN_CONCEPT_NAME),
Concept(Sheerka.SUCCESS_CONCEPT_NAME, key=Sheerka.SUCCESS_CONCEPT_NAME),
Concept(Sheerka.ERROR_CONCEPT_NAME, key=Sheerka.ERROR_CONCEPT_NAME),
]
for concept in builtins:
from_db = self.sdp.get_safe(self.CONCEPTS_ENTRY, concept.name)
if from_db is None:
log.debug(f"'{concept.name}' concept is not found. Adding.")
self.set_id_if_needed(concept, True)
self.sdp.add("init", self.CONCEPTS_ENTRY, concept, use_ref=True)
else:
log.debug(f"Found concept '{from_db}'. Updating.")
concept.update_from(from_db)
def eval(self, text):
# evt_digest = self.sdp.save_event(Event(text))
evt_digest = self.sdp.save_event(Event(text))
exec_context = ExecutionContext(evt_digest)
result = self.try_parse(text)
return_values = []
@@ -93,12 +141,13 @@ class Sheerka(Concept, metaclass=Singleton):
if not status:
return_values.append(ReturnValue(False, ErrorConcept(body=node)))
elif status and isinstance(node, DefConceptNode):
return_values.append(self.add_concept(node))
return_values.append(self.add_concept(exec_context, node))
return return_values
def try_parse(self, text):
result = []
log.debug(f"Parsing '{text}'")
for parser in self.parsers:
p = parser(text)
# try:
@@ -110,32 +159,49 @@ class Sheerka(Concept, metaclass=Singleton):
result.append((p.name, not p.has_error, p.error_sink if p.has_error else tree))
return result
def get_concept(self, name, is_builtin=False):
def get_concept(self, name):
"""
Given a concept name, tries to find it
:param name: name of the concept to look for
:param is_builtin: is it a builtin concept ?
:return: concept if found, UNKNOWN_CONCEPT otherwise
"""
for concept in self.concepts:
if concept.name == name and concept.is_builtin == is_builtin:
for concept in self.concepts_cache:
if concept.name == name:
return concept
return self.concepts[1]
return ErrorConcept()
def add_concept(self, def_concept_node: DefConceptNode):
def add_concept(self, exec_context, def_concept_node: DefConceptNode):
"""
Adds a new concept to the system
:param exec_context:
:param def_concept_node: DefConceptNode
:return: digest of the new concept
"""
# validate the node
get_names_visitor = PythonGetNamesVisitor()
concept = Concept(def_concept_node.name)
for prop in ("where", "pre", "post", "body"):
# put back the sources
concept_part_node = getattr(def_concept_node, prop)
value = concept_part_node.source if hasattr(concept_part_node, "source") else ""
setattr(concept, prop, value)
if isinstance(concept_part_node, PythonNode):
get_names_visitor.visit(concept_part_node.ast)
source = concept_part_node.source if hasattr(concept_part_node, "source") else ""
setattr(concept, prop, source)
# try to find variables (eg props)
for token in def_concept_node.tokens["name"]:
if token.value in get_names_visitor.names:
concept.props.append(Property(token.value, None))
concept.key = DefaultParser.get_concept_name(def_concept_node.tokens["name"], [p.name for p in concept.props])
concept.add_codes(def_concept_node.get_codes())
self.set_id_if_needed(concept, False)
self.sdp.add(exec_context.event_digest, self.CONCEPTS_ENTRY, concept, use_ref=True)
return ReturnValue(True, concept)
@staticmethod