Refactored sheerka class: splitted to use sub handlers. Refactored unit tests to use classes.
This commit is contained in:
@@ -0,0 +1,387 @@
|
||||
from enum import Enum
|
||||
|
||||
from core.concept import Concept, ConceptParts
|
||||
|
||||
|
||||
class BuiltinConcepts(Enum):
|
||||
"""
|
||||
List of builtin concepts that do no need any specific implementation
|
||||
Please note that the value of the enum is informal. It is not used in the system
|
||||
For example, the concept 'NODE' DOES NOT have the key, the id or whatever 200
|
||||
The key if the name of the concept
|
||||
The id is a sequential number given just before the concept is saved in sdp
|
||||
|
||||
The values of the enum is not used the code
|
||||
"""
|
||||
SHEERKA = "sheerka"
|
||||
|
||||
BEFORE_PARSING = "before parsing" # activated before evaluation by the parsers
|
||||
PARSING = "parsing" # activated during the parsing. It contains the text to parse
|
||||
AFTER_PARSING = "after parsing" # after parsing
|
||||
BEFORE_EVALUATION = "before evaluation" # before evaluation
|
||||
EVALUATION = "evaluation" # activated when the parsing process seems to be finished
|
||||
AFTER_EVALUATION = "after evaluation" # activated when the parsing process seems to be finished
|
||||
BEFORE_RENDERING = "before rendering" # activate before the output is rendered
|
||||
RENDERING = "rendering" # rendering the response from sheerka
|
||||
AFTER_RENDERING = "after rendering" # rendering the response from sheerka
|
||||
|
||||
USER_INPUT = "user input" # represent an input from an user
|
||||
SUCCESS = "success"
|
||||
ERROR = "error"
|
||||
UNKNOWN_CONCEPT = "unknown concept" # the request concept is not recognized
|
||||
CANNOT_RESOLVE_CONCEPT = "cannot resolve concept" # when too many concepts with the same name
|
||||
RETURN_VALUE = "return value" # a value is returned
|
||||
CONCEPT_TOO_LONG = "concept too long" # concept cannot be processed by exactConcept parser
|
||||
NEW_CONCEPT = "new concept" # when a new concept is added
|
||||
UNKNOWN_PROPERTY = "unknown property" # when requesting for a unknown property
|
||||
PARSER_RESULT = "parser result"
|
||||
TOO_MANY_SUCCESS = "too many success" # when expecting a limited number of successful return value
|
||||
TOO_MANY_ERRORS = "too many errors" # when expecting a limited number of successful return value
|
||||
NOT_FOR_ME = "not for me" # a parser recognize that the entry is not meant for it
|
||||
IS_EMPTY = "is empty" # when a set is empty
|
||||
INVALID_RETURN_VALUE = "invalid return value" # the return value of an evaluator is not correct
|
||||
CONCEPT_ALREADY_DEFINED = "concept already defined" # when you try to add the same concept twice
|
||||
NOP = "no operation" # no operation concept. Does nothing
|
||||
CONCEPT_EVAL_ERROR = "concept evaluation error" # cannot evaluate a property or metadata of a concept
|
||||
ENUMERATION = "enum" # represents a list or a set
|
||||
LIST = "list" # represents a list
|
||||
CONCEPT_ALREADY_IN_SET = "concept already in set"
|
||||
EVALUATOR_PRE_PROCESS = "evaluator pre process" # used modify / tweak behaviour of evaluators
|
||||
CONCEPT_EVAL_REQUESTED = "concept eval requested"
|
||||
REDUCE_REQUESTED = "reduce requested" # remove meaningless error when possible
|
||||
NOT_A_SET = "not a set" # the concept has no entry in sets
|
||||
|
||||
NODE = "node"
|
||||
GENERIC_NODE = "generic node"
|
||||
IDENTIFIER_NODE = "identifier node"
|
||||
|
||||
def __repr__(self):
|
||||
return "__" + self.name
|
||||
|
||||
def __str__(self):
|
||||
return "__" + self.name
|
||||
|
||||
|
||||
BuiltinUnique = [
|
||||
BuiltinConcepts.BEFORE_PARSING,
|
||||
BuiltinConcepts.PARSING,
|
||||
BuiltinConcepts.AFTER_PARSING,
|
||||
BuiltinConcepts.BEFORE_EVALUATION,
|
||||
BuiltinConcepts.EVALUATION,
|
||||
BuiltinConcepts.AFTER_EVALUATION,
|
||||
BuiltinConcepts.BEFORE_RENDERING,
|
||||
BuiltinConcepts.RENDERING,
|
||||
BuiltinConcepts.AFTER_RENDERING,
|
||||
BuiltinConcepts.SUCCESS,
|
||||
BuiltinConcepts.NOP,
|
||||
BuiltinConcepts.CONCEPT_EVAL_REQUESTED,
|
||||
BuiltinConcepts.REDUCE_REQUESTED,
|
||||
]
|
||||
|
||||
BuiltinErrors = [str(e) for e in {
|
||||
BuiltinConcepts.ERROR,
|
||||
BuiltinConcepts.UNKNOWN_CONCEPT,
|
||||
BuiltinConcepts.CANNOT_RESOLVE_CONCEPT,
|
||||
BuiltinConcepts.CONCEPT_TOO_LONG,
|
||||
BuiltinConcepts.UNKNOWN_PROPERTY,
|
||||
BuiltinConcepts.TOO_MANY_SUCCESS,
|
||||
BuiltinConcepts.TOO_MANY_ERRORS,
|
||||
BuiltinConcepts.INVALID_RETURN_VALUE,
|
||||
BuiltinConcepts.CONCEPT_ALREADY_DEFINED,
|
||||
BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
|
||||
BuiltinConcepts.NOT_A_SET,
|
||||
}]
|
||||
|
||||
"""
|
||||
Some concepts have a specific implementation
|
||||
It's mainly to ease the usage
|
||||
"""
|
||||
|
||||
|
||||
class UserInputConcept(Concept):
|
||||
def __init__(self, text=None, user_name=None):
|
||||
super().__init__(BuiltinConcepts.USER_INPUT, True, False, BuiltinConcepts.USER_INPUT)
|
||||
self.set_metadata_value(ConceptParts.BODY, text)
|
||||
self.set_prop("user_name", user_name)
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self.body
|
||||
|
||||
@property
|
||||
def user_name(self):
|
||||
return self.props["user_name"].value
|
||||
|
||||
def __repr__(self):
|
||||
return f"({self.id}){self.name}: '{self.body}'"
|
||||
|
||||
|
||||
class ErrorConcept(Concept):
|
||||
def __init__(self, error=None):
|
||||
super().__init__(BuiltinConcepts.ERROR, True, False, BuiltinConcepts.ERROR)
|
||||
self.set_metadata_value(ConceptParts.BODY, error)
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
def __repr__(self):
|
||||
return f"({self.id}){self.name}: {self.body}"
|
||||
|
||||
|
||||
class UnknownConcept(Concept):
|
||||
def __init__(self, metadata=None):
|
||||
super().__init__(BuiltinConcepts.UNKNOWN_CONCEPT, True, False, BuiltinConcepts.UNKNOWN_CONCEPT)
|
||||
self.set_metadata_value(ConceptParts.BODY, metadata)
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
def __repr__(self):
|
||||
return f"({self.id}){self.name}: {self.body}"
|
||||
|
||||
|
||||
class ReturnValueConcept(Concept):
|
||||
"""
|
||||
This class represents the result of a data flow processing
|
||||
It's the main input for the evaluators
|
||||
"""
|
||||
|
||||
def __init__(self, who=None, status=None, value=None, message=None, parents=None):
|
||||
super().__init__(BuiltinConcepts.RETURN_VALUE, True, False, BuiltinConcepts.RETURN_VALUE)
|
||||
self.set_metadata_value(ConceptParts.BODY, value)
|
||||
self.set_prop("who", who)
|
||||
self.set_prop("status", status)
|
||||
self.set_prop("message", message)
|
||||
self.set_prop("parents", parents)
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
@property
|
||||
def who(self):
|
||||
return self.props["who"].value
|
||||
|
||||
@who.setter
|
||||
def who(self, value):
|
||||
self.set_prop("who", value)
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return self.props["status"].value
|
||||
|
||||
@status.setter
|
||||
def status(self, value):
|
||||
self.set_prop("status", value)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.body
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self.set_metadata_value(ConceptParts.BODY, value)
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
return self.props["message"].value
|
||||
|
||||
@message.setter
|
||||
def message(self, value):
|
||||
self.set_prop("message", value)
|
||||
|
||||
@property
|
||||
def parents(self):
|
||||
return self.props["parents"].value
|
||||
|
||||
@parents.setter
|
||||
def parents(self, value):
|
||||
self.set_prop("parents", value)
|
||||
|
||||
def __repr__(self):
|
||||
return f"ReturnValue(who={self.who}, status={self.status}, value={self.value}, message={self.message})"
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, ReturnValueConcept):
|
||||
return False
|
||||
|
||||
return self.who == other.who and \
|
||||
self.status == other.status and \
|
||||
self.value == other.value and \
|
||||
self.message == other.message
|
||||
|
||||
def __hash__(self):
|
||||
if hasattr(self.value, "__iter__") and not isinstance(self.value, str):
|
||||
value_hash = hash(tuple(self.value))
|
||||
else:
|
||||
value_hash = hash(self.value)
|
||||
|
||||
return hash((self.who, self.status, value_hash))
|
||||
|
||||
|
||||
class UnknownPropertyConcept(Concept):
|
||||
"""
|
||||
This error is raised when, during sheerka.new(), an unknown property is asked
|
||||
"""
|
||||
|
||||
def __init__(self, property_name=None, concept=None):
|
||||
super().__init__(BuiltinConcepts.UNKNOWN_PROPERTY, True, False, BuiltinConcepts.UNKNOWN_PROPERTY)
|
||||
self.set_metadata_value(ConceptParts.BODY, property_name)
|
||||
self.set_prop("concept", concept)
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
def __repr__(self):
|
||||
return f"UnknownProperty(property={self.property_name}, concept={self.concept})"
|
||||
|
||||
@property
|
||||
def concept(self):
|
||||
return self.props["concept"].value
|
||||
|
||||
@property
|
||||
def property_name(self):
|
||||
return self.body
|
||||
|
||||
|
||||
class ParserResultConcept(Concept):
|
||||
"""
|
||||
Result of a parsing
|
||||
"""
|
||||
|
||||
def __init__(self, parser=None, source=None, value=None, try_parsed=None):
|
||||
super().__init__(BuiltinConcepts.PARSER_RESULT, True, False, BuiltinConcepts.PARSER_RESULT)
|
||||
self.set_metadata_value(ConceptParts.BODY, value)
|
||||
self.set_prop("parser", parser)
|
||||
self.set_prop("source", source)
|
||||
self.set_prop("try_parsed", try_parsed) # in case of error, what was found before the error
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
def __repr__(self):
|
||||
text = f"ParserResult(parser={self.props['parser'].value}"
|
||||
source = self.props['source'].value
|
||||
text += f", source='{source}')" if source else f", body='{self.body}')"
|
||||
return text
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, ParserResultConcept):
|
||||
return False
|
||||
|
||||
return self.source == other.source and \
|
||||
self.parser == other.parser and \
|
||||
self.body == other.body and \
|
||||
self.try_parsed == other.try_parsed
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.metadata.name)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.body
|
||||
|
||||
@property
|
||||
def try_parsed(self):
|
||||
return self.props["try_parsed"].value
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return self.props["source"].value
|
||||
|
||||
@property
|
||||
def parser(self):
|
||||
return self.props["parser"].value
|
||||
|
||||
|
||||
class InvalidReturnValueConcept(Concept):
|
||||
"""
|
||||
Error returned when an evaluator is not correctly coded
|
||||
The accepted return value are
|
||||
ReturnValueConcept, list of ReturnValueConcept or None
|
||||
"""
|
||||
|
||||
def __init__(self, return_value=None, evaluator=None):
|
||||
super().__init__(
|
||||
BuiltinConcepts.INVALID_RETURN_VALUE,
|
||||
True,
|
||||
False,
|
||||
BuiltinConcepts.INVALID_RETURN_VALUE)
|
||||
self.set_metadata_value(ConceptParts.BODY, return_value)
|
||||
self.set_prop("evaluator", evaluator)
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
|
||||
class ConceptEvalError(Concept):
|
||||
def __init__(self, error=None, concept=None, property_name=None):
|
||||
super().__init__(BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
True,
|
||||
False,
|
||||
BuiltinConcepts.CONCEPT_EVAL_ERROR)
|
||||
self.set_metadata_value(ConceptParts.BODY, error)
|
||||
self.set_prop("concept", concept)
|
||||
self.set_prop("property_name", property_name)
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
def __repr__(self):
|
||||
return f"ConceptEvalError(error={self.error}, concept={self.concept}, property={self.property_name})"
|
||||
|
||||
@property
|
||||
def error(self):
|
||||
return self.body
|
||||
|
||||
@property
|
||||
def concept(self):
|
||||
return self.props["concept"].value
|
||||
|
||||
@property
|
||||
def property_name(self):
|
||||
return self.props["property_name"].value
|
||||
|
||||
|
||||
class EnumerationConcept(Concept):
|
||||
def __init__(self, iteration=None):
|
||||
super().__init__(BuiltinConcepts.ENUMERATION, True, False, BuiltinConcepts.ENUMERATION)
|
||||
self.set_metadata_value(ConceptParts.BODY, iteration)
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.body)
|
||||
|
||||
|
||||
class ListConcept(Concept):
|
||||
def __init__(self, items=None):
|
||||
super().__init__(BuiltinConcepts.LIST, True, False, BuiltinConcepts.LIST)
|
||||
self.set_metadata_value(ConceptParts.BODY, items or [])
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
def append(self, obj):
|
||||
self.body.append(obj)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.body)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.body[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.body[key] = value
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.body)
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.body
|
||||
|
||||
|
||||
class ConceptAlreadyInSet(Concept):
|
||||
def __init__(self, concept=None, concept_set=None):
|
||||
super().__init__(BuiltinConcepts.CONCEPT_ALREADY_IN_SET,
|
||||
True,
|
||||
False,
|
||||
BuiltinConcepts.CONCEPT_ALREADY_IN_SET)
|
||||
self.set_metadata_value(ConceptParts.BODY, concept)
|
||||
self.set_prop("concept_set", concept_set)
|
||||
self.metadata.is_evaluated = True
|
||||
|
||||
def __repr__(self):
|
||||
return f"ConceptAlreadyInSet(concept={self.concept}, concept_set={self.concept_set})"
|
||||
|
||||
@property
|
||||
def concept(self):
|
||||
return self.body
|
||||
|
||||
@property
|
||||
def concept_set(self):
|
||||
return self.props["concept_set"].value
|
||||
Reference in New Issue
Block a user