Fixed #61 : SheerkaDebugManager: Add get_value()
Fixed #60 : Hash error when ReturnValue is a list of list Fixed #59 : Implement smart_get() Fixed #58 : SheerkaPromptCompleter: Cannot parse concept token Fixed #57 : SheerkaPrompt: Add concept autocompletion Fixed #56 : automatically backup command Fixed #54 : I can record execution status Fixed #53 : ConceptManager: modify_concept fails
This commit is contained in:
@@ -13,3 +13,4 @@ tests/**/*result_test
|
|||||||
testingPython.ipynb
|
testingPython.ipynb
|
||||||
profile*.txt
|
profile*.txt
|
||||||
*.prof
|
*.prof
|
||||||
|
new.sb
|
||||||
@@ -4,6 +4,13 @@ def concept q from q ? as question(q) pre is_question()
|
|||||||
set_is_lesser(__PRECEDENCE, q, 'Sya')
|
set_is_lesser(__PRECEDENCE, q, 'Sya')
|
||||||
set_auto_eval(c:q:)
|
set_auto_eval(c:q:)
|
||||||
|
|
||||||
|
def concept the x ret memory(x)
|
||||||
|
def concept a x where 'x is a concept' ret x
|
||||||
|
def concept an x where 'x is a concept' ret x
|
||||||
|
set_is_greatest(__PRECEDENCE, c:the x:, 'Sya')
|
||||||
|
set_is_greatest(__PRECEDENCE, c:a x:, 'Sya')
|
||||||
|
set_is_greatest(__PRECEDENCE, c:an x:, 'Sya')
|
||||||
|
|
||||||
def concept "x is a concept" as isinstance(x, Concept) pre is_question()
|
def concept "x is a concept" as isinstance(x, Concept) pre is_question()
|
||||||
|
|
||||||
# is a
|
# is a
|
||||||
@@ -38,9 +45,7 @@ set_is_lesser(__PRECEDENCE, c:x or y:, 'Sya')
|
|||||||
set_is_greater_than(__PRECEDENCE, c:x and y:, c:x or y:, 'Sya')
|
set_is_greater_than(__PRECEDENCE, c:x and y:, c:x or y:, 'Sya')
|
||||||
set_is_less_than(__PRECEDENCE, c:q:, c:x or y:, 'Sya')
|
set_is_less_than(__PRECEDENCE, c:q:, c:x or y:, 'Sya')
|
||||||
|
|
||||||
def concept the x ret memory(x)
|
|
||||||
def concept a x where 'x is a concept' ret x
|
|
||||||
def concept an x where 'x is a concept' ret x
|
|
||||||
|
|
||||||
# default
|
# default
|
||||||
def concept male
|
def concept male
|
||||||
@@ -67,4 +72,6 @@ def concept friday
|
|||||||
def concept saturday
|
def concept saturday
|
||||||
def concept sunday
|
def concept sunday
|
||||||
|
|
||||||
|
# questions
|
||||||
|
def concept what x is y pre is_question() where isa(x, adjective) as smart_get_attr(y, x)
|
||||||
|
def concept how is x pre is_question() as smart_get_attr(x, adjective)
|
||||||
Vendored
-1
@@ -112,7 +112,6 @@ class CacheManager:
|
|||||||
|
|
||||||
old_key = cache_def.get_key(old)
|
old_key = cache_def.get_key(old)
|
||||||
new_key = cache_def.get_key(new)
|
new_key = cache_def.get_key(new)
|
||||||
|
|
||||||
cache_def.cache.update(old_key, old, new_key, new, alt_sdp=alt_sdp)
|
cache_def.cache.update(old_key, old, new_key, new, alt_sdp=alt_sdp)
|
||||||
|
|
||||||
self.is_dirty = True
|
self.is_dirty = True
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from core.builtin_concepts_ids import BuiltinConcepts
|
from core.builtin_concepts_ids import BuiltinConcepts
|
||||||
from core.concept import Concept, ConceptParts
|
from core.concept import Concept, ConceptParts
|
||||||
from core.global_symbols import ErrorObj
|
from core.global_symbols import ErrorObj
|
||||||
|
from core.utils import compute_hash
|
||||||
|
|
||||||
|
|
||||||
class UserInputConcept(Concept):
|
class UserInputConcept(Concept):
|
||||||
@@ -91,12 +92,7 @@ class ReturnValueConcept(Concept):
|
|||||||
self.value == other.value
|
self.value == other.value
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
if hasattr(self.value, "__iter__") and not isinstance(self.value, str):
|
return hash((self.who, self.status, compute_hash(self.value)))
|
||||||
value_hash = hash(tuple(self.value))
|
|
||||||
else:
|
|
||||||
value_hash = hash(self.value)
|
|
||||||
|
|
||||||
return hash((self.who, self.status, value_hash))
|
|
||||||
|
|
||||||
|
|
||||||
class UnknownPropertyConcept(Concept, ErrorObj):
|
class UnknownPropertyConcept(Concept, ErrorObj):
|
||||||
|
|||||||
+6
-5
@@ -2,6 +2,7 @@ import hashlib
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
|
from typing import List
|
||||||
|
|
||||||
import core.utils
|
import core.utils
|
||||||
from core.builtin_concepts_ids import BuiltinDynamicAttrs
|
from core.builtin_concepts_ids import BuiltinDynamicAttrs
|
||||||
@@ -59,6 +60,7 @@ class ConceptMetadata:
|
|||||||
is_evaluated: bool = False # True is the concept is evaluated by sheerka.eval_concept()
|
is_evaluated: bool = False # True is the concept is evaluated by sheerka.eval_concept()
|
||||||
need_validation = False # True if the properties of the concept need to be validated
|
need_validation = False # True if the properties of the concept need to be validated
|
||||||
full_serialization: bool = False # If True, the full object will be serialized, rather than just the diff
|
full_serialization: bool = False # If True, the full object will be serialized, rather than just the diff
|
||||||
|
all_attributes: List[str] = None # list of instance attributes
|
||||||
|
|
||||||
|
|
||||||
ALL_ATTRIBUTES = {}
|
ALL_ATTRIBUTES = {}
|
||||||
@@ -157,7 +159,6 @@ class Concept:
|
|||||||
self._original_definition_hash = None # concept hash before any alteration of the metadata
|
self._original_definition_hash = None # concept hash before any alteration of the metadata
|
||||||
self._format = None # how to print the concept
|
self._format = None # how to print the concept
|
||||||
self._hints = {} # extra processing information to help processing
|
self._hints = {} # extra processing information to help processing
|
||||||
self._all_attributes = None # instance attributes
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
text = f"({self._metadata.id}){self._metadata.name}"
|
text = f"({self._metadata.id}){self._metadata.name}"
|
||||||
@@ -209,7 +210,7 @@ class Concept:
|
|||||||
return hash(self._metadata.name)
|
return hash(self._metadata.name)
|
||||||
|
|
||||||
def get_all_attributes(self):
|
def get_all_attributes(self):
|
||||||
return self._all_attributes
|
return self._metadata.all_attributes
|
||||||
|
|
||||||
def def_var(self, var_name, default_value=None):
|
def def_var(self, var_name, default_value=None):
|
||||||
"""
|
"""
|
||||||
@@ -465,9 +466,9 @@ class Concept:
|
|||||||
# I am not sure how cost efficient it is to check for new attribute everytime
|
# I am not sure how cost efficient it is to check for new attribute everytime
|
||||||
# Need to find a better way
|
# Need to find a better way
|
||||||
if name not in get_concept_attrs(self):
|
if name not in get_concept_attrs(self):
|
||||||
if self._all_attributes is None:
|
if self._metadata.all_attributes is None:
|
||||||
self._all_attributes = get_concept_attrs(self).copy()
|
self._metadata.all_attributes = get_concept_attrs(self).copy()
|
||||||
self._all_attributes.append(name)
|
self._metadata.all_attributes.append(name)
|
||||||
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print(f"Cannot set {name}")
|
print(f"Cannot set {name}")
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ EVENT_RULE_PRECEDENCE_MODIFIED = "evt_rp_m"
|
|||||||
EVENT_CONTEXT_DISPOSED = "evt_ctx_d"
|
EVENT_CONTEXT_DISPOSED = "evt_ctx_d"
|
||||||
EVENT_USER_INPUT_EVALUATED = "evt_ui_e"
|
EVENT_USER_INPUT_EVALUATED = "evt_ui_e"
|
||||||
EVENT_CONCEPT_CREATED = "evt_c_c"
|
EVENT_CONCEPT_CREATED = "evt_c_c"
|
||||||
|
EVENT_CONCEPT_MODIFIED = "evt_c_m"
|
||||||
EVENT_CONCEPT_DELETED = "evt_c_d"
|
EVENT_CONCEPT_DELETED = "evt_c_d"
|
||||||
EVENT_CONCEPT_ID_DELETED = "evt_c_id_d"
|
EVENT_CONCEPT_ID_DELETED = "evt_c_id_d"
|
||||||
EVENT_RULE_CREATED = "evt_r_c"
|
EVENT_RULE_CREATED = "evt_r_c"
|
||||||
@@ -79,3 +80,7 @@ class SyaAssociativity(Enum):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
|
SHEERKA_BACKUP_FOLDER = "SHEERKA_BACKUP_FOLDER"
|
||||||
|
SHEERKA_BACKUP_FILE = "SHEERKA_BACKUP_FILE"
|
||||||
|
|||||||
@@ -497,3 +497,17 @@ class ExecutionContext:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def set_state_is_modified(self):
|
||||||
|
root_parent = self
|
||||||
|
while root_parent._parent is not None:
|
||||||
|
root_parent = root_parent._parent
|
||||||
|
|
||||||
|
root_parent.values["is_state_modified"] = True
|
||||||
|
|
||||||
|
def is_state_modified(self):
|
||||||
|
root_parent = self
|
||||||
|
while root_parent._parent is not None:
|
||||||
|
root_parent = root_parent._parent
|
||||||
|
|
||||||
|
return "is_state_modified" in root_parent.values and bool(root_parent.values["is_state_modified"]) is True
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ class Sheerka(Concept):
|
|||||||
self.save_execution_context = True
|
self.save_execution_context = True
|
||||||
self.enable_process_return_values = True
|
self.enable_process_return_values = True
|
||||||
self.enable_process_rules = True
|
self.enable_process_rules = True
|
||||||
|
self.enable_commands_backup = True
|
||||||
|
|
||||||
self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods
|
self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods
|
||||||
self.sheerka_methods = {
|
self.sheerka_methods = {
|
||||||
@@ -158,8 +159,6 @@ class Sheerka(Concept):
|
|||||||
Loads the current configuration
|
Loads the current configuration
|
||||||
Notes that when it's the first time, it also create the needed working folders
|
Notes that when it's the first time, it also create the needed working folders
|
||||||
:param root_folder: root configuration folder
|
:param root_folder: root configuration folder
|
||||||
:param save_execution_context:
|
|
||||||
:param enable_process_return_values:
|
|
||||||
:return: ReturnValue(Success or Error)
|
:return: ReturnValue(Success or Error)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -167,6 +166,7 @@ class Sheerka(Concept):
|
|||||||
self.enable_process_return_values = kwargs.get("enable_process_return_values",
|
self.enable_process_return_values = kwargs.get("enable_process_return_values",
|
||||||
self.enable_process_return_values)
|
self.enable_process_return_values)
|
||||||
self.enable_process_rules = kwargs.get("enable_process_rules", self.enable_process_rules)
|
self.enable_process_rules = kwargs.get("enable_process_rules", self.enable_process_rules)
|
||||||
|
self.enable_commands_backup = kwargs.get("enable_commands_backup", self.enable_commands_backup)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.during_initialisation = True
|
self.during_initialisation = True
|
||||||
@@ -370,7 +370,7 @@ class Sheerka(Concept):
|
|||||||
if self.enable_process_rules:
|
if self.enable_process_rules:
|
||||||
ret = self.execute_rules(execution_context, ret, RULES_EVALUATE_STEPS, RULES_EXECUTE_STEPS)
|
ret = self.execute_rules(execution_context, ret, RULES_EVALUATE_STEPS, RULES_EXECUTE_STEPS)
|
||||||
|
|
||||||
if self.om.is_dirty:
|
if self.om.is_dirty():
|
||||||
self.om.commit(execution_context)
|
self.om.commit(execution_context)
|
||||||
|
|
||||||
self.publish(execution_context, EVENT_USER_INPUT_EVALUATED)
|
self.publish(execution_context, EVENT_USER_INPUT_EVALUATED)
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import sys
|
import os
|
||||||
import time
|
import time
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
from core.builtin_concepts_ids import BuiltinConcepts, BuiltinContainers
|
from core.builtin_concepts_ids import BuiltinConcepts, BuiltinContainers
|
||||||
from core.builtin_helpers import ensure_concept_or_rule
|
from core.builtin_helpers import ensure_concept_or_rule
|
||||||
from core.concept import Concept
|
from core.concept import Concept
|
||||||
|
from core.global_symbols import SHEERKA_BACKUP_FOLDER
|
||||||
from core.sheerka.services.SheerkaHistoryManager import SheerkaHistoryManager
|
from core.sheerka.services.SheerkaHistoryManager import SheerkaHistoryManager
|
||||||
from core.sheerka.services.SheerkaMemory import SheerkaMemory
|
from core.sheerka.services.SheerkaMemory import SheerkaMemory
|
||||||
from core.sheerka.services.sheerka_service import BaseService
|
from core.sheerka.services.sheerka_service import BaseService
|
||||||
|
|
||||||
CONCEPTS_FILE_LITE = "_concepts_lite.txt"
|
CONCEPTS_FILE_FULL = "full.sb"
|
||||||
CONCEPTS_FILE_FULL = "_concepts_full.txt"
|
|
||||||
CONCEPTS_FILE_TO_USE = CONCEPTS_FILE_FULL
|
CONCEPTS_FILE_TO_USE = CONCEPTS_FILE_FULL
|
||||||
|
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ class SheerkaAdmin(BaseService):
|
|||||||
|
|
||||||
return self.sheerka.om.current_sdp()
|
return self.sheerka.om.current_sdp()
|
||||||
|
|
||||||
def restore(self, concept_file=CONCEPTS_FILE_TO_USE):
|
def restore(self, backup_file=CONCEPTS_FILE_TO_USE):
|
||||||
"""
|
"""
|
||||||
Restore the state with all previous valid concept definitions
|
Restore the state with all previous valid concept definitions
|
||||||
:return:
|
:return:
|
||||||
@@ -79,7 +79,7 @@ class SheerkaAdmin(BaseService):
|
|||||||
def restore_from_file(file_name):
|
def restore_from_file(file_name):
|
||||||
_nb_lines, _nb_instructions, _nb_lines_in_error = 0, 0, 0
|
_nb_lines, _nb_instructions, _nb_lines_in_error = 0, 0, 0
|
||||||
_min_time, _max_time = None, None
|
_min_time, _max_time = None, None
|
||||||
file_path = path.join(path.dirname(sys.argv[0]), file_name)
|
file_path = path.join(backup_folder, file_name)
|
||||||
if not path.exists(file_path):
|
if not path.exists(file_path):
|
||||||
print(f"\u001b[31mFile '{file_path}' is not found !\u001b[0m")
|
print(f"\u001b[31mFile '{file_path}' is not found !\u001b[0m")
|
||||||
return 0, 0, 1
|
return 0, 0, 1
|
||||||
@@ -90,7 +90,7 @@ class SheerkaAdmin(BaseService):
|
|||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
|
||||||
if line.startswith("#import "):
|
if line.startswith("#import "):
|
||||||
to_import = "_concepts_" + line[8:] + ".txt"
|
to_import = line[8:] + ".sb"
|
||||||
print(f" ==== Importing {to_import} ==== ")
|
print(f" ==== Importing {to_import} ==== ")
|
||||||
res = restore_from_file(to_import)
|
res = restore_from_file(to_import)
|
||||||
_nb_lines += res[0]
|
_nb_lines += res[0]
|
||||||
@@ -122,8 +122,12 @@ class SheerkaAdmin(BaseService):
|
|||||||
|
|
||||||
return _nb_lines, _nb_instructions, _nb_lines_in_error, _min_time, _max_time
|
return _nb_lines, _nb_instructions, _nb_lines_in_error, _min_time, _max_time
|
||||||
|
|
||||||
if not concept_file.startswith("_concepts"):
|
backup_folder = os.getenv(SHEERKA_BACKUP_FOLDER)
|
||||||
concept_file = f"_concepts_{concept_file}.txt"
|
if backup_folder is None:
|
||||||
|
return self.sheerka.ret(self.NAME, False, self.sheerka.err(SHEERKA_BACKUP_FOLDER + " is not defined"))
|
||||||
|
|
||||||
|
if not backup_file.endswith(".sb"):
|
||||||
|
backup_file = backup_file + ".sb"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
start = time.time_ns()
|
start = time.time_ns()
|
||||||
@@ -132,7 +136,7 @@ class SheerkaAdmin(BaseService):
|
|||||||
enable_process_return_values_previous_value = self.sheerka.enable_process_return_values
|
enable_process_return_values_previous_value = self.sheerka.enable_process_return_values
|
||||||
self.sheerka.enable_process_return_values = False
|
self.sheerka.enable_process_return_values = False
|
||||||
|
|
||||||
nb_lines, nb_instructions, nb_lines_in_error, min_time, max_time = restore_from_file(concept_file)
|
nb_lines, nb_instructions, nb_lines_in_error, min_time, max_time = restore_from_file(backup_file)
|
||||||
|
|
||||||
self.sheerka.enable_process_return_values = enable_process_return_values_previous_value
|
self.sheerka.enable_process_return_values = enable_process_return_values_previous_value
|
||||||
self.sheerka.save_execution_context = True
|
self.sheerka.save_execution_context = True
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ from core.builtin_concepts_ids import BuiltinConcepts, AllBuiltinConcepts, Built
|
|||||||
from core.builtin_helpers import ensure_concept, ensure_bnf
|
from core.builtin_helpers import ensure_concept, ensure_bnf
|
||||||
from core.concept import Concept, DEFINITION_TYPE_DEF, DEFINITION_TYPE_BNF, freeze_concept_attrs, ConceptMetadata, \
|
from core.concept import Concept, DEFINITION_TYPE_DEF, DEFINITION_TYPE_BNF, freeze_concept_attrs, ConceptMetadata, \
|
||||||
VARIABLE_PREFIX
|
VARIABLE_PREFIX
|
||||||
from core.global_symbols import EVENT_CONCEPT_CREATED, NotInit, NotFound, ErrorObj, EVENT_CONCEPT_DELETED, NoFirstToken
|
from core.global_symbols import EVENT_CONCEPT_CREATED, NotInit, NotFound, ErrorObj, EVENT_CONCEPT_DELETED, NoFirstToken, \
|
||||||
|
EVENT_CONCEPT_MODIFIED
|
||||||
from core.sheerka.services.sheerka_service import BaseService
|
from core.sheerka.services.sheerka_service import BaseService
|
||||||
from core.tokenizer import Tokenizer, TokenKind
|
from core.tokenizer import Tokenizer, TokenKind
|
||||||
from parsers.BnfNodeParser import RegExDef
|
from parsers.BnfNodeParser import RegExDef
|
||||||
@@ -118,6 +119,7 @@ class SheerkaConceptManager(BaseService):
|
|||||||
self.sheerka.bind_service_method(self.set_id_if_needed, True)
|
self.sheerka.bind_service_method(self.set_id_if_needed, True)
|
||||||
self.sheerka.bind_service_method(self.set_attr, True)
|
self.sheerka.bind_service_method(self.set_attr, True)
|
||||||
self.sheerka.bind_service_method(self.get_attr, False)
|
self.sheerka.bind_service_method(self.get_attr, False)
|
||||||
|
self.sheerka.bind_service_method(self.smart_get_attr, False)
|
||||||
self.sheerka.bind_service_method(self.set_property, True, as_name="set_prop")
|
self.sheerka.bind_service_method(self.set_property, True, as_name="set_prop")
|
||||||
self.sheerka.bind_service_method(self.get_property, False, as_name="get_prop")
|
self.sheerka.bind_service_method(self.get_property, False, as_name="get_prop")
|
||||||
self.sheerka.bind_service_method(self.get_by_key, False, visible=False)
|
self.sheerka.bind_service_method(self.get_by_key, False, visible=False)
|
||||||
@@ -346,7 +348,7 @@ class SheerkaConceptManager(BaseService):
|
|||||||
if modify_source:
|
if modify_source:
|
||||||
self._update_concept(context, concept, to_add, to_remove)
|
self._update_concept(context, concept, to_add, to_remove)
|
||||||
|
|
||||||
# KSI 2021-02-16 publish the modification of the concept only when someone needs it
|
sheerka.publish(context, EVENT_CONCEPT_MODIFIED, {"old": concept, "new": new_concept})
|
||||||
return sheerka.ret(self.NAME, True, sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=new_concept))
|
return sheerka.ret(self.NAME, True, sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=new_concept))
|
||||||
|
|
||||||
def remove_concept(self, context, concept):
|
def remove_concept(self, context, concept):
|
||||||
@@ -390,7 +392,6 @@ class SheerkaConceptManager(BaseService):
|
|||||||
def set_attr(self, concept, attribute, value):
|
def set_attr(self, concept, attribute, value):
|
||||||
"""
|
"""
|
||||||
Modifies an attribute of a concept (concept.values)
|
Modifies an attribute of a concept (concept.values)
|
||||||
:param context:
|
|
||||||
:param concept:
|
:param concept:
|
||||||
:param attribute:
|
:param attribute:
|
||||||
:param value:
|
:param value:
|
||||||
@@ -416,7 +417,6 @@ class SheerkaConceptManager(BaseService):
|
|||||||
def get_attr(self, concept, attribute):
|
def get_attr(self, concept, attribute):
|
||||||
"""
|
"""
|
||||||
Returns the attribute of a concept
|
Returns the attribute of a concept
|
||||||
:param context:
|
|
||||||
:param concept:
|
:param concept:
|
||||||
:param attribute:
|
:param attribute:
|
||||||
:return:
|
:return:
|
||||||
@@ -431,6 +431,72 @@ class SheerkaConceptManager(BaseService):
|
|||||||
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"#concept": concept, "#attr": attribute})
|
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"#concept": concept, "#attr": attribute})
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def smart_get_attr(self, concept, attribute):
|
||||||
|
|
||||||
|
def get_obj_value(c, concept_to_look_for):
|
||||||
|
"""
|
||||||
|
Return the body of the concept c if it is an instance of concept_to_look_for
|
||||||
|
Go deeper in the bodies until the concept_to_look_for is found
|
||||||
|
:param c:
|
||||||
|
:param concept_to_look_for:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
while c.body is not NotInit:
|
||||||
|
if self.sheerka.isinstance(c.body, concept_to_look_for):
|
||||||
|
return c.body
|
||||||
|
c = c.body
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
ensure_concept()
|
||||||
|
if not self.sheerka.is_success(concept):
|
||||||
|
return concept
|
||||||
|
|
||||||
|
value = self.get_attr(concept, attribute)
|
||||||
|
if not self.sheerka.isinstance(value, BuiltinConcepts.NOT_FOUND):
|
||||||
|
return value
|
||||||
|
|
||||||
|
if not isinstance(attribute, Concept):
|
||||||
|
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"#concept": concept, "#attr": attribute})
|
||||||
|
|
||||||
|
# try to be smart
|
||||||
|
result = []
|
||||||
|
|
||||||
|
# first look in children
|
||||||
|
for k, v in concept.variables().items():
|
||||||
|
attr_as_concept_key, attr_concept_id = core.utils.unstr_concept(k)
|
||||||
|
if attr_concept_id is not None:
|
||||||
|
attr_as_concept = self.sheerka.fast_resolve((attr_as_concept_key, attr_concept_id), return_new=False)
|
||||||
|
if attr_as_concept is not None and self.sheerka.isa(attribute, attr_as_concept):
|
||||||
|
if hasattr(v, "__iter__"):
|
||||||
|
for _v in v:
|
||||||
|
value = get_obj_value(_v, attribute)
|
||||||
|
if value is not None:
|
||||||
|
result.append(value)
|
||||||
|
else:
|
||||||
|
value = get_obj_value(v, attribute)
|
||||||
|
if value is not None:
|
||||||
|
result.append(value)
|
||||||
|
|
||||||
|
if len(result) > 0:
|
||||||
|
return result[0] if len(result) == 1 else result
|
||||||
|
|
||||||
|
# then try the ancestors
|
||||||
|
for k, v in concept.variables().items():
|
||||||
|
attr_as_concept_key, attr_concept_id = core.utils.unstr_concept(k)
|
||||||
|
if attr_concept_id is not None:
|
||||||
|
attr_as_concept = self.sheerka.fast_resolve((attr_as_concept_key, attr_concept_id), return_new=False)
|
||||||
|
if attr_as_concept is not None and self.sheerka.isa(attr_as_concept, attribute):
|
||||||
|
if isinstance(v, list):
|
||||||
|
result.extend(v)
|
||||||
|
else:
|
||||||
|
result.append(v)
|
||||||
|
|
||||||
|
if len(result) > 0:
|
||||||
|
return result[0] if len(result) == 1 else result
|
||||||
|
|
||||||
|
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"#concept": concept, "#attr": attribute})
|
||||||
|
|
||||||
def get_property(self, concept, prop):
|
def get_property(self, concept, prop):
|
||||||
"""
|
"""
|
||||||
Returns the value of a concept property
|
Returns the value of a concept property
|
||||||
|
|||||||
@@ -322,6 +322,7 @@ class SheerkaDebugManager(BaseService):
|
|||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.sheerka.bind_service_method(self.set_debug, True)
|
self.sheerka.bind_service_method(self.set_debug, True)
|
||||||
self.sheerka.bind_service_method(self.inspect, False)
|
self.sheerka.bind_service_method(self.inspect, False)
|
||||||
|
self.sheerka.bind_service_method(self.get_value, False)
|
||||||
self.sheerka.bind_service_method(self.get_debugger, False)
|
self.sheerka.bind_service_method(self.get_debugger, False)
|
||||||
self.sheerka.bind_service_method(self.reset_debug, False)
|
self.sheerka.bind_service_method(self.reset_debug, False)
|
||||||
self.sheerka.bind_service_method(self.set_debug_var, True)
|
self.sheerka.bind_service_method(self.set_debug_var, True)
|
||||||
@@ -802,6 +803,26 @@ class SheerkaDebugManager(BaseService):
|
|||||||
|
|
||||||
return self.get_inner_values(obj.body, **kwargs)
|
return self.get_inner_values(obj.body, **kwargs)
|
||||||
|
|
||||||
|
def get_value(self, obj):
|
||||||
|
"""
|
||||||
|
Returns the underlying values of an object
|
||||||
|
:param obj:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if isinstance(obj, list):
|
||||||
|
return [self.get_value(item) for item in obj]
|
||||||
|
|
||||||
|
if isinstance(obj, set):
|
||||||
|
return {self.get_value(item) for item in obj}
|
||||||
|
|
||||||
|
if isinstance(obj, tuple):
|
||||||
|
return tuple(self.get_value(item) for item in obj)
|
||||||
|
|
||||||
|
if isinstance(obj, Concept):
|
||||||
|
return self.get_value(self.sheerka.objvalue(obj))
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_debug_repr(obj, **kwargs):
|
def get_debug_repr(obj, **kwargs):
|
||||||
if kwargs.get("as_bag", False):
|
if kwargs.get("as_bag", False):
|
||||||
@@ -897,3 +918,4 @@ class SheerkaDebugManager(BaseService):
|
|||||||
del res["self"]
|
del res["self"]
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ class SheerkaHasAManager(BaseService):
|
|||||||
return self.sheerka.ret(
|
return self.sheerka.ret(
|
||||||
self.NAME,
|
self.NAME,
|
||||||
False,
|
False,
|
||||||
self.sheerka.new(BuiltinConcepts.PropertyAlreadyDefined,
|
self.sheerka.new(BuiltinConcepts.PROPERTY_ALREADY_DEFINED,
|
||||||
body=concept_b,
|
property_value=concept_b,
|
||||||
name=BuiltinConcepts.HASA,
|
property_name=BuiltinConcepts.HASA,
|
||||||
concept=concept_a))
|
concept=concept_a))
|
||||||
|
|
||||||
merged_concepts = merge_sets(concept_a.get_prop(BuiltinConcepts.HASA), {concept_b})
|
merged_concepts = merge_sets(concept_a.get_prop(BuiltinConcepts.HASA), {concept_b})
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from core.global_symbols import NotInit
|
||||||
from core.sheerka.services.sheerka_service import BaseService
|
from core.sheerka.services.sheerka_service import BaseService
|
||||||
from sdp.sheerkaDataProvider import Event
|
from sdp.sheerkaDataProvider import Event
|
||||||
|
|
||||||
@@ -7,14 +8,22 @@ hist = namedtuple("HistoryTest", "text status") # tests purposes only
|
|||||||
|
|
||||||
|
|
||||||
class History:
|
class History:
|
||||||
def __init__(self, event: Event, result):
|
def __init__(self, event: Event, result, extra_info):
|
||||||
self.event = event
|
self.event = event
|
||||||
self.result = result
|
self.result = result
|
||||||
self._status = None
|
self.extra_info = extra_info
|
||||||
|
self._status = NotInit
|
||||||
|
self._is_state_modified = NotInit
|
||||||
self._format_instructions = None
|
self._format_instructions = None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
msg = f"{self.event.get_digest()} {self.event.date.strftime('%d/%m/%Y %H:%M:%S')} : {self.event.message}"
|
if self.is_state_modified is NotInit:
|
||||||
|
state_modified_str = "[?]"
|
||||||
|
else:
|
||||||
|
state_modified_str = "[X]" if self.is_state_modified else "[ ]"
|
||||||
|
event_date = self.event.date.strftime('%d/%m/%Y %H:%M:%S')
|
||||||
|
|
||||||
|
msg = f"{self.event.get_digest()} {event_date} {state_modified_str} : {self.event.message}"
|
||||||
status = self.status
|
status = self.status
|
||||||
if status is not None:
|
if status is not None:
|
||||||
msg += f" => {status}"
|
msg += f" => {status}"
|
||||||
@@ -40,12 +49,28 @@ class History:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
if self._status:
|
if self._status is not NotInit:
|
||||||
|
return self._status
|
||||||
|
|
||||||
|
if self.extra_info and "status" in self.extra_info:
|
||||||
|
self._status = self.extra_info["status"]
|
||||||
return self._status
|
return self._status
|
||||||
|
|
||||||
self._status = self.result.get_status() if self.result else None
|
self._status = self.result.get_status() if self.result else None
|
||||||
return self._status
|
return self._status
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_state_modified(self):
|
||||||
|
if self._is_state_modified is not NotInit:
|
||||||
|
return self._is_state_modified
|
||||||
|
|
||||||
|
if self.extra_info and "is_state_modified" in self.extra_info:
|
||||||
|
self._is_state_modified = self.extra_info["is_state_modified"]
|
||||||
|
return self._is_state_modified
|
||||||
|
|
||||||
|
self._is_state_modified = self.result.is_state_modified() if self.result else None
|
||||||
|
return self._is_state_modified
|
||||||
|
|
||||||
def get_format_instructions(self):
|
def get_format_instructions(self):
|
||||||
return self._format_instructions
|
return self._format_instructions
|
||||||
|
|
||||||
@@ -73,4 +98,10 @@ class SheerkaHistoryManager(BaseService):
|
|||||||
result = self.sheerka.om.current_sdp().load_result(event.get_digest())
|
result = self.sheerka.om.current_sdp().load_result(event.get_digest())
|
||||||
except (IOError, KeyError):
|
except (IOError, KeyError):
|
||||||
result = None
|
result = None
|
||||||
yield History(event, result)
|
|
||||||
|
try:
|
||||||
|
extra_info = self.sheerka.om.current_sdp().load_result_extra_info(event.get_digest())
|
||||||
|
except (IOError, KeyError):
|
||||||
|
extra_info = None
|
||||||
|
|
||||||
|
yield History(event, result, extra_info)
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import ast
|
import ast
|
||||||
|
import os
|
||||||
|
|
||||||
from cache.Cache import Cache
|
from cache.Cache import Cache
|
||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
from core.global_symbols import EVENT_USER_INPUT_EVALUATED, EVENT_CONCEPT_CREATED, NotFound
|
from core.global_symbols import EVENT_USER_INPUT_EVALUATED, EVENT_CONCEPT_CREATED, NotFound, EVENT_CONCEPT_MODIFIED, \
|
||||||
|
EVENT_CONCEPT_DELETED, EVENT_CONCEPT_PRECEDENCE_MODIFIED, EVENT_RULE_CREATED, EVENT_RULE_DELETED, \
|
||||||
|
EVENT_RULE_PRECEDENCE_MODIFIED, SHEERKA_BACKUP_FOLDER, SHEERKA_BACKUP_FILE
|
||||||
from core.sheerka.services.sheerka_service import BaseService
|
from core.sheerka.services.sheerka_service import BaseService
|
||||||
from core.utils import CONSOLE_COLORS_MAP as CCM
|
from core.utils import CONSOLE_COLORS_MAP as CCM
|
||||||
from core.utils import as_bag
|
from core.utils import as_bag
|
||||||
@@ -40,6 +43,14 @@ class SheerkaResultManager(BaseService):
|
|||||||
self.sheerka.subscribe(EVENT_USER_INPUT_EVALUATED, self.user_input_evaluated)
|
self.sheerka.subscribe(EVENT_USER_INPUT_EVALUATED, self.user_input_evaluated)
|
||||||
self.sheerka.subscribe(EVENT_CONCEPT_CREATED, self.new_concept_created)
|
self.sheerka.subscribe(EVENT_CONCEPT_CREATED, self.new_concept_created)
|
||||||
|
|
||||||
|
self.sheerka.subscribe(EVENT_CONCEPT_CREATED, self.on_global_state_is_modified)
|
||||||
|
self.sheerka.subscribe(EVENT_CONCEPT_MODIFIED, self.on_global_state_is_modified)
|
||||||
|
self.sheerka.subscribe(EVENT_CONCEPT_DELETED, self.on_global_state_is_modified)
|
||||||
|
self.sheerka.subscribe(EVENT_CONCEPT_PRECEDENCE_MODIFIED, self.on_global_state_is_modified)
|
||||||
|
self.sheerka.subscribe(EVENT_RULE_CREATED, self.on_global_state_is_modified)
|
||||||
|
self.sheerka.subscribe(EVENT_RULE_DELETED, self.on_global_state_is_modified)
|
||||||
|
self.sheerka.subscribe(EVENT_RULE_PRECEDENCE_MODIFIED, self.on_global_state_is_modified)
|
||||||
|
|
||||||
def initialize_deferred(self, context, is_first_time):
|
def initialize_deferred(self, context, is_first_time):
|
||||||
self.restore_values(*self.state_vars)
|
self.restore_values(*self.state_vars)
|
||||||
|
|
||||||
@@ -59,6 +70,11 @@ class SheerkaResultManager(BaseService):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_predicate(**kwargs):
|
def get_predicate(**kwargs):
|
||||||
|
"""
|
||||||
|
Create a filtering predicate from a arguments
|
||||||
|
:param kwargs:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
if len(kwargs) == 0:
|
if len(kwargs) == 0:
|
||||||
return None
|
return None
|
||||||
res = []
|
res = []
|
||||||
@@ -241,13 +257,25 @@ class SheerkaResultManager(BaseService):
|
|||||||
:param execution_context:
|
:param execution_context:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
extra_info = {
|
||||||
|
"status": execution_context.get_status(),
|
||||||
|
"is_state_modified": execution_context.is_state_modified(),
|
||||||
|
}
|
||||||
if self.sheerka.save_execution_context:
|
if self.sheerka.save_execution_context:
|
||||||
try:
|
try:
|
||||||
self.sheerka.om.current_sdp().save_result(execution_context)
|
self.sheerka.om.current_sdp().save_result(execution_context)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print(f"{CCM['red']}Failed to save execution context. Reason: {ex}{CCM['reset']}")
|
print(f"{CCM['red']}Failed to save execution context. Reason: {ex}{CCM['reset']}")
|
||||||
|
|
||||||
|
# save extra info
|
||||||
|
try:
|
||||||
|
self.sheerka.om.current_sdp().save_result_extra_info(execution_context.event.get_digest(), extra_info)
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"{CCM['red']}Failed to save execution context extra info. Reason: {ex}{CCM['reset']}")
|
||||||
pass
|
pass
|
||||||
# self.log.error(f"Failed to save execution context. Reason: {ex}")
|
|
||||||
|
# write the command in a backup file if needed
|
||||||
|
self.backup_command(execution_context)
|
||||||
|
|
||||||
self.executions_contexts_cache.put(execution_context.event.get_digest(), execution_context)
|
self.executions_contexts_cache.put(execution_context.event.get_digest(), execution_context)
|
||||||
self.last_execution = execution_context
|
self.last_execution = execution_context
|
||||||
@@ -298,11 +326,21 @@ class SheerkaResultManager(BaseService):
|
|||||||
return self.last_errors
|
return self.last_errors
|
||||||
|
|
||||||
def new_concept_created(self, context, concept):
|
def new_concept_created(self, context, concept):
|
||||||
|
"""
|
||||||
|
Subscriber (callback) when a new concept is created
|
||||||
|
:param context:
|
||||||
|
:param concept:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
self.last_created_concept = concept
|
self.last_created_concept = concept
|
||||||
self.last_created_concept_id = concept.id
|
self.last_created_concept_id = concept.id
|
||||||
|
|
||||||
self.sheerka.record_var(context, self.NAME, "last_created_concept_id", concept.id)
|
self.sheerka.record_var(context, self.NAME, "last_created_concept_id", concept.id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def on_global_state_is_modified(context, *argv, **kwargs):
|
||||||
|
context.set_state_is_modified()
|
||||||
|
|
||||||
def get_last_created_concept(self, context):
|
def get_last_created_concept(self, context):
|
||||||
if self.last_created_concept:
|
if self.last_created_concept:
|
||||||
return self.last_created_concept
|
return self.last_created_concept
|
||||||
@@ -333,3 +371,24 @@ class SheerkaResultManager(BaseService):
|
|||||||
consumed = 0
|
consumed = 0
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def backup_command(self, execution_context):
|
||||||
|
if (self.sheerka.during_restore or
|
||||||
|
not execution_context.is_state_modified() or
|
||||||
|
not self.sheerka.enable_commands_backup):
|
||||||
|
return
|
||||||
|
|
||||||
|
folder = os.getenv(SHEERKA_BACKUP_FOLDER)
|
||||||
|
if folder is None:
|
||||||
|
print(f"{CCM['red']}Cannot backup command. Backup folder (env SHEERKA_BACKUP_FOLDER) is not defined.{CCM['reset']}")
|
||||||
|
return
|
||||||
|
|
||||||
|
file = os.getenv(SHEERKA_BACKUP_FILE)
|
||||||
|
if file is None:
|
||||||
|
print(f"{CCM['red']}Cannot backup command. Backup file (env SHEERKA_BACKUP_FILE) is not defined.{CCM['reset']}")
|
||||||
|
return
|
||||||
|
|
||||||
|
full_path = os.path.join(folder, file)
|
||||||
|
with open(full_path, "a") as f:
|
||||||
|
f.write(f"{execution_context.event.message}\n")
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,14 @@ class SheerkaVariableManager(BaseService):
|
|||||||
|
|
||||||
def __init__(self, sheerka):
|
def __init__(self, sheerka):
|
||||||
super().__init__(sheerka, order=3)
|
super().__init__(sheerka, order=3)
|
||||||
|
|
||||||
|
# Bound variables:
|
||||||
|
# automatically set services (including Sheerka) attributes at startup or at ontology creation / deletion
|
||||||
self.bound_variables = {
|
self.bound_variables = {
|
||||||
self.sheerka.name: {"enable_process_return_values", "save_execution_context", "enable_process_rules"}
|
self.sheerka.name: {"enable_process_return_values",
|
||||||
|
"save_execution_context",
|
||||||
|
"enable_process_rules",
|
||||||
|
"enable_commands_backup"}
|
||||||
}
|
}
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
@@ -88,6 +94,7 @@ class SheerkaVariableManager(BaseService):
|
|||||||
if who in self.bound_variables and key in self.bound_variables[who]:
|
if who in self.bound_variables and key in self.bound_variables[who]:
|
||||||
service = self.sheerka if who == self.sheerka.name else self.sheerka.services[who]
|
service = self.sheerka if who == self.sheerka.name else self.sheerka.services[who]
|
||||||
setattr(service, key, value)
|
setattr(service, key, value)
|
||||||
|
print(f"{service=} is set")
|
||||||
|
|
||||||
def load_var(self, who, key):
|
def load_var(self, who, key):
|
||||||
variable = self.sheerka.om.get(self.VARIABLES_ENTRY, who + "|" + key)
|
variable = self.sheerka.om.get(self.VARIABLES_ENTRY, who + "|" + key)
|
||||||
|
|||||||
@@ -777,6 +777,14 @@ def escape_str(x):
|
|||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def new_array(size):
|
||||||
|
res = []
|
||||||
|
for _ in range(size):
|
||||||
|
res.append(0)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
class NextIdManager:
|
class NextIdManager:
|
||||||
"""
|
"""
|
||||||
solely return the next integer
|
solely return the next integer
|
||||||
@@ -788,3 +796,16 @@ class NextIdManager:
|
|||||||
def get_next_id(self):
|
def get_next_id(self):
|
||||||
self.id += 1
|
self.id += 1
|
||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
|
|
||||||
|
def compute_hash(obj):
|
||||||
|
if isinstance(obj, list):
|
||||||
|
return hash(tuple([compute_hash(o) for o in obj]))
|
||||||
|
|
||||||
|
if isinstance(obj, set):
|
||||||
|
return hash(tuple([compute_hash(o) for o in sorted(list(obj))]))
|
||||||
|
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
return hash(repr(obj))
|
||||||
|
|
||||||
|
return hash(obj)
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class Expando:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class PythonEvalError:
|
class PythonEvalError:
|
||||||
error: Exception
|
error: Exception
|
||||||
|
source: str
|
||||||
traceback: str = field(repr=False)
|
traceback: str = field(repr=False)
|
||||||
concepts: dict = field(repr=False)
|
concepts: dict = field(repr=False)
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ class PythonEvalError:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return isinstance(self.error, type(other.error)) and \
|
return isinstance(self.error, type(other.error)) and \
|
||||||
|
self.source == other.source and \
|
||||||
self.traceback == other.traceback and \
|
self.traceback == other.traceback and \
|
||||||
self.concepts == other.concepts
|
self.concepts == other.concepts
|
||||||
|
|
||||||
@@ -151,10 +153,12 @@ class PythonEvaluator(OneReturnValueEvaluator):
|
|||||||
if concepts_entries is None:
|
if concepts_entries is None:
|
||||||
concepts_entries = self.get_concepts_entries_from_globals(my_globals)
|
concepts_entries = self.get_concepts_entries_from_globals(my_globals)
|
||||||
eval_error = PythonEvalError(ex,
|
eval_error = PythonEvalError(ex,
|
||||||
|
node.source,
|
||||||
traceback.format_exc() if get_trace_back else None,
|
traceback.format_exc() if get_trace_back else None,
|
||||||
self.get_concepts_values_from_globals(globals_, concepts_entries))
|
self.get_concepts_values_from_globals(globals_, concepts_entries))
|
||||||
errors.append(eval_error)
|
errors.append(eval_error)
|
||||||
exception_debugger.debug_var("exception", eval_error.error, is_error=True)
|
exception_debugger.debug_var("exception", eval_error.error, is_error=True)
|
||||||
|
exception_debugger.debug_var("source", eval_error.source, is_error=True)
|
||||||
exception_debugger.debug_var("trace", eval_error.traceback, is_error=True)
|
exception_debugger.debug_var("trace", eval_error.traceback, is_error=True)
|
||||||
|
|
||||||
if evaluated == NotInit:
|
if evaluated == NotInit:
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ import inspect
|
|||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from prompt_toolkit.completion import Completer, Completion
|
||||||
|
|
||||||
|
from core.concept import Concept
|
||||||
from core.sheerka.Sheerka import EXIT_COMMANDS
|
from core.sheerka.Sheerka import EXIT_COMMANDS
|
||||||
|
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
|
||||||
from core.sheerka.services.SheerkaFunctionsParametersHistory import SheerkaFunctionsParametersHistory
|
from core.sheerka.services.SheerkaFunctionsParametersHistory import SheerkaFunctionsParametersHistory
|
||||||
from core.tokenizer import Tokenizer, TokenKind, LexerError
|
from core.tokenizer import Tokenizer, TokenKind, LexerError
|
||||||
from prompt_toolkit.completion import Completer, Completion
|
|
||||||
|
|
||||||
NAME = re.compile(r'[a-zA-Z0-9_\.]*[a-zA-Z_]')
|
NAME = re.compile(r'[a-zA-Z0-9_\.]*[a-zA-Z_]')
|
||||||
|
|
||||||
@@ -24,9 +27,119 @@ class FuncFound:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class CompletionDesc:
|
class CompletionDesc:
|
||||||
text: str
|
text: str # what to autocomplete
|
||||||
display: str
|
display: str # what to display in the list of proposals
|
||||||
meta_display: str
|
meta_display: str # extra info when listing proposals
|
||||||
|
|
||||||
|
|
||||||
|
class ConceptCompleterHelper:
|
||||||
|
|
||||||
|
def __init__(self, concept: Concept, pos: int):
|
||||||
|
self.concept_id = concept.id
|
||||||
|
self.display = concept.name
|
||||||
|
self.parts = self.initialize_parts(concept)
|
||||||
|
|
||||||
|
self.start_text_index = pos - 1
|
||||||
|
self.last_part_index = 0 # for debug purpose
|
||||||
|
|
||||||
|
self.need_candidates = False # when multi-parts, we have reached the end of a part
|
||||||
|
self.completion = None
|
||||||
|
self.to_remove = False # True if completed or if a mismatch is found
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"ConceptCompleterHelper(id={self.concept_id}, pos={self.start_text_index}, part={self.last_part_index})"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def initialize_parts(concept):
|
||||||
|
"""
|
||||||
|
initialize the different parts of the concept, when trying to auto complete
|
||||||
|
:param concept:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
parts = []
|
||||||
|
buffer = ""
|
||||||
|
for t in Tokenizer(concept.key, yield_eof=False):
|
||||||
|
if t.type == TokenKind.VAR_DEF:
|
||||||
|
if buffer.strip():
|
||||||
|
parts.append(buffer.lstrip())
|
||||||
|
|
||||||
|
if len(parts) > 0 and parts[-1] != TokenKind.VAR_DEF:
|
||||||
|
parts.append(TokenKind.VAR_DEF)
|
||||||
|
buffer = ""
|
||||||
|
else:
|
||||||
|
buffer += t.to_str(False)
|
||||||
|
|
||||||
|
# do not forget the remaining part
|
||||||
|
if buffer.strip():
|
||||||
|
parts.append(buffer.lstrip())
|
||||||
|
|
||||||
|
return parts
|
||||||
|
|
||||||
|
def match(self, text, pos):
|
||||||
|
if pos < self.start_text_index:
|
||||||
|
self.to_remove = True
|
||||||
|
return False
|
||||||
|
|
||||||
|
current_part_index = 0
|
||||||
|
current_text_index = 0
|
||||||
|
previous_char = None
|
||||||
|
self.completion = self.get_completion(text, current_part_index, current_text_index)
|
||||||
|
|
||||||
|
for c in text[self.start_text_index:pos]:
|
||||||
|
current_text = self.parts[current_part_index]
|
||||||
|
self.need_candidates = False
|
||||||
|
|
||||||
|
if current_text == TokenKind.VAR_DEF:
|
||||||
|
if (previous_char == " " and
|
||||||
|
current_part_index < len(self.parts) - 1 and c == self.parts[current_part_index + 1][0]):
|
||||||
|
# we are starting the next part
|
||||||
|
current_part_index += 1
|
||||||
|
current_text_index = 1
|
||||||
|
|
||||||
|
self.completion = self.get_completion(text, current_part_index, current_text_index)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if c == current_text[current_text_index]:
|
||||||
|
# Validating a part
|
||||||
|
current_text_index += 1
|
||||||
|
self.completion = self.get_completion(text, current_part_index, current_text_index)
|
||||||
|
if current_text_index == len(current_text):
|
||||||
|
# The whole part is validated
|
||||||
|
current_part_index += 1
|
||||||
|
|
||||||
|
if current_part_index == len(self.parts):
|
||||||
|
# no more part to validate
|
||||||
|
self.need_candidates = True # ask for new candidates
|
||||||
|
# self.to_remove = True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
current_text_index = 0
|
||||||
|
self.need_candidates = True
|
||||||
|
else:
|
||||||
|
# a misspell is found. go back to the previous state
|
||||||
|
current_part_index -= 1
|
||||||
|
if current_part_index < 0:
|
||||||
|
self.last_part_index = current_part_index
|
||||||
|
return False
|
||||||
|
self.completion = self.get_completion(text, current_part_index, current_text_index)
|
||||||
|
|
||||||
|
previous_char = c
|
||||||
|
|
||||||
|
self.last_part_index = current_part_index
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_completion(self, text, current_part_index, current_text_index):
|
||||||
|
if self.parts[current_part_index] == TokenKind.VAR_DEF:
|
||||||
|
if current_part_index == len(self.parts) - 1:
|
||||||
|
return text[self.start_text_index:]
|
||||||
|
else:
|
||||||
|
if text[-1] == " ":
|
||||||
|
return text[self.start_text_index:] + self.parts[current_part_index + 1]
|
||||||
|
else:
|
||||||
|
return text[self.start_text_index:] + " " + self.parts[current_part_index + 1]
|
||||||
|
|
||||||
|
# else
|
||||||
|
return text[self.start_text_index:] + self.parts[current_part_index][current_text_index:]
|
||||||
|
|
||||||
|
|
||||||
class SheerkaPromptCompleter(Completer):
|
class SheerkaPromptCompleter(Completer):
|
||||||
@@ -36,28 +149,39 @@ class SheerkaPromptCompleter(Completer):
|
|||||||
self.params_history_service = self.sheerka.services[SheerkaFunctionsParametersHistory.NAME]
|
self.params_history_service = self.sheerka.services[SheerkaFunctionsParametersHistory.NAME]
|
||||||
self.builtins = []
|
self.builtins = []
|
||||||
for name, bound_method in sheerka.sheerka_methods.items():
|
for name, bound_method in sheerka.sheerka_methods.items():
|
||||||
self.builtins.append(self.get_completion_desc(name, bound_method.method, "builtin", ["context"]))
|
self.builtins.append(self.get_completion_desc_for_function(name,
|
||||||
|
bound_method.method,
|
||||||
|
"builtin",
|
||||||
|
["context"]))
|
||||||
|
|
||||||
self.exit_commands = [CompletionDesc(c, c, "command") for c in EXIT_COMMANDS]
|
self.exit_commands = [CompletionDesc(c, c, "command") for c in EXIT_COMMANDS]
|
||||||
self.globals = {k: v.method for k, v in self.sheerka.sheerka_methods.items()}
|
self.globals = {k: v.method for k, v in self.sheerka.sheerka_methods.items()}
|
||||||
|
|
||||||
|
self.concepts_by_first_keyword = sheerka.om.copy(SheerkaConceptManager.RESOLVED_CONCEPTS_BY_FIRST_KEYWORD_ENTRY)
|
||||||
|
self.concepts_candidates = {} # key = (concept_id, pos), value = ConceptCompleterHelper
|
||||||
|
self.last_token_index = None
|
||||||
|
|
||||||
|
self.complete_concepts = True
|
||||||
|
self.complete_commands = True
|
||||||
|
self.complete_builtins = True
|
||||||
|
|
||||||
def get_completions(self, document, complete_event):
|
def get_completions(self, document, complete_event):
|
||||||
|
|
||||||
text = document.text_before_cursor
|
text = document.text_before_cursor
|
||||||
|
pos = document.cursor_position
|
||||||
|
|
||||||
if func_found := self.inside_function(document.text, document.cursor_position):
|
last_token = self.get_last_token(text)
|
||||||
param_number, comma_index = self.get_param_number(text[func_found.paren_index + 1:])
|
if self.last_token_index is None or last_token and last_token.index != self.last_token_index:
|
||||||
values = self.params_history_service.get_function_parameters(func_found.name, param_number)
|
self.last_token_index = last_token.index if last_token else 0
|
||||||
as_custom_desc = [CompletionDesc(v, v, "history") for v in values]
|
# new word or new symbol detected
|
||||||
param_text = text[func_found.paren_index + comma_index + 2:].lstrip()
|
self.update_concepts_candidates(last_token, pos)
|
||||||
yield from self.yield_completion_from_completion_desc(as_custom_desc, param_text)
|
|
||||||
return
|
|
||||||
|
|
||||||
if " " not in text:
|
if self.complete_concepts:
|
||||||
|
yield from self.yield_completion_for_concepts(self.concepts_candidates.values(), text, pos)
|
||||||
|
|
||||||
|
if self.complete_commands:
|
||||||
yield from self.yield_completion_from_completion_desc(self.exit_commands, text)
|
yield from self.yield_completion_from_completion_desc(self.exit_commands, text)
|
||||||
yield from self.yield_completion_from_completion_desc(self.builtins, text)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
if self.complete_builtins:
|
||||||
yield from self.yield_completion_from_completion_desc(self.builtins, text)
|
yield from self.yield_completion_from_completion_desc(self.builtins, text)
|
||||||
|
|
||||||
def get_completions_fom_jedi(self, document):
|
def get_completions_fom_jedi(self, document):
|
||||||
@@ -109,6 +233,39 @@ class SheerkaPromptCompleter(Completer):
|
|||||||
display=c.name_with_symbols,
|
display=c.name_with_symbols,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update_concepts_candidates(self, last_token, pos):
|
||||||
|
possible_concepts_ids = set()
|
||||||
|
candidates_that_need_candidates = [c for c in self.concepts_candidates.values() if c.need_candidates]
|
||||||
|
if len(self.concepts_candidates) == 0 or len(candidates_that_need_candidates) > 0:
|
||||||
|
|
||||||
|
last_word = last_token.str_value.strip() if last_token else ""
|
||||||
|
for k, v in self.concepts_by_first_keyword.items():
|
||||||
|
if k.startswith(last_word):
|
||||||
|
possible_concepts_ids.update(set(v))
|
||||||
|
|
||||||
|
for concept_id in possible_concepts_ids:
|
||||||
|
if (concept_id, pos) not in self.concepts_candidates:
|
||||||
|
concept = self.sheerka.fast_resolve((None, concept_id), return_new=False)
|
||||||
|
if concept:
|
||||||
|
concept_completer_helper = ConceptCompleterHelper(concept, pos)
|
||||||
|
self.concepts_candidates[(concept_id, pos)] = concept_completer_helper
|
||||||
|
|
||||||
|
def update_functions_candidates(self):
|
||||||
|
# if func_found := self.inside_function(document.text, document.cursor_position):
|
||||||
|
# param_number, comma_index = self.get_param_number(text[func_found.paren_index + 1:])
|
||||||
|
# values = self.params_history_service.get_function_parameters(func_found.name, param_number)
|
||||||
|
# as_custom_desc = [CompletionDesc(v, v, "history") for v in values]
|
||||||
|
# param_text = text[func_found.paren_index + comma_index + 2:].lstrip()
|
||||||
|
# yield from self.yield_completion_from_completion_desc(as_custom_desc, param_text)
|
||||||
|
# return
|
||||||
|
|
||||||
|
# if " " not in text:
|
||||||
|
# yield from self.yield_completion_from_completion_desc(self.exit_commands, text)
|
||||||
|
# yield from self.yield_completion_from_completion_desc(self.builtins, text)
|
||||||
|
# yield from self.yield_completion_from_completion_desc(possible_concepts, text)
|
||||||
|
# return
|
||||||
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def yield_completion_from_completion_desc(definitions, text):
|
def yield_completion_from_completion_desc(definitions, text):
|
||||||
for completion_desc in definitions:
|
for completion_desc in definitions:
|
||||||
@@ -123,8 +280,35 @@ class SheerkaPromptCompleter(Completer):
|
|||||||
display=completion_desc.display,
|
display=completion_desc.display,
|
||||||
display_meta=completion_desc.meta_display)
|
display_meta=completion_desc.meta_display)
|
||||||
|
|
||||||
|
def yield_completion_for_concepts(self, concepts_completion_helpers, text, pos):
|
||||||
|
for concept_completion_helper in concepts_completion_helpers:
|
||||||
|
if text is None or text == "":
|
||||||
|
yield Completion(concept_completion_helper.parts[0],
|
||||||
|
0,
|
||||||
|
display=concept_completion_helper.display,
|
||||||
|
display_meta="concept")
|
||||||
|
else:
|
||||||
|
if concept_completion_helper.match(text, pos):
|
||||||
|
yield Completion(concept_completion_helper.completion,
|
||||||
|
concept_completion_helper.start_text_index - pos,
|
||||||
|
display=concept_completion_helper.display,
|
||||||
|
display_meta="concept")
|
||||||
|
|
||||||
|
# clean
|
||||||
|
items = [i for i in self.concepts_candidates.values() if i.to_remove]
|
||||||
|
for i in items:
|
||||||
|
del self.concepts_candidates[(i.concept_id, i.start_text_index + 1)]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_completion_desc(name, function, meta_display, skip_params):
|
def get_completion_desc_for_function(name, function, meta_display, skip_params):
|
||||||
|
"""
|
||||||
|
Manage trailing parenthesis when autocompleting a function call
|
||||||
|
:param name: name of the function
|
||||||
|
:param function: actual function (Python object) to parse the signature
|
||||||
|
:param meta_display: function meta
|
||||||
|
:param skip_params: parameters that must not be displayed (like 'context' for example)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
function_name = name + "("
|
function_name = name + "("
|
||||||
signature = inspect.signature(function)
|
signature = inspect.signature(function)
|
||||||
@@ -166,19 +350,21 @@ class SheerkaPromptCompleter(Completer):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def last_word(text, pos, left_strip=True):
|
def get_last_word(text):
|
||||||
if pos == 0:
|
if text is None or text.strip() == "":
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
start = pos - 1 if text[pos - 1] == " " else pos
|
return list(Tokenizer(text, yield_eof=False))[-1].str_value.strip()
|
||||||
if start < 0:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
for i in range(start)[::-1]:
|
@staticmethod
|
||||||
if text[i] == " ":
|
def get_last_token(text):
|
||||||
return text[i:pos].lstrip() if left_strip else text[i:pos]
|
if text is None or text.strip() == "":
|
||||||
|
return None
|
||||||
|
|
||||||
return text[:pos].lstrip() if left_strip else text[:pos]
|
try:
|
||||||
|
return list(Tokenizer(text, yield_eof=False))[-1]
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_param_number(text):
|
def get_param_number(text):
|
||||||
@@ -234,70 +420,3 @@ class SheerkaPromptCompleter(Completer):
|
|||||||
except Exception:
|
except Exception:
|
||||||
# Workaround for: https://github.com/jonathanslenders/ptpython/issues/91
|
# Workaround for: https://github.com/jonathanslenders/ptpython/issues/91
|
||||||
return None
|
return None
|
||||||
# def find_backwards(
|
|
||||||
# self,
|
|
||||||
# sub: str,
|
|
||||||
# in_current_line: bool = False,
|
|
||||||
# ignore_case: bool = False,
|
|
||||||
# count: int = 1,
|
|
||||||
# ) -> Optional[int]:
|
|
||||||
# """
|
|
||||||
# Find `text` before the cursor, return position relative to the cursor
|
|
||||||
# position. Return `None` if nothing was found.
|
|
||||||
# :param count: Find the n-th occurrence.
|
|
||||||
# """
|
|
||||||
# if in_current_line:
|
|
||||||
# before_cursor = self.current_line_before_cursor[::-1]
|
|
||||||
# else:
|
|
||||||
# before_cursor = self.text_before_cursor[::-1]
|
|
||||||
#
|
|
||||||
# flags = re.IGNORECASE if ignore_case else 0
|
|
||||||
# iterator = re.finditer(re.escape(sub[::-1]), before_cursor, flags)
|
|
||||||
#
|
|
||||||
# try:
|
|
||||||
# for i, match in enumerate(iterator):
|
|
||||||
# if i + 1 == count:
|
|
||||||
# return -match.start(0) - len(sub)
|
|
||||||
# except StopIteration:
|
|
||||||
# pass
|
|
||||||
# return None
|
|
||||||
|
|
||||||
# def find(
|
|
||||||
# self,
|
|
||||||
# sub: str,
|
|
||||||
# in_current_line: bool = False,
|
|
||||||
# include_current_position: bool = False,
|
|
||||||
# ignore_case: bool = False,
|
|
||||||
# count: int = 1,
|
|
||||||
# ) -> Optional[int]:
|
|
||||||
# """
|
|
||||||
# Find `text` after the cursor, return position relative to the cursor
|
|
||||||
# position. Return `None` if nothing was found.
|
|
||||||
# :param count: Find the n-th occurrence.
|
|
||||||
# """
|
|
||||||
# assert isinstance(ignore_case, bool)
|
|
||||||
#
|
|
||||||
# if in_current_line:
|
|
||||||
# text = self.current_line_after_cursor
|
|
||||||
# else:
|
|
||||||
# text = self.text_after_cursor
|
|
||||||
#
|
|
||||||
# if not include_current_position:
|
|
||||||
# if len(text) == 0:
|
|
||||||
# return None # (Otherwise, we always get a match for the empty string.)
|
|
||||||
# else:
|
|
||||||
# text = text[1:]
|
|
||||||
#
|
|
||||||
# flags = re.IGNORECASE if ignore_case else 0
|
|
||||||
# iterator = re.finditer(re.escape(sub), text, flags)
|
|
||||||
#
|
|
||||||
# try:
|
|
||||||
# for i, match in enumerate(iterator):
|
|
||||||
# if i + 1 == count:
|
|
||||||
# if include_current_position:
|
|
||||||
# return match.start(0)
|
|
||||||
# else:
|
|
||||||
# return match.start(0) + 1
|
|
||||||
# except StopIteration:
|
|
||||||
# pass
|
|
||||||
# return None
|
|
||||||
|
|||||||
@@ -437,6 +437,10 @@ class SheerkaDataProvider:
|
|||||||
ext = "_admin_result" if is_admin else "_result"
|
ext = "_admin_result" if is_admin else "_result"
|
||||||
return self.io.get_obj_path(SheerkaDataProvider.EventFolder, digest) + ext
|
return self.io.get_obj_path(SheerkaDataProvider.EventFolder, digest) + ext
|
||||||
|
|
||||||
|
def get_result_extra_info_file_path(self, digest):
|
||||||
|
ext = "_result_extra_info"
|
||||||
|
return self.io.get_obj_path(SheerkaDataProvider.EventFolder, digest) + ext
|
||||||
|
|
||||||
def has_result(self, digest, is_admin=False):
|
def has_result(self, digest, is_admin=False):
|
||||||
"""
|
"""
|
||||||
Check is a result file was created for a specific event
|
Check is a result file was created for a specific event
|
||||||
@@ -471,6 +475,23 @@ class SheerkaDataProvider:
|
|||||||
self.log.debug(f"Saved execution context. message={message}, length={length}, elapsed={elapsed}")
|
self.log.debug(f"Saved execution context. message={message}, length={length}, elapsed={elapsed}")
|
||||||
return digest
|
return digest
|
||||||
|
|
||||||
|
def save_result_extra_info(self, digest, result_extra_info):
|
||||||
|
"""
|
||||||
|
On the top of execution context, also save some extra information like, status of the execution
|
||||||
|
:param digest:
|
||||||
|
:param result_extra_info:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.log.debug(f"Saving execution context extra information. digest={digest}")
|
||||||
|
target_path = self.get_result_extra_info_file_path(digest)
|
||||||
|
if self.io.exists(target_path):
|
||||||
|
return digest
|
||||||
|
|
||||||
|
context = SerializerContext(sheerka=self.sheerka)
|
||||||
|
length = self.io.write_binary(target_path, self.serializer.serialize(result_extra_info, context).read())
|
||||||
|
self.log.debug(f"Saved execution context. message={result_extra_info}, length={length}")
|
||||||
|
return digest
|
||||||
|
|
||||||
def load_result(self, digest, is_admin=False):
|
def load_result(self, digest, is_admin=False):
|
||||||
"""
|
"""
|
||||||
Load and deserialize a result file
|
Load and deserialize a result file
|
||||||
@@ -484,6 +505,20 @@ class SheerkaDataProvider:
|
|||||||
context = SerializerContext(sheerka=self.sheerka)
|
context = SerializerContext(sheerka=self.sheerka)
|
||||||
return self.serializer.deserialize(f, context)
|
return self.serializer.deserialize(f, context)
|
||||||
|
|
||||||
|
def load_result_extra_info(self, digest):
|
||||||
|
"""
|
||||||
|
Load and deserialize a result extra file
|
||||||
|
:param digest:
|
||||||
|
:return:
|
||||||
|
:param digest:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
target_path = self.get_result_extra_info_file_path(digest)
|
||||||
|
|
||||||
|
with self.io.open(target_path, "rb") as f:
|
||||||
|
context = SerializerContext(sheerka=self.sheerka)
|
||||||
|
return self.serializer.deserialize(f, context)
|
||||||
|
|
||||||
def load_ref_if_needed(self, obj, load_origin=True):
|
def load_ref_if_needed(self, obj, load_origin=True):
|
||||||
"""
|
"""
|
||||||
Make sure the real obj is returned
|
Make sure the real obj is returned
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class TestUsingMemoryBasedSheerka(BaseTest):
|
|||||||
sheerka.initialize("mem://",
|
sheerka.initialize("mem://",
|
||||||
save_execution_context=False,
|
save_execution_context=False,
|
||||||
enable_process_return_values=False,
|
enable_process_return_values=False,
|
||||||
enable_process_rules=False)
|
enable_process_rules=False,
|
||||||
|
enable_commands_backup=False)
|
||||||
return sheerka
|
return sheerka
|
||||||
|
|
||||||
def get_sheerka(self, **kwargs) -> Sheerka:
|
def get_sheerka(self, **kwargs) -> Sheerka:
|
||||||
|
|||||||
@@ -840,7 +840,8 @@ class TestSheerkaConceptManager(TestUsingMemoryBasedSheerka):
|
|||||||
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
||||||
assert sheerka.get_property(foo_instance, BuiltinConcepts.ASSOCIATIVITY) == SyaAssociativity.Left
|
assert sheerka.get_property(foo_instance, BuiltinConcepts.ASSOCIATIVITY) == SyaAssociativity.Left
|
||||||
|
|
||||||
res = sheerka.set_property(context, foo_instance, sheerka.new(BuiltinConcepts.ASSOCIATIVITY), SyaAssociativity.Right)
|
res = sheerka.set_property(context, foo_instance, sheerka.new(BuiltinConcepts.ASSOCIATIVITY),
|
||||||
|
SyaAssociativity.Right)
|
||||||
assert res.status
|
assert res.status
|
||||||
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS)
|
||||||
assert sheerka.get_property(foo_instance, BuiltinConcepts.ASSOCIATIVITY) == SyaAssociativity.Right
|
assert sheerka.get_property(foo_instance, BuiltinConcepts.ASSOCIATIVITY) == SyaAssociativity.Right
|
||||||
@@ -1235,6 +1236,173 @@ class TestSheerkaConceptManager(TestUsingMemoryBasedSheerka):
|
|||||||
assert sheerka.chicken_and_eggs.get(two.id) == {one.id, two.id, three.id}
|
assert sheerka.chicken_and_eggs.get(two.id) == {one.id, two.id, three.id}
|
||||||
assert sheerka.chicken_and_eggs.get(three.id) == {one.id, two.id, three.id}
|
assert sheerka.chicken_and_eggs.get(three.id) == {one.id, two.id, three.id}
|
||||||
|
|
||||||
|
def test_i_can_smart_get_attr_when_the_value_is_known(self):
|
||||||
|
sheerka, context = self.init_concepts()
|
||||||
|
foo = Concept("foo")
|
||||||
|
prop = Concept("property")
|
||||||
|
bar = Concept("bar")
|
||||||
|
|
||||||
|
sheerka.set_attr(foo, prop, bar)
|
||||||
|
assert sheerka.smart_get_attr(foo, prop) == bar
|
||||||
|
|
||||||
|
def test_i_can_smart_get_attr_when_simple_isa(self):
|
||||||
|
sheerka, context, adjective, color, red, table = self.init_concepts("adjective",
|
||||||
|
"color",
|
||||||
|
"red",
|
||||||
|
"table",
|
||||||
|
create_new=True)
|
||||||
|
sheerka.set_isa(context, color, adjective)
|
||||||
|
sheerka.set_isa(context, red, color)
|
||||||
|
|
||||||
|
color_instance = sheerka.new(color, body=red)
|
||||||
|
adjective_instance = sheerka.new(adjective, body=color_instance)
|
||||||
|
table_instance = sheerka.new(table)
|
||||||
|
sheerka.set_attr(table_instance, adjective, adjective_instance)
|
||||||
|
|
||||||
|
assert sheerka.smart_get_attr(table_instance, color) == color_instance
|
||||||
|
assert sheerka.objvalue(sheerka.smart_get_attr(table_instance, color)) == red
|
||||||
|
|
||||||
|
def test_i_can_smart_get_when_multiple_levels_of_isa(self):
|
||||||
|
sheerka, context, adjective, color, reddish, red, table = self.init_concepts("adjective",
|
||||||
|
"color",
|
||||||
|
"reddish",
|
||||||
|
"red",
|
||||||
|
"table",
|
||||||
|
create_new=True)
|
||||||
|
sheerka.set_isa(context, color, adjective)
|
||||||
|
sheerka.set_isa(context, reddish, color)
|
||||||
|
sheerka.set_isa(context, red, reddish)
|
||||||
|
|
||||||
|
reddish_instance = sheerka.new(reddish, body=red)
|
||||||
|
color_instance = sheerka.new(color, body=reddish_instance)
|
||||||
|
adjective_instance = sheerka.new(adjective, body=color_instance)
|
||||||
|
table_instance = sheerka.new(table)
|
||||||
|
sheerka.set_attr(table_instance, adjective, adjective_instance)
|
||||||
|
|
||||||
|
assert sheerka.smart_get_attr(table_instance, reddish_instance) == reddish_instance
|
||||||
|
assert sheerka.objvalue(sheerka.smart_get_attr(table_instance, color)) == red
|
||||||
|
|
||||||
|
def test_i_can_smart_get_when_multiple_values(self):
|
||||||
|
sheerka, context, adjective, color, red, blue, table = self.init_concepts("adjective",
|
||||||
|
"color",
|
||||||
|
"red",
|
||||||
|
"blue",
|
||||||
|
"table",
|
||||||
|
create_new=True)
|
||||||
|
sheerka.set_isa(context, color, adjective)
|
||||||
|
sheerka.set_isa(context, red, color)
|
||||||
|
sheerka.set_isa(context, blue, color)
|
||||||
|
table_instance = sheerka.new(table)
|
||||||
|
|
||||||
|
# add red color
|
||||||
|
red_color_instance = sheerka.new(color, body=red)
|
||||||
|
red_adjective_instance = sheerka.new(adjective, body=red_color_instance)
|
||||||
|
sheerka.set_attr(table_instance, adjective, red_adjective_instance)
|
||||||
|
|
||||||
|
# add blue color
|
||||||
|
blue_color_instance = sheerka.new(color, body=blue)
|
||||||
|
blue_adjective_instance = sheerka.new(adjective, body=blue_color_instance)
|
||||||
|
sheerka.set_attr(table_instance, adjective, blue_adjective_instance)
|
||||||
|
|
||||||
|
res = sheerka.smart_get_attr(table_instance, color)
|
||||||
|
assert res == [red_color_instance, blue_color_instance]
|
||||||
|
|
||||||
|
def test_i_can_smart_get_when_ancestor_is_asked(self):
|
||||||
|
sheerka, context, adjective, color, red, table = self.init_concepts("adjective",
|
||||||
|
"color",
|
||||||
|
"red",
|
||||||
|
"table",
|
||||||
|
create_new=True)
|
||||||
|
sheerka.set_isa(context, color, adjective)
|
||||||
|
sheerka.set_isa(context, red, color)
|
||||||
|
|
||||||
|
color_instance = sheerka.new(color, body=red)
|
||||||
|
table_instance = sheerka.new(table)
|
||||||
|
sheerka.set_attr(table_instance, color, color_instance)
|
||||||
|
|
||||||
|
assert sheerka.smart_get_attr(table_instance, adjective) == color_instance
|
||||||
|
|
||||||
|
def test_i_can_smart_get_when_ancestor_is_asked_and_multiple_values(self):
|
||||||
|
sheerka, context, adjective, color, red, size, large, table = self.init_concepts("adjective",
|
||||||
|
"color",
|
||||||
|
"red",
|
||||||
|
"size",
|
||||||
|
"large",
|
||||||
|
"table",
|
||||||
|
create_new=True)
|
||||||
|
sheerka.set_isa(context, color, adjective)
|
||||||
|
sheerka.set_isa(context, red, color)
|
||||||
|
sheerka.set_isa(context, size, adjective)
|
||||||
|
sheerka.set_isa(context, large, size)
|
||||||
|
|
||||||
|
table_instance = sheerka.new(table)
|
||||||
|
color_instance = sheerka.new(color, body=red)
|
||||||
|
sheerka.set_attr(table_instance, color, color_instance)
|
||||||
|
size_instance = sheerka.new(size, body=large)
|
||||||
|
sheerka.set_attr(table_instance, size, size_instance)
|
||||||
|
|
||||||
|
assert sheerka.smart_get_attr(table_instance, adjective) == [color_instance, size_instance]
|
||||||
|
|
||||||
|
def test_i_can_smart_get_when_direct_value_takes_precedence_over_child_value(self):
|
||||||
|
sheerka, context, adjective, color, red, blue, table = self.init_concepts("adjective",
|
||||||
|
"color",
|
||||||
|
"red",
|
||||||
|
"blue",
|
||||||
|
"table",
|
||||||
|
create_new=True)
|
||||||
|
sheerka.set_isa(context, color, adjective)
|
||||||
|
sheerka.set_isa(context, red, color)
|
||||||
|
|
||||||
|
color_instance = sheerka.new(color, body=red)
|
||||||
|
adjective_instance = sheerka.new(adjective, body=color_instance)
|
||||||
|
table_instance = sheerka.new(table)
|
||||||
|
sheerka.set_attr(table_instance, adjective, adjective_instance)
|
||||||
|
sheerka.set_attr(table_instance, color, blue) # set direct color value
|
||||||
|
|
||||||
|
assert sheerka.smart_get_attr(table_instance, color) == blue
|
||||||
|
|
||||||
|
def test_i_can_smart_get_when_direct_value_takes_precedence_over_ancestor_value(self):
|
||||||
|
sheerka, context, adjective, color, red, blue, table = self.init_concepts("adjective",
|
||||||
|
"color",
|
||||||
|
"red",
|
||||||
|
"blue",
|
||||||
|
"table",
|
||||||
|
create_new=True)
|
||||||
|
sheerka.set_isa(context, color, adjective)
|
||||||
|
sheerka.set_isa(context, red, color)
|
||||||
|
|
||||||
|
color_instance = sheerka.new(color, body=red)
|
||||||
|
table_instance = sheerka.new(table)
|
||||||
|
sheerka.set_attr(table_instance, color, color_instance)
|
||||||
|
sheerka.set_attr(table_instance, adjective, blue) # set direct color value
|
||||||
|
|
||||||
|
assert sheerka.smart_get_attr(table_instance, adjective) == blue
|
||||||
|
|
||||||
|
def test_i_cannot_smart_get_attr_if_value_is_unknown_and_attribute_not_a_concept(self):
|
||||||
|
sheerka, context = self.init_concepts()
|
||||||
|
foo = Concept("foo")
|
||||||
|
|
||||||
|
res = sheerka.smart_get_attr(foo, "attribute")
|
||||||
|
assert sheerka.isinstance(res, BuiltinConcepts.NOT_FOUND)
|
||||||
|
|
||||||
|
def test_i_cannot_smart_get_attr_if_value_is_unknown_and_no_isa(self):
|
||||||
|
sheerka, context, adjective, color, red, table = self.init_concepts("adjective",
|
||||||
|
"color",
|
||||||
|
"red",
|
||||||
|
"table",
|
||||||
|
create_new=True)
|
||||||
|
|
||||||
|
# sheerka.set_isa(context, color, adjective) # color is not defined as an adjective
|
||||||
|
sheerka.set_isa(context, red, color)
|
||||||
|
|
||||||
|
color_instance = sheerka.new(color, body=red)
|
||||||
|
adjective_instance = sheerka.new(adjective, body=color_instance)
|
||||||
|
table_instance = sheerka.new(table)
|
||||||
|
sheerka.set_attr(table_instance, adjective, adjective_instance)
|
||||||
|
|
||||||
|
res = sheerka.smart_get_attr(table_instance, color)
|
||||||
|
assert sheerka.isinstance(res, BuiltinConcepts.NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
class TestSheerkaConceptManagerUsingFileBasedSheerka(TestUsingFileBasedSheerka):
|
class TestSheerkaConceptManagerUsingFileBasedSheerka(TestUsingFileBasedSheerka):
|
||||||
def test_i_can_add_several_concepts(self):
|
def test_i_can_add_several_concepts(self):
|
||||||
|
|||||||
@@ -15,3 +15,15 @@ class TestSheerkaHasAManager(TestUsingMemoryBasedSheerka):
|
|||||||
|
|
||||||
# check that the definition of the concept has been updated
|
# check that the definition of the concept has been updated
|
||||||
assert sheerka.hasa(sheerka.new("king"), kingdom)
|
assert sheerka.hasa(sheerka.new("king"), kingdom)
|
||||||
|
|
||||||
|
def test_i_cannot_set_the_same_attribute_twice(self):
|
||||||
|
sheerka, context, king, kingdom = self.init_concepts("king", "kingdom")
|
||||||
|
|
||||||
|
sheerka.set_hasa(context, sheerka.new("king"), kingdom)
|
||||||
|
res = sheerka.set_hasa(context, sheerka.new("king"), kingdom)
|
||||||
|
|
||||||
|
assert not res.status
|
||||||
|
assert sheerka.isinstance(res.body, BuiltinConcepts.PROPERTY_ALREADY_DEFINED)
|
||||||
|
assert res.body.property_name == BuiltinConcepts.HASA
|
||||||
|
assert res.body.property_value == kingdom
|
||||||
|
assert res.body.concept == sheerka.new("king")
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import types
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from core.builtin_concepts import BuiltinConcepts
|
from core.builtin_concepts import BuiltinConcepts
|
||||||
|
from core.concept import Concept
|
||||||
|
from core.global_symbols import EVENT_CONCEPT_CREATED, EVENT_CONCEPT_MODIFIED, EVENT_CONCEPT_PRECEDENCE_MODIFIED, \
|
||||||
|
EVENT_RULE_PRECEDENCE_MODIFIED
|
||||||
|
from core.rule import Rule, ACTION_TYPE_EXEC
|
||||||
from core.sheerka.ExecutionContext import ExecutionContext
|
from core.sheerka.ExecutionContext import ExecutionContext
|
||||||
from core.sheerka.SheerkaOntologyManager import SheerkaOntologyManager
|
from core.sheerka.SheerkaOntologyManager import SheerkaOntologyManager
|
||||||
from core.sheerka.services.SheerkaResultManager import SheerkaResultManager
|
from core.sheerka.services.SheerkaResultManager import SheerkaResultManager
|
||||||
@@ -400,6 +404,51 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
|
|||||||
assert service.last_error_event_id is None
|
assert service.last_error_event_id is None
|
||||||
assert sheerka.get_last_error() == self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"query": "get_last_error"})
|
assert sheerka.get_last_error() == self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"query": "get_last_error"})
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("event_id, event_args", [
|
||||||
|
(EVENT_CONCEPT_CREATED, Concept("foo")),
|
||||||
|
(EVENT_CONCEPT_MODIFIED, {"old": Concept("foo"), "new": Concept("bar")}),
|
||||||
|
(EVENT_CONCEPT_PRECEDENCE_MODIFIED, None),
|
||||||
|
(EVENT_RULE_PRECEDENCE_MODIFIED, None),
|
||||||
|
])
|
||||||
|
def test_i_can_detect_when_the_global_state_has_change(self, event_id, event_args):
|
||||||
|
sheerka, context = self.init_test().unpack()
|
||||||
|
assert not context.is_state_modified()
|
||||||
|
|
||||||
|
if event_args:
|
||||||
|
sheerka.publish(context, event_id, event_args)
|
||||||
|
else:
|
||||||
|
sheerka.publish(context, event_id)
|
||||||
|
assert context.is_state_modified()
|
||||||
|
|
||||||
|
def test_i_can_detect_when_the_global_state_has_change_when_a_concept_is_deleted(self):
|
||||||
|
sheerka, context, foo = self.init_concepts("foo", create_new=True)
|
||||||
|
del context.values["is_state_modified"]
|
||||||
|
assert not context.is_state_modified()
|
||||||
|
|
||||||
|
with context.push("Testing state", None) as sub_context:
|
||||||
|
sheerka.remove_concept(sub_context, foo)
|
||||||
|
|
||||||
|
assert context.is_state_modified()
|
||||||
|
|
||||||
|
def test_i_can_detect_when_the_global_state_has_change_when_a_rule_is_created(self):
|
||||||
|
sheerka, context = self.init_test().unpack()
|
||||||
|
|
||||||
|
rule = Rule(ACTION_TYPE_EXEC, "testing state has change", "True", "'hello world'")
|
||||||
|
with context.push("Testing state", None) as sub_context:
|
||||||
|
sheerka.create_new_rule(sub_context, rule)
|
||||||
|
|
||||||
|
assert context.is_state_modified()
|
||||||
|
|
||||||
|
def test_i_can_detect_when_the_global_state_has_change_when_a_rule_is_deleted(self):
|
||||||
|
sheerka, context, rule = self.init_exec_rules(("testing state has change", "True", "'hello world'"))
|
||||||
|
del context.values["is_state_modified"]
|
||||||
|
assert not context.is_state_modified()
|
||||||
|
|
||||||
|
with context.push("Testing state", None) as sub_context:
|
||||||
|
sheerka.remove_rule(sub_context, rule)
|
||||||
|
|
||||||
|
assert context.is_state_modified()
|
||||||
|
|
||||||
|
|
||||||
class TestSheerkaResultManagerFileBased(TestUsingFileBasedSheerka):
|
class TestSheerkaResultManagerFileBased(TestUsingFileBasedSheerka):
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -430,6 +430,19 @@ def test_i_can_deep_copy_a_custom_type():
|
|||||||
assert core.utils.sheerka_deepcopy(Removed) is Removed
|
assert core.utils.sheerka_deepcopy(Removed) is Removed
|
||||||
|
|
||||||
|
|
||||||
|
def test_i_can_deep_copy_concepts_than_look_the_same():
|
||||||
|
foo = Concept("foo")
|
||||||
|
foo2 = Concept().update_from(foo)
|
||||||
|
foo2.set_value("prop_name", "prop_value")
|
||||||
|
assert foo != foo2
|
||||||
|
|
||||||
|
copied_foo = core.utils.sheerka_deepcopy(foo)
|
||||||
|
copied_foo2 = core.utils.sheerka_deepcopy(foo2)
|
||||||
|
|
||||||
|
assert copied_foo != copied_foo2
|
||||||
|
assert len({copied_foo, copied_foo2}) == 2
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("expression1, expression2, expected", [
|
@pytest.mark.parametrize("expression1, expression2, expected", [
|
||||||
("foo bar baz", "foo bar baz", True),
|
("foo bar baz", "foo bar baz", True),
|
||||||
("foo()", " foo ( ) ", True),
|
("foo()", " foo ( ) ", True),
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from core.sheerka.services.SheerkaFunctionsParametersHistory import SheerkaFunctionsParametersHistory
|
|
||||||
from prompt_toolkit.completion import CompleteEvent
|
from prompt_toolkit.completion import CompleteEvent
|
||||||
from prompt_toolkit.document import Document
|
from prompt_toolkit.document import Document
|
||||||
from repl.SheerkaPromptCompleter import SheerkaPromptCompleter, FuncFound
|
|
||||||
|
|
||||||
|
from core.concept import Concept
|
||||||
|
from core.tokenizer import TokenKind
|
||||||
|
from repl.SheerkaPromptCompleter import SheerkaPromptCompleter, FuncFound, ConceptCompleterHelper
|
||||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||||
|
|
||||||
|
|
||||||
@@ -38,41 +39,180 @@ class TestSheerkaPromptCompleter(TestUsingMemoryBasedSheerka):
|
|||||||
assert as_dict["quit"].display_text == "quit"
|
assert as_dict["quit"].display_text == "quit"
|
||||||
assert as_dict["quit"].display_meta_text == "command"
|
assert as_dict["quit"].display_meta_text == "command"
|
||||||
|
|
||||||
@pytest.mark.parametrize("text, expected", [
|
@pytest.mark.parametrize("concept_name", [
|
||||||
("func(", ["10", "20", "30"]),
|
"foo",
|
||||||
("func(1", ["10"]),
|
"a long name",
|
||||||
("func( 1", ["10"]),
|
|
||||||
("func( 10, ", ["'hello'"]),
|
|
||||||
("func( 10, v", []),
|
|
||||||
("func( 10, 'hel", ["'hello'"]),
|
|
||||||
('func( 10, "hel', []),
|
|
||||||
("func('hell,,', func2(2,4), 'w", ["'world'"]),
|
|
||||||
])
|
])
|
||||||
def test_i_can_complete_function_parameters(self, text, expected):
|
def test_i_can_complete_concept(self, concept_name):
|
||||||
sheerka = self.get_sheerka()
|
sheerka, context, foo = self.init_concepts(Concept(concept_name), create_new=True)
|
||||||
context = self.get_context(sheerka)
|
completer = SheerkaPromptCompleter(sheerka)
|
||||||
params_history_service = sheerka.services[SheerkaFunctionsParametersHistory.NAME]
|
completer.complete_commands = False
|
||||||
params_history_service.record_function_parameter(context, "func", 0, "10")
|
completer.complete_builtins = False
|
||||||
params_history_service.record_function_parameter(context, "func", 0, "20")
|
|
||||||
params_history_service.record_function_parameter(context, "func", 0, "30")
|
|
||||||
params_history_service.record_function_parameter(context, "func", 1, "'hello'")
|
|
||||||
params_history_service.record_function_parameter(context, "func", 2, "'world'")
|
|
||||||
|
|
||||||
document = Document(text)
|
for i in range(1, len(concept_name)):
|
||||||
completions = SheerkaPromptCompleter(sheerka).get_completions(document, CompleteEvent())
|
before_cursor = concept_name[:i]
|
||||||
as_list = [c.display_text for c in completions]
|
completions = list(completer.get_completions(Document(before_cursor), CompleteEvent()))
|
||||||
assert as_list == expected
|
as_dict = {c.display_text: c for c in completions}
|
||||||
|
assert concept_name in as_dict, f"{i=}, {before_cursor=}"
|
||||||
|
|
||||||
@pytest.mark.parametrize("text, pos, expected", [
|
def test_i_can_complete_sya_concept_with_variable(self):
|
||||||
("", 0, ""),
|
sheerka, context, foo, something, bar = self.init_concepts(
|
||||||
("foo", 3, "foo"),
|
Concept("if true x then do y else do z end").def_var("x").def_var("y").def_var("z"),
|
||||||
("foo ", 4, "foo "),
|
Concept("something"),
|
||||||
("foo", 2, "fo"),
|
Concept("bar"),
|
||||||
("foo bar", 7, "bar"),
|
create_new=True)
|
||||||
("foo bar", 4, "foo "),
|
completer = SheerkaPromptCompleter(sheerka)
|
||||||
])
|
completer.complete_commands = False
|
||||||
def test_last_word(self, text, pos, expected):
|
completer.complete_builtins = False
|
||||||
assert SheerkaPromptCompleter.last_word(text, pos) == expected
|
|
||||||
|
text = "if true some cond then do some thing else do something else end"
|
||||||
|
# i 0123456789012345678901234567890123456789012345678901234567890123
|
||||||
|
# 0 1 2 3 4 5 6
|
||||||
|
for i in range(1, len(text) + 1):
|
||||||
|
before_cursor = text[:i]
|
||||||
|
d, c = Document(before_cursor), CompleteEvent()
|
||||||
|
completions = list(completer.get_completions(d, c))
|
||||||
|
display_as_dict = {(c.display_text, c.start_position): c for c in completions}
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
assert len(completer.concepts_candidates) == 3
|
||||||
|
assert (foo.name, 0) in display_as_dict
|
||||||
|
assert display_as_dict[(foo.name, 0)].text == "if true "
|
||||||
|
assert (something.name, 0) in display_as_dict
|
||||||
|
assert (bar.name, 0) in display_as_dict
|
||||||
|
|
||||||
|
if i in range(1, 8): # 'if true'
|
||||||
|
assert len(completer.concepts_candidates) == 1
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 0
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == "if true "
|
||||||
|
assert len(display_as_dict) == 1
|
||||||
|
|
||||||
|
if i == 8: # 'if true '
|
||||||
|
assert len(completer.concepts_candidates) == 1
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 1
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == "if true "
|
||||||
|
assert len(display_as_dict) == 1
|
||||||
|
|
||||||
|
elif i in range(9, 13): # 'some'
|
||||||
|
assert len(completer.concepts_candidates) == 2
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 1
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == before_cursor.rstrip() + " then do "
|
||||||
|
|
||||||
|
assert (something.name, 8 - i) in display_as_dict
|
||||||
|
assert display_as_dict[(something.name, 8 - i)].text == "something"
|
||||||
|
|
||||||
|
elif i in range(13, 18): # ' cond'
|
||||||
|
assert len(completer.concepts_candidates) == 2, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 1
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == before_cursor.rstrip() + " then do "
|
||||||
|
|
||||||
|
elif i == 18: # 'cond '
|
||||||
|
assert len(completer.concepts_candidates) == 2, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 1
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == before_cursor.rstrip() + " then do "
|
||||||
|
|
||||||
|
elif i in range(19, 26): # 'then do'
|
||||||
|
assert len(completer.concepts_candidates) == 2, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 2
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == "if true some cond then do "
|
||||||
|
|
||||||
|
elif i == 26: # 'then do '
|
||||||
|
assert len(completer.concepts_candidates) == 2, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 3
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == "if true some cond then do "
|
||||||
|
|
||||||
|
elif i in range(27, 31): # 'some'
|
||||||
|
assert len(completer.concepts_candidates) == 3, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 3
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == before_cursor.rstrip() + " else do "
|
||||||
|
|
||||||
|
assert (something.name, 26 - i) in display_as_dict
|
||||||
|
assert display_as_dict[(something.name, 26 - i)].text == "something"
|
||||||
|
|
||||||
|
elif i in range(31, 38): # ' thing '
|
||||||
|
assert len(completer.concepts_candidates) == 3, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 3
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == before_cursor.rstrip() + " else do "
|
||||||
|
|
||||||
|
elif i in range(38, 45): # 'else do'
|
||||||
|
assert len(completer.concepts_candidates) == 3, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 4
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == "if true some cond then do some thing else do "
|
||||||
|
|
||||||
|
elif i == 45: # 'else do '
|
||||||
|
assert len(completer.concepts_candidates) == 3, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 5
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == "if true some cond then do some thing else do "
|
||||||
|
|
||||||
|
elif i in range(46, 54): # 'something'
|
||||||
|
assert len(completer.concepts_candidates) == 4, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 5
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == before_cursor.rstrip() + " end"
|
||||||
|
|
||||||
|
assert (something.name, 45 - i) in display_as_dict
|
||||||
|
assert display_as_dict[(something.name, 45 - i)].text == "something"
|
||||||
|
|
||||||
|
elif i == 54: # 'g' from 'something'
|
||||||
|
assert len(completer.concepts_candidates) == 4, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 5
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == before_cursor.rstrip() + " end"
|
||||||
|
|
||||||
|
elif i == 55: # ' ' before 'else '
|
||||||
|
assert len(completer.concepts_candidates) == 7, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 5
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == before_cursor.rstrip() + " end"
|
||||||
|
|
||||||
|
elif i == 56: # 'e' from 'else '
|
||||||
|
assert len(completer.concepts_candidates) == 7, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 6
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == "if true some cond then do some thing else do something end"
|
||||||
|
|
||||||
|
elif i in range(57, 60): # 'lse '
|
||||||
|
assert len(completer.concepts_candidates) == 7, f"{i=}, {before_cursor=}"
|
||||||
|
assert (foo.name, -i) in display_as_dict
|
||||||
|
assert completer.concepts_candidates[(foo.id, 1)].last_part_index == 5
|
||||||
|
assert display_as_dict[(foo.name, -i)].text == before_cursor.rstrip() + " end"
|
||||||
|
|
||||||
|
|
||||||
|
# @pytest.mark.parametrize("text, expected", [
|
||||||
|
# ("func(", ["10", "20", "30"]),
|
||||||
|
# ("func(1", ["10"]),
|
||||||
|
# ("func( 1", ["10"]),
|
||||||
|
# ("func( 10, ", ["'hello'"]),
|
||||||
|
# ("func( 10, v", []),
|
||||||
|
# ("func( 10, 'hel", ["'hello'"]),
|
||||||
|
# ('func( 10, "hel', []),
|
||||||
|
# ("func('hell,,', func2(2,4), 'w", ["'world'"]),
|
||||||
|
# ])
|
||||||
|
# def test_i_can_complete_function_parameters(self, text, expected):
|
||||||
|
# sheerka = self.get_sheerka()
|
||||||
|
# context = self.get_context(sheerka)
|
||||||
|
# params_history_service = sheerka.services[SheerkaFunctionsParametersHistory.NAME]
|
||||||
|
# params_history_service.record_function_parameter(context, "func", 0, "10")
|
||||||
|
# params_history_service.record_function_parameter(context, "func", 0, "20")
|
||||||
|
# params_history_service.record_function_parameter(context, "func", 0, "30")
|
||||||
|
# params_history_service.record_function_parameter(context, "func", 1, "'hello'")
|
||||||
|
# params_history_service.record_function_parameter(context, "func", 2, "'world'")
|
||||||
|
#
|
||||||
|
# document = Document(text)
|
||||||
|
# completions = SheerkaPromptCompleter(sheerka).get_completions(document, CompleteEvent())
|
||||||
|
# as_list = [c.display_text for c in completions]
|
||||||
|
# assert as_list == expected
|
||||||
|
|
||||||
@pytest.mark.parametrize("text, pos, expected", [
|
@pytest.mark.parametrize("text, pos, expected", [
|
||||||
("", 0, None),
|
("", 0, None),
|
||||||
@@ -104,6 +244,27 @@ class TestSheerkaPromptCompleter(TestUsingMemoryBasedSheerka):
|
|||||||
def test_get_param_number(self, text, expected_param_number, expected_comma_index):
|
def test_get_param_number(self, text, expected_param_number, expected_comma_index):
|
||||||
assert SheerkaPromptCompleter.get_param_number(text) == (expected_param_number, expected_comma_index)
|
assert SheerkaPromptCompleter.get_param_number(text) == (expected_param_number, expected_comma_index)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("concept, expected_parts", [
|
||||||
|
(Concept("if true x then do y else do z end").def_var("x").def_var("y").def_var("z"), [
|
||||||
|
"if true ",
|
||||||
|
TokenKind.VAR_DEF,
|
||||||
|
"then do ",
|
||||||
|
TokenKind.VAR_DEF,
|
||||||
|
"else do ",
|
||||||
|
TokenKind.VAR_DEF,
|
||||||
|
"end"
|
||||||
|
]),
|
||||||
|
(Concept("x something y").def_var("x").def_var("y"), ["something ", TokenKind.VAR_DEF]),
|
||||||
|
(Concept("foo x y").def_var("x").def_var("y"), ["foo ", TokenKind.VAR_DEF]),
|
||||||
|
(Concept("foo x y bar").def_var("x").def_var("y"), ["foo ", TokenKind.VAR_DEF, "bar"]),
|
||||||
|
(Concept("x y foo").def_var("x").def_var("y"), ["foo"]),
|
||||||
|
])
|
||||||
|
def test_i_can_initialize_concept_parts(self, concept, expected_parts):
|
||||||
|
concept.init_key()
|
||||||
|
parts = ConceptCompleterHelper.initialize_parts(concept)
|
||||||
|
|
||||||
|
assert parts == expected_parts
|
||||||
|
|
||||||
# def test_jedi_infer(self):
|
# def test_jedi_infer(self):
|
||||||
# sheerka = self.get_sheerka()
|
# sheerka = self.get_sheerka()
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#compdef sheerka.rebuild.sh
|
||||||
|
|
||||||
|
_alternative \
|
||||||
|
'helps:helps:(-h)' \
|
||||||
|
'backups:available backups:($(find "$SHEERKA_BACKUP_FOLDER" -name "*.sb" -type f -printf "%f\n" | sed "s/\.[^.]*$//"))'
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#compdef sheerka.reset.sh
|
||||||
|
|
||||||
|
_alternative \
|
||||||
|
'helps:helps:(-h)' \
|
||||||
|
"backups:available backups:($(find $HOME/.sheerka_* -maxdepth 0 -type d -printf '%f\n' | awk -F_ '{ print $2}'))"
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
To allow auto completion
|
||||||
|
|
||||||
|
* make sure the following lines are in the _.zshrc_ file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fpath=(~/my-completions $fpath)
|
||||||
|
autoload -Uz compinit
|
||||||
|
compinit
|
||||||
|
```
|
||||||
|
|
||||||
|
* create the folder _~/my-completions_
|
||||||
|
* copy the files __sheerka.rebuild_ and __sheerka.reset_ into the folder
|
||||||
|
* restart the shell
|
||||||
|
|
||||||
|
> :warning: **Environment variable SHEERKA_BACKUP_FOLDER must be set**
|
||||||
+31
-13
@@ -1,42 +1,60 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
BASEDIR=$(dirname "$0")
|
BASEDIR=$(dirname "$0")
|
||||||
|
|
||||||
list_available() {
|
list_available() {
|
||||||
available=$(ls "$BASEDIR"/../_concepts_*.txt | awk -F_ '{ print " "$3}' ) 2> /dev/null
|
available=$(find "$SHEERKA_BACKUP_FOLDER"/*.sb -type f -printf "%f\n" | sed 's/\.[^.]*$//') 2> /dev/null
|
||||||
|
|
||||||
if [ "$available" = "" ]; then
|
if [ "$available" = "" ]; then
|
||||||
echo "Error. No available environment !" >&2
|
echo "Error. No available backup !" >&2
|
||||||
else
|
else
|
||||||
echo "Available environments are:"
|
echo "Available backups are:"
|
||||||
echo "$available"
|
for backup in ${available}; do
|
||||||
|
echo " ${backup}";
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ "$#" -eq 0 ]; then
|
usage() {
|
||||||
echo "Usage: $0 <environment>"
|
echo "Usage: $0 <backup>"
|
||||||
|
echo "Creates a fresh install of Sheerka, using the provided backup file."
|
||||||
list_available
|
list_available
|
||||||
exit 0
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ] || [ "$1" = "-h" ]; then
|
||||||
|
usage
|
||||||
fi
|
fi
|
||||||
|
|
||||||
env_file="$BASEDIR"/../_concepts_"$1".txt
|
|
||||||
env_folder="$HOME/.sheerka_$1"
|
|
||||||
|
|
||||||
if ! [ -e "$env_file" ]; then
|
if [ -z "${SHEERKA_BACKUP_FOLDER+x}" ]; then
|
||||||
echo "$env_file not found" >&2
|
echo "SHEERKA_BACKUP_FOLDER is not set !"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
backup_file="$SHEERKA_BACKUP_FOLDER"/"$1".sb
|
||||||
|
sheerka_root_folder="$HOME/.sheerka_$1"
|
||||||
|
|
||||||
|
if ! [ -e "$backup_file" ]; then
|
||||||
|
echo "$backup_file not found" >&2
|
||||||
list_available
|
list_available
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Rebuilding $1..."
|
echo "Rebuilding $1..."
|
||||||
|
|
||||||
|
# backup current sheerka
|
||||||
if [ -e ~/.sheerka ]; then
|
if [ -e ~/.sheerka ]; then
|
||||||
rm -rf ~/.sheerka.bak
|
rm -rf ~/.sheerka.bak
|
||||||
mv ~/.sheerka ~/.sheerka.bak
|
mv ~/.sheerka ~/.sheerka.bak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# re-install sheerka, using the requested backup file
|
||||||
python "$BASEDIR"/../main.py "sheerka.restore('$1')"
|
python "$BASEDIR"/../main.py "sheerka.restore('$1')"
|
||||||
rm -rf "$env_folder"
|
|
||||||
cp -R ~/.sheerka "$env_folder"
|
# create a copy of the freshly installed sheerka, for future reset
|
||||||
|
rm -rf "$sheerka_root_folder"
|
||||||
|
cp -R ~/.sheerka "$sheerka_root_folder"
|
||||||
|
|||||||
+30
-4
@@ -1,5 +1,33 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
|
list_available() {
|
||||||
|
available=$(find $HOME/.sheerka_* -maxdepth 0 -type d | awk -F_ '{ print " "$2}') 2> /dev/null
|
||||||
|
|
||||||
|
if [ "$available" = "" ]; then
|
||||||
|
echo "Error. No available environment !" >&2
|
||||||
|
else
|
||||||
|
echo "Available environment are:"
|
||||||
|
for backup in ${available}; do
|
||||||
|
echo " ${backup}";
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 [environment]"
|
||||||
|
echo "Resetting Sheerka environment from a previously build."
|
||||||
|
echo "If no environment is set, create a fresh install of Sheerka from scratch."
|
||||||
|
list_available
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$1" = "-h" ]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
# No environment provided.
|
||||||
|
# Create a new environment from scratch (by simply removing .sheerka folder)
|
||||||
if [ "$#" -eq 0 ]; then
|
if [ "$#" -eq 0 ]; then
|
||||||
echo "Resetting Sheerka environment."
|
echo "Resetting Sheerka environment."
|
||||||
rm -rf ~/.sheerka
|
rm -rf ~/.sheerka
|
||||||
@@ -8,9 +36,7 @@ fi
|
|||||||
|
|
||||||
if ! [ -e "$HOME/.sheerka_$1" ]; then
|
if ! [ -e "$HOME/.sheerka_$1" ]; then
|
||||||
echo "$HOME/.sheerka_$1 not found" >&2
|
echo "$HOME/.sheerka_$1 not found" >&2
|
||||||
available=$(ls -d $HOME/.sheerka_* | awk -F_ '{ print " "$2}')
|
list_available
|
||||||
echo "Available environments are:"
|
|
||||||
echo "$available"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user