def receive_ask_one(self, msg: KQMLPerformative, content: KQMLList): """Override of default ask one, creates Companions style responses. Gets the arguments bindings from the cdr of the content. The predicate (car) is then used to find the function bound to the ask predicate, and that function is called with the bounded argument list unpacked into it's own inputs. The resulting query is then passed along to the response_to_query helper which will properly respond to patterns or bindings based on out response type. Arguments: msg (KQMLPerformative): reply mechanism content (KQMLList): predicate to look up in asks dict, arguments of the ask call - to be passed in to the call. Returns: None: returns only to exit function early if conditions aren't met """ if content.head() not in self.asks: error_msg = f'No ask query predicate named {content.head()} known' LOGGER.warning(error_msg) self.error_reply(msg, error_msg) return bounded = [] for each in content.data[1:]: if str(each[0]) != '?': bounded.append(each) ask_question = self.asks[content.head()] # TODO - same argument structure issue as achieve, won't work with self expected_args = len(getfullargspec(ask_question).args) if expected_args != len(bounded): error_msg = (f'Expected {expected_args} input arguments to query ' f'predicate {content.head()}, got {len(bounded)}') LOGGER.warning(error_msg) self.error_reply(msg, error_msg) return LOGGER.info('received ask-one %s', content.head()) try: results = self.asks[content.head()](*bounded) except (TypeError, ValueError) as except_msg: LOGGER.warning('Failed execution: %s, %s', except_msg, print_exc()) error_msg = f'An error occurred while executing: {content.head()}' self.error_reply(msg, error_msg) return LOGGER.debug('Ask-one returned results: %s', results) self.response_to_query(msg, content, results, msg.get('response'))
def receive_achieve(self, msg: KQMLPerformative, content: KQMLList): """Overrides the default KQMLModule receive for achieves and instead does basic error checking before attempting the action by calling the proper ask function with the arguments passed along as inputs. Arguments: msg (KQMLPerformative): predicate/ signifier of task (message sent to python from companions) content (KQMLList): action task is referring to (content of message) Returns: None: returns only to exit function early if conditions aren't met """ if content.head() != 'task': error_msg = (f'Only support achieve command of task, instead got ' f'{content.head()}') LOGGER.debug(error_msg) self.error_reply(msg, error_msg) return action = content.get('action') if not action: error_msg = 'No action for achieve task provided' LOGGER.debug(error_msg) self.error_reply(msg, error_msg) return if action.head() not in self.achieves: error_msg = f'No action named {action.head()} is known' LOGGER.debug(error_msg) self.error_reply(msg, error_msg) return achieve_question = self.achieves[action.head()] expected_args = len(getfullargspec(achieve_question).args) actual_args = action.data[1:] if expected_args != len(actual_args): error_msg = (f'Expected {expected_args} input arguments to achieve' f' task {action.head()}, got {len(actual_args)}') LOGGER.debug(error_msg) self.error_reply(msg, error_msg) return LOGGER.info('received achieve %s', action.head()) try: results = self.achieves[action.head()](*actual_args) except (TypeError, ValueError) as except_msg: LOGGER.debug('Failed execution: %s, %s', except_msg, print_exc()) error_msg = f'An error occurred while executing {action.head()}' self.error_reply(msg, error_msg) return LOGGER.debug('Acheive returned results: %s', results) reply = performative(f'(tell :sender {self.name} :content ' f'{listify(results)})') self.reply(msg, reply)
def receive_subscribe(self, msg: KQMLPerformative, content: KQMLList): """Override of KQMLModule default, expects a performative of ask-all. Gets the ask-all query from the message contents, then checks to see if the query head is in the dictionary of available asks and checks if the query string is in the dictionary of subscribers. If both of these are true we then append the message to the subscriber query, clean out any previous subscription data, and reply with a tell ok message. Arguments: msg (KQMLPerformative): performative to be passed along to reply and stored in the subscribers dictionary (for future replies) content (KQMLList): ask-all for a query Returns: None: returns only to exit function early if conditions aren't met """ if content.head() != 'ask-all': error_msg = (f'Only supports ask-all subscription, received ' f'unsupported performative {content.head()}') LOGGER.debug(error_msg) self.error_reply(msg, error_msg) return query = content.get('content') pattern = query.to_string() if query.head() not in self.asks: error_msg = f'No ask named {query.head()} is known' LOGGER.debug(error_msg) self.error_reply(msg, error_msg) return if pattern not in self.subscriptions: error_msg = f'Ask ({query.head()}) is not subscribable' LOGGER.debug(error_msg) self.error_reply(msg, error_msg) return LOGGER.info('received subscription %s to %s', msg, pattern) self.subscriptions.subscribe(pattern, msg) reply_msg = f'(tell :sender {self.name} :content :ok)' self.reply(msg, performative(reply_msg))
def receive_achieve(self, msg: KQMLPerformative, content: KQMLList): """Overrides the default KQMLModule receive for achieves and instead does basic error checking before attempting the action by calling the proper ask function with the arguments passed along as inputs. Arguments: msg (KQMLPerformative): predicate/ signifier of task (message sent to python from companions) content (KQMLList): action task is referring to (content of message) Returns: None: returns only to exit function early if conditions aren't met """ if content.head() != 'task': error_msg = (f'Only support achieve command of task, instead got ' f'{content.head()}') LOGGER.warning(error_msg) self.error_reply(msg, error_msg) return action = content.get('action') if not action: error_msg = 'No action for achieve task provided' LOGGER.warning(error_msg) self.error_reply(msg, error_msg) return if action.head() not in self.achieves: error_msg = f'No action named {action.head()} is known' LOGGER.warning(error_msg) self.error_reply(msg, error_msg) return achieve_question = self.achieves[action.head()] # TODO - argument structure and call won't work if the self argument # is needed. Easy enough to filter out the arguments but it will be # harder to use self if that self argument doesn't correlate to a # pythonian object... ie if someone passes in a function that is from # inside another class. question_args = getfullargspec(achieve_question).args # call_needs_self = False # if question_args[0] is 'self': # question_args = question_args[1:] # call_needs_self = True expected_args = len(question_args) actual_args = action.data[1:] if expected_args != len(actual_args): error_msg = (f'Expected {expected_args} input arguments to achieve' f' task {action.head()}, got {len(actual_args)}') LOGGER.warning(error_msg) self.error_reply(msg, error_msg) return LOGGER.info('received achieve %s', action.head()) try: results = self.achieves[action.head()](*actual_args) except (TypeError, ValueError) as except_msg: LOGGER.warning('Failed execution: %s, %s', except_msg, print_exc()) error_msg = f'An error occurred while executing {action.head()}' self.error_reply(msg, error_msg) return LOGGER.debug('Acheive returned results: %s', results) reply = performative(f'(tell :sender {self.name} :content ' f'{listify(results)})') self.reply(msg, reply)