Esempio n. 1
0
    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'))
Esempio n. 2
0
    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)
Esempio n. 3
0
    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))
Esempio n. 4
0
    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)