Esempio n. 1
0
    def monitor_process(self, origin_pid: Pid, target, ref=None):
        """ Locate the process referenced by the target and place the origin
            pid into its ``monitors_`` collection. When something happens to the
            ``target``, a special message will be sent to the ``origin``.
            Remote targets are supported.

            :param ref: If not None, will be reused, else a new random ref will
                be generated.
            :type ref: None or term.reference.Reference
            :type origin_pid: term.pid.Pid
            :param origin_pid: The (possibly remote) process who will be monitoring
                the target from now and wants to know when we exit.
            :type target: term.pid.Pid or term.atom.Atom
            :param target: Name or pid of a monitor target process.
            :rtype: term.reference.Reference
            :raises pyrlang.node.ProcessNotFoundError: if target does not exist.
        """
        target_pid = self.where_is(target)
        m_ref = ref if ref is not None \
            else Reference.create(node_name=self.node_name_,
                                  creation=self.dist_.creation_)

        if not origin_pid.is_local_to(self):
            # Origin is remote and wants to monitor local process
            return self._monitor_local_process(origin_pid, target_pid, m_ref)
        if target_pid.is_local_to(self):
            # Target is local and we notify a local process
            return self._monitor_local_process(origin_pid, target_pid, m_ref)
        # Target is remote and a distribution message has to be sent
        return self._monitor_remote_process(origin_pid, target_pid, m_ref)
Esempio n. 2
0
 def _encode_pid(self, codec):
     sample1 = bytes([py_impl.ETF_VERSION_TAG,
                      py_impl.TAG_PID_EXT,
                      py_impl.TAG_SMALL_ATOM_UTF8_EXT, 13]) \
               + bytes("nonode@nohost", "latin-1") \
               + bytes([0, 0, 0, 1,
                        0, 0, 0, 2,
                        3])
     val = Pid("nonode@nohost", 1, 2, 3)
     bin1 = codec.term_to_binary(val, None)
     self.assertEqual(bin1, sample1)
Esempio n. 3
0
    def _monitor_remote_process(self, origin_pid: Pid, target_pid: Pid,
                                ref: Reference):
        monitor_msg = ('monitor_p', origin_pid, target_pid, ref)
        self._dist_command(receiver_node=target_pid.node_name_,
                           message=monitor_msg)

        # if the origin is local, register monitor in it. Remote pids are
        # handled by the remote
        assert origin_pid.is_local_to(self)
        origin_p = self.where_is_process(origin_pid)
        origin_p.add_monitor(pid=target_pid, ref=ref)
Esempio n. 4
0
    def _demonitor_local_process(self, origin_pid: Pid, target_pid: Pid,
                                 ref: Reference):
        target_proc = self.processes_.get(target_pid, None)
        if target_proc is not None:
            target_proc.remove_monitored_by(ref=ref, pid=origin_pid)
        else:
            msg = "Demonitor target %s does not exist" % target_pid
            LOG.error(msg)
            raise ProcessNotFoundError(msg)

        # if the origin is local, unregister monitor from it
        # Remotely set monitors have remote origin
        if origin_pid.is_local_to(self):
            origin_p = self.where_is_process(origin_pid)
            origin_p.remove_monitor(ref=ref, pid=target_pid)
Esempio n. 5
0
    def _monitor_local_process(self, origin_pid: Pid, target_pid: Pid,
                               ref: Reference):
        """ Monitor a local target. """
        target_proc = self.processes_.get(target_pid, None)
        # LOG.info("Monitor: orig=%s targ=%s -> %s", origin, target, target_proc)

        if target_proc is not None:
            target_proc.add_monitored_by(pid=origin_pid, ref=ref)
        else:
            msg = "Monitor target %s does not exist" % target_pid
            LOG.error(msg)
            raise ProcessNotFoundError(msg)

        # if the origin is local, register monitor in it.
        # Remotely set monitors have remote origin
        if origin_pid.is_local_to(self):
            origin_p = self.where_is_process(origin_pid)
            origin_p.add_monitor(pid=target_proc.pid_, ref=ref)
Esempio n. 6
0
    def register_new_process(self, proc=None) -> Pid:
        """ Generate a new pid and add the process to the process dictionary.

            :type proc: Process or None
            :param proc: A new process which needs a pid, or None if you only
                need a fake pid
            :return: A new pid (does not modify the process in place, so please
                store the pid!)
        """
        pid1 = Pid(node_name=self.node_name_,
                   id=self.pid_counter_ // 0x7fffffff,
                   serial=self.pid_counter_ % 0x7fffffff,
                   creation=self.dist_.creation_)
        self.pid_counter_ += 1

        if proc is not None:
            self.processes_[pid1] = proc
        return pid1
Esempio n. 7
0
def binary_to_term_2(data: bytes, options: dict = None) -> (any, bytes):
    """ Proceed decoding after leading tag has been checked and removed.

        Erlang lists are decoded into ``term.List`` object, whose ``elements_``
        field contains the data, ``tail_`` field has the optional tail and a
        helper function exists to assist with extracting an unicode string.

        Atoms are decoded to :py:class:`~Term.atom.Atom` or optionally to bytes
        or to strings.
        Pids are decoded into :py:class:`~Term.pid.Pid`.
        Refs decoded to and :py:class:`~Term.reference.Reference`.
        Maps are decoded into Python ``dict``.
        Binaries are decoded into ``bytes``
        object and bitstrings into a pair of ``(bytes, last_byte_bits:int)``.

        :param options: dict(str, _);
                * "atom": "str" | "bytes" | "Atom"; default "Atom".
                  Returns atoms as strings, as bytes or as atom.Atom class objects
               * "byte_string": "str" | "bytes" (default "str").
                 Returns 8-bit strings as Python str or bytes.
        :param data: Bytes containing encoded term without 131 header
        :rtype: Tuple[Value, Tail: bytes]
        :return: The function consumes as much data as
            possible and returns the tail. Tail can be used again to parse
            another term if there was any.
        :raises PyCodecError(str): on various errors
    """
    if options is None:
        options = {}

    create_atom_fn = _get_create_atom_fn(options.get("atom", "Atom"))
    create_str_fn = _get_create_str_fn(options.get("byte_string", "str"))

    tag = data[0]

    if tag in [TAG_ATOM_EXT, TAG_ATOM_UTF8_EXT]:
        len_data = len(data)
        if len_data < 3:
            return incomplete_data("decoding length for an atom name")

        len_expected = util.u16(data, 1) + 3
        if len_expected > len_data:
            return incomplete_data("decoding text for an atom")

        name = data[3:len_expected]
        enc = 'latin-1' if tag == TAG_ATOM_EXT else 'utf8'
        return _bytes_to_atom(name, enc, create_atom_fn), data[len_expected:]

    if tag in [TAG_SMALL_ATOM_EXT, TAG_SMALL_ATOM_UTF8_EXT]:
        len_data = len(data)
        if len_data < 2:
            return incomplete_data("decoding length for a small-atom name")

        len_expected = data[1] + 2
        name = data[2:len_expected]

        enc = 'latin-1' if tag == TAG_SMALL_ATOM_EXT else 'utf8'
        return _bytes_to_atom(name, enc, create_atom_fn), data[len_expected:]

    if tag == TAG_NIL_EXT:
        return [], data[1:]

    if tag == TAG_STRING_EXT:
        len_data = len(data)
        if len_data < 3:
            return incomplete_data("decoding length for a string")

        len_expected = util.u16(data, 1) + 3

        if len_expected > len_data:
            return incomplete_data()

        return create_str_fn(data[3:len_expected]), data[len_expected:]

    if tag == TAG_LIST_EXT:
        if len(data) < 5:
            return incomplete_data("decoding length for a list")
        len_expected = util.u32(data, 1)
        result_l = []
        tail = data[5:]
        while len_expected > 0:
            term1, tail = binary_to_term_2(tail)
            result_l.append(term1)
            len_expected -= 1

        # Read list tail and set it
        list_tail, tail = binary_to_term_2(tail)
        if list_tail == NIL:
            return result_l, tail
        return (result_l, list_tail), tail

    if tag == TAG_SMALL_TUPLE_EXT:
        if len(data) < 2:
            return incomplete_data("decoding length for a small tuple")
        len_expected = data[1]
        result_t = []
        tail = data[2:]
        while len_expected > 0:
            term1, tail = binary_to_term_2(tail)
            result_t.append(term1)
            len_expected -= 1

        return tuple(result_t), tail

    if tag == TAG_LARGE_TUPLE_EXT:
        if len(data) < 5:
            return incomplete_data("decoding length for a large tuple")
        len_expected = util.u32(data, 1)
        result_lt = []
        tail = data[5:]
        while len_expected > 0:
            term1, tail = binary_to_term_2(tail)
            result_lt.append(term1)
            len_expected -= 1

        return tuple(result_lt), tail

    if tag == TAG_SMALL_INT:
        if len(data) < 2:
            return incomplete_data("decoding a 8-bit small uint")
        return data[1], data[2:]

    if tag == TAG_INT:
        if len(data) < 5:
            return incomplete_data("decoding a 32-bit int")
        return util.i32(data, 1), data[5:]

    if tag == TAG_PID_EXT:
        node, tail = binary_to_term_2(data[1:])
        id1 = util.u32(tail, 0)
        serial = util.u32(tail, 4)
        creation = tail[8]

        assert isinstance(node, Atom)
        pid = Pid(node_name=node.text_,
                  id=id1,
                  serial=serial,
                  creation=creation)
        return pid, tail[9:]

    if tag == TAG_NEW_REF_EXT:
        if len(data) < 2:
            return incomplete_data("decoding length for a new-ref")
        term_len = util.u16(data, 1)
        node, tail = binary_to_term_2(data[3:])
        creation = tail[0]
        id_len = 4 * term_len
        id1 = tail[1:id_len + 1]

        ref = Reference(node_name=node.text_, creation=creation, refid=id1)
        return ref, tail[id_len + 1:]

    if tag == TAG_MAP_EXT:
        if len(data) < 5:
            return incomplete_data("decoding length for a map")
        len_expected = util.u32(data, 1)
        result_m = {}
        tail = data[5:]
        while len_expected > 0:
            term1, tail = binary_to_term_2(tail)
            term2, tail = binary_to_term_2(tail)
            result_m[term1] = term2
            len_expected -= 1

        return result_m, tail

    if tag == TAG_BINARY_EXT:
        len_data = len(data)
        if len_data < 5:
            return incomplete_data("decoding length for a binary")
        len_expected = util.u32(data, 1) + 5
        if len_expected > len_data:
            return incomplete_data("decoding data for a binary")

        # Returned as Python `bytes`
        return data[5:len_expected], data[len_expected:]

    if tag == TAG_BIT_BINARY_EXT:
        len_data = len(data)
        if len_data < 6:
            return incomplete_data("decoding length for a bit-binary")
        len_expected = util.u32(data, 1) + 6
        lbb = data[5]
        if len_expected > len_data:
            return incomplete_data("decoding data for a bit-binary")

        # Returned as tuple `(bytes, last_byte_bits:int)`
        return (data[6:len_expected], lbb), data[len_expected:]

    if tag == TAG_NEW_FLOAT_EXT:
        (result_f, ) = struct.unpack(">d", data[1:9])
        return result_f, data[9:]

    if tag == TAG_SMALL_BIG_EXT:
        nbytes = data[1]
        # Data is encoded little-endian as bytes (least significant first)
        in_bytes = data[3:(3 + nbytes)]
        # NOTE: int.from_bytes is Python 3.2+
        result_bi = int.from_bytes(in_bytes, byteorder='little')
        if data[2] != 0:
            result_bi = -result_bi
        return result_bi, data[3 + nbytes:]

    if tag == TAG_LARGE_BIG_EXT:
        nbytes = util.u32(data, 1)
        # Data is encoded little-endian as bytes (least significant first)
        in_bytes = data[6:(6 + nbytes)]
        # NOTE: int.from_bytes is Python 3.2+
        result_lbi = int.from_bytes(in_bytes, byteorder='little')
        if data[5] != 0:
            result_lbi = -result_lbi
        return result_lbi, data[6 + nbytes:]

    if tag == TAG_NEW_FUN_EXT:
        # size = util.u32(data, 1)
        arity = data[5]
        uniq = data[6:22]
        index = util.u32(data, 22)
        num_free = util.u32(data, 26)
        (mod, tail) = binary_to_term_2(data[30:])
        (old_index, tail) = binary_to_term_2(tail)
        (old_uniq, tail) = binary_to_term_2(tail)
        (pid, tail) = binary_to_term_2(tail)

        free_vars = []
        while num_free > 0:
            (v, tail) = binary_to_term_2(tail)
            free_vars.append(v)
            num_free -= 1

        return Fun(mod=mod,
                   arity=arity,
                   pid=pid,
                   index=index,
                   uniq=uniq,
                   old_index=old_index,
                   old_uniq=old_uniq,
                   free=free_vars), tail

    raise PyCodecError("Unknown tag %d" % data[0])