def _handle_incoming_call(self, im: GenIncomingMessage): # TODO: noreply, and other stop codes call_msg = im.message_ if isinstance(call_msg, tuple): f_name = as_str(call_msg[0]) f_args = list(call_msg[1:]) else: f_name = as_str(call_msg) f_args = [] if f_name not in self.gen_accepted_calls_: raise GenException( "Call to method %s is not in accepted_calls list" % f_name) try: method = getattr(self, f_name) LOG.debug("method=%s", method) result = method(*f_args) LOG.debug("Replying with result=%s", result) im.reply(local_pid=self.pid_, result=result) except Exception as excpt: # Send an error if self.traceback_depth_ > 0: excpt.traceback = traceback.format_exc(self.traceback_depth_) im.reply_exit(local_pid=self.pid_, reason=excpt)
def parse_gen_call(msg, node_name: str): """ Determine if msg is a gen:call message and create a :py:class:`~Pyrlang.gen.GenIncomingCall` object. .. note:: Module and function parameters to ``rpc:call`` can be binary, ASCII strings or atoms. .. note:: You do not need to import module in ``rpc:call``, it is done by Rex. :param node_name: Name of the current node, used to route replies back to the caller :param msg: An Erlang tuple hopefully starting with a '$gen_call' :return: str with error if msg wasn't a call message, otherwise constructs and returns a ``GenIncomingCall`` object. """ # Incoming {$gen_call, {From, Ref}, {call, Mod, Fun, Args}} if type(msg) != tuple: # ignore all non-tuple messages return "Only {tuple} messages allowed" # ignore tuples with non-atom 1st, ignore non-gen_call mesages if not isinstance(msg[0], Atom) or msg[0].text_ != '$gen_call': return "Only {$gen_call, _, _} messages allowed" (_, _sender_mref, _call_mfa_gl) = msg (msender, mref) = _sender_mref # TODO: Maybe also check first element to be an atom 'call' if len(_call_mfa_gl) != 5: return "Expecting a 5-tuple (with a 'call' atom)" (call, m, f, args, group_leader) = _call_mfa_gl return GenIncomingCall( mod=as_str(m), fun=as_str(f), args=args, group_leader=group_leader, sender=msender, # pid of the sender ref=mref, # reference used in response node_name=node_name)
def _resolve_path(self, p: List[str]) -> Callable: """ Imports p[0] and then follows the list p, by applying getattr() repeatedly. """ if isinstance(p, str): p = [p] # First element would be the import, or a stored value reference first_path_element = p[0] if isinstance(first_path_element, tuple) \ and first_path_element[0] == Atom("$pyrlangval"): # First element is {'$pyrlangval', X} - query the value val = self._retrieve_value(first_path_element) else: # First element is a string, import it val = __import__(as_str(first_path_element)) # Follow the elements in path, and getattr deeper for item in p[1:]: val = getattr(val, as_str(item)) return val