Package: py-sc-client
py-sc-clientThe python implementation of the client for communication withthe OSTIS Technology web-oriented platform.
This library is compatible with0.7.0version ofsc-machine.Low-level functionality implemented inpy-sc-client:ScAddrScTypeScStructureScTemplate.High-level functionality implemented inpy-sc-kpm:ScKeynodesScAgentScModuleScServerWarning: there are some of these classes inpy-sc-client(deprecated)Installation py-sc-clientpy-sc-client is available onPyPI:$pipinstallpy-sc-clientpy-sc-client officially supports Python 3.8+.Connection to the sc-serverFirst you need connect to the sc-server.
It's implemented using web-socket in another thread.
Do not forget to disconnect after all operations.sc_client.client.connect(url: str)Connect to the sc-server byurl.fromsc_client.clientimportconnecturl="ws://localhost:8090/ws_json"connect(url)sc_client.client.disconnect()Close the connection with the sc-server.fromsc_client.clientimportdisconnectdisconnect()sc_client.client.is_connected()Returns boolean state of the connection with the sc-server.fromsc_client.clientimportis_connectedifis_connected():...sc_client.client.set_error_handler(callback)Sets a handler callback to manage client and server errors. Callback must take one argument - an exception object.fromsc_client.clientimportset_error_handlerdefon_error(e):ifisinstance(e,AttributeError):print(e)set_error_handler(on_error)...sc_client.client.set_reconnect_handler(**reconnect_kwargs)Sets handler callback to reconnect on sc-server connection failure. Method takes the following arguments:_reconnect_handler_- handler callback function. Default value:_session.default_reconnect_handler_._post_reconnect_callback_- handler callback invoked after_reconnect_handler_has finished successfully._reconnect_retries_- amount of call tries of_reconnect_handler_. Default value:5._reconnect_retry_delay_- period between call tries of_reconnect_handler_(in seconds). Default value:2.If the sc-server did not respond to one of the resent messages, after a requested_reconnect_retry_delay_the_reconnect_handler_is called, and the same message is sent again. This procedure is repeated for_reconnect_retries_times, until the message is sent and a response is received.fromsc_client.clientimportset_reconnect_handlerurl="ws://localhost:8090/ws_json"defon_reconnect():connect(url)set_reconnect_handler(reconnect_handler=connect,post_reconnect_handler=None,reconnect_retries=5,reconnect_retry_delay=1.0#seconds)...Base classesScAddrMinimum element of sc is ScAddr.
It contains address of some element in sc-memory.
Knowing it, you can find related elements, connect edges, check the type, and so on:sc_client.models.ScAddrClass with int address and usable methods.
If value is zero (0), ScAddr is invalid (doesn't exist or there is an error).fromsc_client.modelsimportScAddraddr=ScAddr(0)# You can initialize ScAddr, but it's not recommended.# Usually you will receive valid ones from special functions.is_valid=addr.is_valid()# you can check if explicitlyifaddr:# Or implicitly using magic method __bool__()...assertaddr==addr# You can compare ScAddr with ScAddr if they are equalassertaddr.is_equal(addr)# Alternative variantassertaddr==0# InvalidTypeErrorScTypeEvery valid sc-element has some type.
Sc-type represents a bit mask. But you don't need to know it.
There is the ScType class that contains all methods to check type.
All common ScTypes like node const are already defined.sc_client.constants.ScTypeClass with type of sc-element.
It uses when new elements are created or if it's need to check right type.
It contains methods to check if it is node, edge or link, const or var, and so on.If you paid attention, the class is in constants submodule.
They are already defined, and you can import them from filesc_client.constants.sc_types.
If you need bitmasks, they are insc_client.constants.sc_types.bitmasks.fromsc_client.constantsimportsc_typessc_type_struct=sc_types.NODE_CONST_STRUCTassertsc_type_struct.is_valid()assertsc_type_struct.is_node()assertnotsc_type_struct.is_edge()assertnotsc_type_struct.is_link()assertsc_type_struct.is_const()assertnotsc_type_struct.is_var()assertsc_type_struct.is_struct()assertnotsc_type_struct.is_tuple()# And many moreStructure classesStructure classes are using to work with set of sc-elements.
ScConstruction uses individual elements like nodes and edges,
ScTemplate - tripletsScConstructionsc_client.models.ScConstructionClass that allow to create single nodes, edges and links.
You can use aliases to name nodes, and use one element several times in construction.Methods:ScConstruction.create_node(sc_type: ScType, alias: str = None)ScConstruction.create_edge(sc_type: ScType, src: str | ScAddr, trg: str | ScAddr, alias: str = None)ScConstruction.create_link(sc_type: ScType, content: ScLinkContent, alias: str = None)ScConstruction doesn't create elements. To do it use function:sc_client.client.create_elements(constr: ScConstruction)It returns list of all elements by ScConstructionconstr.fromsc_client.clientimportcreate_elementsfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScConstructionfromsc_client.modelsimportScLinkContent,ScLinkContentTypeconstruction=ScConstruction()# First you need initializeconstruction.create_node(sc_types.NODE_CONST,'node')# Create node constlink_content=ScLinkContent("Hello!",ScLinkContentType.STRING)# Create link contentconstruction.create_link(sc_types.LINK_CONST,link_content,'link')# Create link with that contentconstruction.create_edge(sc_types.EDGE_ACCESS_CONST_POS_PERM,'node','link')# Create unaliased edge between previous nodeaddrs=create_elements(construction)# List of elementsassertlen(addrs)==3# Assert that there is 3 elements as in the constructionassertall(addrs)# Assert that they are all validAlso, you can generate a construction by template address or its system identifier or scs-template as well as search
constructions.ScTemplatesc_client.models.ScTemplateClass that allow to create and search triplets.
You can also use aliases to name nodes, and use one element several times in template.Methods:ScTemplate.triple(src, edge, trg)Templatesrc-edge-trgScTemplate.triple_with_relation(src, edge, trg, edge2, src2)Two tripletssrc-edge-trgandsrc2-edge2-edgeTo set aliases use syntax:element >> aliasRecommended(element, alias)[element, alias]Deprecated in version 0.3.0After setting alias you use it without elementSearch templatesc_client.client.template_search(template: ScTemplate, params: ScTemplateParams = None)Returns list of ScTemplateResult bytemplate.fromsc_client.clientimporttemplate_searchfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScTemplate,ScAddraction_node:ScAddrquestion_node:ScAddrrrel_1:ScAddr# Some ScAddrs for exampletemplate=ScTemplate()template.triple(question_node,sc_types.EDGE_ACCESS_VAR_POS_PERM,action_node>>"_action_node")# Triple `question_node-(*new)edge_access-(*aliased with "_action_node")action_node`template.triple_with_relation("_action_node",sc_types.EDGE_ACCESS_VAR_POS_TEMP,sc_types.NODE_VAR>>"_arg_node",sc_types.EDGE_ACCESS_VAR_POS_TEMP,rrel_1,)search_results=template_search(template)Search by sc-template address.fromsc_client.clientimporttemplate_searchfromsc_client.modelsimportScAddrtemplate:ScAddr# Template from sc-memorysearch_results=template_search(template)search_result=search_results[0]You can also use ScAddr templates:Search by sc-template system identifier.fromsc_client.clientimporttemplate_searchfromsc_client.modelsimportScAddrlink_node:ScAddrsearch_params={'_link':link_node,'_var_node':'node_idtf'}search_results=template_search('my_template',search_params)search_result=search_results[0]Search by scs sc-template.fromsc_client.clientimporttemplate_searchsearch_results=template_search('class _-> _node;;')search_result=search_results[0]Generate templatesc_client.client.template_generate(template: ScTemplate, params: ScTemplateParams = None)Returns ScTemplateResult bytemplate.fromsc_client.clientimporttemplate_generatefromsc_client.constantsimportsc_typesfromsc_client.modelsimportScTemplate,ScAddrmain_node:ScAddrrelation_node:ScAddrlink_node:ScAddr# Some ScAddrs for exampletemplate=ScTemplate()template.triple_with_relation(main_node>>'_main_node',sc_types.EDGE_D_COMMON_VAR,sc_types.LINK_VAR>>'_link',sc_types.EDGE_ACCESS_VAR_POS_PERM,relation_node,)template.triple('_main_node',sc_types.EDGE_ACCESS_VAR_POS_TEMP,(sc_types.NODE_VAR,'_var_node'))gen_params={'_link':link_node,'_var_node':'node_idtf'}gen_result=template_generate(template,gen_params)Also, you can generate a construction by template address or its system identifier or scs-template as well as search
constructions.ScTemplateResultAfter operations with template you'll receive ScTemplateResult:sc_client.models.ScTemplateResultIt has all ScAddr and aliases. You can get addrs or iterate.Methods:len(ScTemplateResult)Get count of elementsScTemplateResult.size()The same, deprecated in version 0.3.0ScTemplateResult[i: int]Get ScAddr by indexScTemplateResult.get(alias_or_index: str | int)Get ScAddr by alias or indexiter(ScTemplateResult),next(ScTemplateResult)Iterate by triplets usingforScTemplateResult.for_each_triple(func: Callable[[ScAddr, ScAddr, ScAddr], Enum])Run function with each triple. Deprecated in version 0.3.0fromenumimportEnumfromsc_client.modelsimportScTemplateResult,ScAddrtemplate_result:ScTemplateResultlength=len(template_result)# in the resulting constructiontemplate_result.size()# deprecated count of elements, will be removed in version 0.3.0first_element=template_result[0]# get an element from the result by index (recommended)template_result.get(0)# get an element from the result by indexarg_node=template_result.get("_arg_node")# get an element from the result by aliasforsrc,edge,trgintemplate_result:...# do smth with each triple in the resultdeftriplets_function(src:ScAddr,edge:ScAddr,trg:ScAddr)->Enum:...template_result.for_each_triple(triplets_function)# to use function to each triple. Deprecated in 0.3.0Common functionsCheck element typessc_client.client.check_elements(*addrs: ScAddr)Returns list of ScTypes for given elements.fromsc_client.clientimportcheck_elementsfromsc_client.clientimportcreate_elementsfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScConstructionconstruction=ScConstruction()# Create elements for exampleconstruction.create_node(sc_types.NODE_CONST)construction.create_node(sc_types.NODE_VAR)elements=create_elements(construction)elements_types=check_elements(*elements)assertelements_types[0].is_node()assertnotelements_types[1].is_edge()assertelements_types[1].is_var()Create elements by SCSsc_client.client.create_elements_by_scs(texts: List[Union[str, SCs]])Create elements by scs texts in the KB memory,
put them in structure and returns boolean statuses.fromsc_client.clientimportcreate_elements_by_scsresults=create_elements_by_scs(["concept1 -> node1;;","concept1 -> ;;"])assertresults==[True,False]# Warning: it doesn't return False, it raised errorfromsc_client.clientimportcreate_elements_by_scs,create_elementsfromsc_client.constantsimportsc_typesfromsc_client.modelsimportSCs,ScConstructionconstruction=ScConstruction()# Create output_struct for exampleconstruction.create_node(sc_types.NODE_CONST)output_struct=create_elements(construction)[0]results=create_elements_by_scs([SCs("concept1 -> node1;;",output_struct),"concept1 -> node2;;"])assertresults==[True,True]Delete elementssc_client.client.delete_elements(*addrs: ScAddr)Deleteaddrsfrom the KB memory and returns boolean status.fromsc_client.clientimportcreate_elements,set_link_contentsfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScConstruction,ScLinkContent,ScLinkContentTypeconstruction=ScConstruction()# Create link for exampleconstruction.create_link(sc_types.LINK_CONST,ScLinkContent("One",ScLinkContentType.STRING))link=create_elements(construction)[0]link_content=ScLinkContent("Two",ScLinkContentType.STRING,link)status=set_link_contents(link_content)assertstatusResolve keynodessc_client.client.resolve_keynodes(*params: ScIdtfResolveParams)Resolve keynodes from the KB memory by ScIdtfResolveParams and return list of ScAddrs. If it doesn't exist, then create
a new one.sc_client.models.ScIdtfResolveParamsTyped-dict class that containsidtfand optionaltypeAdvice: better to use ScKeynodes from py-sc-kpmfromsc_client.clientimportresolve_keynodesfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScIdtfResolveParamsparams=ScIdtfResolveParams(idtf='new_keynode_that_doesnt_exist',type=sc_types.NODE_CONST)addrs=resolve_keynodes(params)# list with 1 new keynode addrparams=ScIdtfResolveParams(idtf='keynode_that_have_to_exist_but_doesnt',type=None)addrs=resolve_keynodes(params)# list with 1 invalid addrparams=ScIdtfResolveParams(idtf='keynode_that_exists',type=None)addrs=resolve_keynodes(params)# list with 1 keynode addrLink content functionsScLinkContent classsc_client.models.ScLinkContentClass that describes content, its type (enumsc_client.models.ScLinkContentType).It uses to create and change links.fromsc_client.modelsimportScLinkContent,ScLinkContentType,ScAddrstr_content=ScLinkContent("str content",ScLinkContentType.STRING)int_content=ScLinkContent(12,ScLinkContentType.INT)float_content=ScLinkContent(3.14,ScLinkContentType.FLOAT)link_addr:ScAddr# ScAddr of existed linklink_content=ScLinkContent(12,ScLinkContentType.INT,link_addr)deprecated_type=ScLinkContent("use enum without .value",ScLinkContentType.STRING.value)# Value type is deprecated and will be removed in version 0.3.0Set links contentsc_client.client.set_link_contents(*contents: ScLinkContent)Set the new content to corresponding links and return boolean status.fromsc_client.clientimportset_link_contents,create_elementsfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScLinkContent,ScLinkContentType,ScConstructionconstruction=ScConstruction()# Create link for examplelink_content1=ScLinkContent("One",ScLinkContentType.STRING)construction.create_link(sc_types.LINK_CONST,link_content1)link=create_elements(construction)[0]link_content2=ScLinkContent("Two",ScLinkContentType.STRING,link)status=set_link_contents(link_content2)assertstatusGet links contentsc_client.client.get_link_contents(*addr: ScAddr)Get list of contents of the given links.fromsc_client.clientimportcreate_elements,get_link_contentfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScLinkContent,ScLinkContentType,ScConstructionconstruction=ScConstruction()# Create link for examplelink_content1=ScLinkContent("One",ScLinkContentType.STRING)construction.create_link(sc_types.LINK_CONST,link_content1)link=create_elements(construction)[0]link_content=get_link_content(link)[0]assertlink_content.data==link_content1.dataGet links by contentsc_client.client.get_links_by_content(*contents: ScLinkContent | str | int)Get list of lists of links for every content.fromsc_client.clientimportcreate_elements,get_links_by_contentfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScLinkContent,ScLinkContentType,ScConstructionsearch_string="search string"construction=ScConstruction()# Create link with search stringlink_content1=ScLinkContent(search_string,ScLinkContentType.STRING)construction.create_link(sc_types.LINK_CONST,link_content1)link=create_elements(construction)[0]links=get_links_by_content(search_string)[0]assertlinkinlinksGet links by content substringsc_client.client.get_links_by_content_substring(*contents: ScLinkContent | str | int)Get list of lists of links for every content substring.fromsc_client.clientimportcreate_elements,get_links_by_content_substringfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScLinkContent,ScLinkContentType,ScConstructionsearch_string="substring1 substring2"construction=ScConstruction()# Create link with search stringlink_content1=ScLinkContent(search_string,ScLinkContentType.STRING)construction.create_link(sc_types.LINK_CONST,link_content1)link=create_elements(construction)[0]links_list=get_links_by_content_substring(*search_string.split(" "))assertall(linkinlinksforlinksinlinks_list)Get links contents by content substringsc_client.client.get_links_contents_by_content_substring(*contents: ScLinkContent | str | int)Get list of lists of contents of the given content substrings.Warning: it returns int addrsfromsc_client.clientimportcreate_elements,get_links_contents_by_content_substringfromsc_client.constantsimportsc_typesfromsc_client.modelsimportScLinkContent,ScLinkContentType,ScConstructionsearch_string="substring1 substring2"construction=ScConstruction()# Create link with search stringlink_content1=ScLinkContent(search_string,ScLinkContentType.STRING)construction.create_link(sc_types.LINK_CONST,link_content1)link_addr=create_elements(construction)[0]links_list=get_links_contents_by_content_substring(*search_string.split(" "))assertall(link_addr.valueinlinksforlinksinlinks_list)Events functionsCreate eventssc_client.client.events_create(*events: ScEventParams)Create an event in the KB memory by ScEventParams and return list of ScEvents.fromsc_client.clientimportevents_createfromsc_client.constants.commonimportScEventTypefromsc_client.modelsimportScEventParams,ScAddrdefevent_callback(src:ScAddr,edge:ScAddr,trg:ScAddr):...bounded_elem_addr:ScAddrevent_type=ScEventType.ADD_OUTGOING_EDGEevent_params=ScEventParams(bounded_elem_addr,event_type,event_callback)sc_event=events_create(event_params)Check event validitysc_client.client.is_event_valid(event: ScEvent)Return boolean status ifeventis active and or not.Parameters: An ScEvent class object.Returns: The boolean value (true if an event is valid).fromsc_client.clientimportis_event_validfromsc_client.modelsimportScEventsc_event:ScEventstatus=is_event_valid(sc_event)Destroy eventssc_client.client.events_destroy(*events: ScEvent)Destroyeventsin the KB memory and return boolean status.fromsc_client.clientimportevents_destroyfromsc_client.modelsimportScEventsc_event:ScEventstatus=events_destroy(sc_event)ClassesWarning: these classes are deprecated because they are realized in py-sc-kpm.The library contains the python implementation of useful classes and functions to work with the sc-memory.There is a list of classes:ScKeynodesScAgentScModuleScKeynodesA singleton dictionary object which provides the ability to cache the identifier and ScAddr of keynodes stored in the
KB.
Create an instance of the ScKeynodes class to get access to the cache:keynodes=ScKeynodes()Get the provided identifier:keynodes["identifier_of_keynode"]# returns an ScAddr of the given identifierkeynodes["not_stored_in_kb"]# returns an invalid ScAddr if an identifier does not exist in the memoryUseresolve_identifiers()to upload identifiers fromEnumclasses:classCommonIdentifiers(Enum):RREL_ONE="rrel_1"RREL_TWO="rrel_2"classQuestionStatus(Enum):QUESTION_INITIATED="question_initiated"QUESTION_FINISHED="question_finished"QUESTION_FINISHED_SUCCESSFULLY="question_finished_successfully"QUESTION_FINISHED_UNSUCCESSFULLY="question_finished_unsuccessfully"keynodes.resolve_identifiers([QuestionStatus,CommonIdentifiers])ScAgentA class for handling a single ScEvent. Define your agents like this:classMyAgent(ScAgent):action="Identifier_of_action_class"defregister(self)->ScEvent:# override method, must return an ScEvent instanceparams=[MyAgent.keynodes["action_initiated"],common.ScEventType.ADD_OUTGOING_EDGE,MyAgent.run_impl]event_params=ScEventParams(*params)sc_event=client.events_create(event_params)returnsc_event[0]@staticmethoddefrun_impl(action_class:ScAddr,edge:ScAddr,action_node:ScAddr)->None:# override method, must have 3 args and be static...ScModuleA class for handling a multiple ScAgent. Define your modules like this:classMyModule(ScModule):def__init__(self)->None:agents_to_register=[MyAgent1,MyAgent2]# list of agent classessuper().__init__(agents_to_register)LoggingSometimes you might be in a situation where you deal with data that should be correct, but actually is not.
You may still want to log that something fishy happened. This is where loggers come in handy.
Default logger is preconfigured for you to use.There is an example for logs review using root logger:importloggingfromsc_clientimportclientroot_logger=logging.getLogger()root_logger.level=logging.DEBUGroot_logger.addHandler(logging.StreamHandler())client.connect("ws://localhost:8090/ws_json")# Connectedresult=client.create_elements_by_scs([])# Send: {"id": 2, "type": "create_elements_by_scs", "payload": []}# Receive: {"errors":[],"event":0,"id":2,"payload":[],"status":1}client.disconnect()# DisconnectedSeelogging documentationfor more information.