Esempio n. 1
0
    def func(self):
        """
        Adds the line without any formatting changes.

        If the editor handles code, it might add automatic
        indentation.
        """
        caller = self.caller
        editor = caller.ndb._eveditor
        buf = editor.get_buffer()

        # add a line of text to buffer
        line = self.raw_string.strip("\r\n")
        if editor._codefunc and editor._indent >= 0:
            # if automatic indentation is active, add spaces
            line = editor.deduce_indent(line, buf)
        buf = line if not buf else buf + "\n%s" % line
        self.editor.update_buffer(buf)
        if self.editor._echo_mode:
            # need to do it here or we will be off one line
            cline = len(self.editor.get_buffer().split('\n'))
            if editor._codefunc:
                # display the current level of identation
                indent = editor._indent
                if indent < 0:
                    indent = "off"

                self.caller.msg("|b%02i|||n (|g%s|n) %s" % (
                    cline, indent, raw(line)))
            else:
                self.caller.msg("|b%02i|||n %s" % (cline, raw(self.args)))
Esempio n. 2
0
    def display_buffer(self, buf=None, offset=0, linenums=True, options={"raw": False}):
        """
        This displays the line editor buffer, or selected parts of it.

        Args:
            buf (str, optional): The buffer or part of buffer to display.
            offset (int, optional): If `buf` is set and is not the full buffer,
                `offset` should define the actual starting line number, to
                get the linenum display right.
            linenums (bool, optional): Show line numbers in buffer.
            options: raw (bool, optional): Tell protocol to not parse
                formatting information.

        """
        if buf is None:
            buf = self._buffer
        if is_iter(buf):
            buf = "\n".join(buf)

        lines = buf.split('\n')
        nlines = len(lines)
        nwords = len(buf.split())
        nchars = len(buf)

        sep = self._sep
        header = "|n" + sep * 10 + "Line Editor [%s]" % self._key + sep * (_DEFAULT_WIDTH - 24 - len(self._key))
        footer = "|n" + sep * 10 +\
                 "[l:%02i w:%03i c:%04i]" % (nlines, nwords, nchars) + sep * 12 + "(:h for help)" + sep * (_DEFAULT_WIDTH - 54)
        if linenums:
            main = "\n".join("|b%02i|||n %s" % (iline + 1 + offset, raw(line)) for iline, line in enumerate(lines))
        else:
            main = "\n".join([raw(line) for line in lines])
        string = "%s\n%s\n%s" % (header, main, footer)
        self._caller.msg(string, options=options)
Esempio n. 3
0
    def func(self):
        """
        Adds the line without any formatting changes.

        If the editor handles code, it might add automatic
        indentation.
        """
        caller = self.caller
        editor = caller.ndb._eveditor
        buf = editor.get_buffer()

        # add a line of text to buffer
        line = self.raw_string.strip("\r\n")
        if editor._codefunc and editor._indent >= 0:
            # if automatic indentation is active, add spaces
            line = editor.deduce_indent(line, buf)
        buf = line if not buf else buf + "\n%s" % line
        self.editor.update_buffer(buf)
        if self.editor._echo_mode:
            # need to do it here or we will be off one line
            cline = len(self.editor.get_buffer().split('\n'))
            if editor._codefunc:
                # display the current level of identation
                indent = editor._indent
                if indent < 0:
                    indent = "off"

                self.caller.msg("|b%02i|||n (|g%s|n) %s" %
                                (cline, indent, raw(line)))
            else:
                self.caller.msg("|b%02i|||n %s" % (cline, raw(self.args)))
Esempio n. 4
0
    def handle_error(self, callback, trace):
        """
        Handle an error in a callback.

        Args:
            callback (dict): the callback representation.
            trace (list): the traceback containing the exception.

        Notes:
            This method can be useful to override to change the default
            handling of errors.  By default, the error message is sent to
            the character who last updated the callback, if connected.
            If not, display to the everror channel.

        """
        callback_name = callback["name"]
        number = callback["number"]
        obj = callback["obj"]
        oid = obj.id
        logger.log_err("An error occurred during the callback {} of "
                       "{} (#{}), number {}\n{}".format(
                           callback_name, obj, oid, number + 1,
                           "\n".join(trace)))

        # Create the error message
        line = "|runknown|n"
        lineno = "|runknown|n"
        for error in trace:
            if error.startswith('  File "<string>", line '):
                res = RE_LINE_ERROR.search(error)
                if res:
                    lineno = int(res.group(1))

                    # Try to extract the line
                    try:
                        line = raw(callback["code"].splitlines()[lineno - 1])
                    except IndexError:
                        continue
                    else:
                        break

        exc = raw(trace[-1].strip("\n").splitlines()[-1])
        err_msg = "Error in {} of {} (#{})[{}], line {}:" \
            " {}\n{}".format(callback_name, obj,
                             oid, number + 1, lineno, line, exc)

        # Inform the last updater if connected
        updater = callback.get("updated_by")
        if updater is None:
            updater = callback["created_by"]

        if updater and updater.sessions.all():
            updater.msg(err_msg)
        else:
            err_msg = "Error in {} of {} (#{})[{}], line {}:" \
                " {}\n          {}".format(callback_name, obj,
                                           oid, number + 1, lineno, line, exc)
            self.ndb.channel.msg(err_msg)
Esempio n. 5
0
    def handle_error(self, callback, trace):
        """
        Handle an error in a callback.

        Args:
            callback (dict): the callback representation.
            trace (list): the traceback containing the exception.

        Notes:
            This method can be useful to override to change the default
            handling of errors.  By default, the error message is sent to
            the character who last updated the callback, if connected.
            If not, display to the everror channel.

        """
        callback_name = callback["name"]
        number = callback["number"]
        obj = callback["obj"]
        oid = obj.id
        logger.log_err("An error occurred during the callback {} of " \
                "{} (#{}), number {}\n{}".format(callback_name, obj,
                oid, number + 1, "\n".join(trace)))

        # Create the error message
        line = "|runknown|n"
        lineno = "|runknown|n"
        for error in trace:
            if error.startswith('  File "<string>", line '):
                res = RE_LINE_ERROR.search(error)
                if res:
                    lineno = int(res.group(1))

                    # Try to extract the line
                    try:
                        line = raw(callback["code"].splitlines()[lineno - 1])
                    except IndexError:
                        continue
                    else:
                        break

        exc = raw(trace[-1].strip("\n").splitlines()[-1])
        err_msg = "Error in {} of {} (#{})[{}], line {}:" \
                " {}\n{}".format(callback_name, obj,
                oid, number + 1, lineno, line, exc)

        # Inform the last updater if connected
        updater = callback.get("updated_by")
        if updater is None:
            updater = callback["created_by"]

        if updater and updater.sessions.all():
            updater.msg(err_msg)
        else:
            err_msg = "Error in {} of {} (#{})[{}], line {}:" \
                    " {}\n          {}".format(callback_name, obj,
                    oid, number + 1, lineno, line, exc)
            self.ndb.channel.msg(err_msg)
Esempio n. 6
0
    def display_buffer(self, buf=None, offset=0, linenums=True, options={"raw": False}):
        """
        This displays the line editor buffer, or selected parts of it.

        Args:
            buf (str, optional): The buffer or part of buffer to display.
            offset (int, optional): If `buf` is set and is not the full buffer,
                `offset` should define the actual starting line number, to
                get the linenum display right.
            linenums (bool, optional): Show line numbers in buffer.
            options: raw (bool, optional): Tell protocol to not parse
                formatting information.

        """
        if buf is None:
            buf = self._buffer
        if is_iter(buf):
            buf = "\n".join(buf)

        lines = buf.split("\n")
        nlines = len(lines)
        nwords = len(buf.split())
        nchars = len(buf)

        sep = self._sep
        header = (
            "|n"
            + sep * 10
            + "Line Editor [%s]" % self._key
            + sep * (_DEFAULT_WIDTH - 24 - len(self._key))
        )
        footer = (
            "|n"
            + sep * 10
            + "[l:%02i w:%03i c:%04i]" % (nlines, nwords, nchars)
            + sep * 12
            + "(:h for help)"
            + sep * (_DEFAULT_WIDTH - 54)
        )
        if linenums:
            main = "\n".join(
                "|b%02i|||n %s" % (iline + 1 + offset, raw(line))
                for iline, line in enumerate(lines)
            )
        else:
            main = "\n".join([raw(line) for line in lines])
        string = "%s\n%s\n%s" % (header, main, footer)
        self._caller.msg(string, options=options)
Esempio n. 7
0
    def add_callback(self):
        """Add a callback."""
        obj = self.obj
        callback_name = self.callback_name
        types = self.handler.get_events(obj)

        # Check that the callback exists
        if not callback_name.startswith("chain_") and callback_name not in types:
            self.msg("The callback name {} can't be found in {} of "
                     "typeclass {}.".format(callback_name, obj, type(obj)))
            return

        definition = types.get(callback_name, (None, "Chained event."))
        description = definition[1]
        self.msg(raw(description.strip("\n")))

        # Open the editor
        callback = self.handler.add_callback(obj, callback_name, "",
                                             self.caller, False, parameters=self.parameters)

        # Lock this callback right away
        self.handler.db.locked.append((obj, callback_name, callback["number"]))

        # Open the editor for this callback
        self.caller.db._callback = callback
        EvEditor(self.caller, loadfunc=_ev_load, savefunc=_ev_save,
                 quitfunc=_ev_quit, key="Callback {} of {}".format(
                     callback_name, obj), persistent=True, codefunc=_ev_save)
Esempio n. 8
0
    def format_usage(self):
        """Return the usage line.

        Note:
            This method is present to return the raw-escaped usage line,
            in order to avoid unintentional color codes.

        """
        return raw(super(UnixCommandParser, self).format_usage())
Esempio n. 9
0
    def format_usage(self):
        """Return the usage line.

        Note:
            This method is present to return the raw-escaped usage line,
            in order to avoid unintentional color codes.

        """
        return raw(super(UnixCommandParser, self).format_usage())
Esempio n. 10
0
    def format_help(self):
        """Return the parser help, including its epilog.

        Note:
            This method is present to return the raw-escaped help,
            in order to avoid unintentional color codes.  Color codes
            in the epilog (the command docstring) are supported.

        """
        autohelp = raw(super(UnixCommandParser, self).format_help())
        return "\n" + autohelp + "\n" + self.post_help
Esempio n. 11
0
    def format_help(self):
        """Return the parser help, including its epilog.

        Note:
            This method is present to return the raw-escaped help,
            in order to avoid unintentional color codes.  Color codes
            in the epilog (the command docstring) are supported.

        """
        autohelp = raw(super(UnixCommandParser, self).format_help())
        return "\n" + autohelp + "\n" + self.post_help
Esempio n. 12
0
    def privmsg(self, user, channel, msg):
        """
        Called when the connected channel receives a message.

        Args:
            user (str): User name sending the message.
            channel (str): Channel name seeing the message.
            msg (str): The message arriving from channel.

        """
        if not msg.startswith('***'):
            user = user.split('!', 1)[0]
            user = ansi.raw(user)
            self.data_in("bot_data_in %s@%s: %s" % (user, channel, msg))
Esempio n. 13
0
    def privmsg(self, user, channel, msg):
        """
        Called when the connected channel receives a message.

        Args:
            user (str): User name sending the message.
            channel (str): Channel name seeing the message.
            msg (str): The message arriving from channel.

        """
        if not msg.startswith('***'):
            user = user.split('!', 1)[0]
            user = ansi.raw(user)
            self.data_in("%s@%s: %s" % (user, channel, msg))
Esempio n. 14
0
    def privmsg(self, user, channel, msg):
        """
        Called when the connected channel receives a message.

        Args:
            user (str): User name sending the message.
            channel (str): Channel name seeing the message.
            msg (str): The message arriving from channel.

        """
        if channel == self.nickname:
            # private message
            user = user.split('!', 1)[0]
            self.data_in(text=msg, type="privmsg", user=user, channel=channel)
        elif not msg.startswith('***'):
            # channel message
            user = user.split('!', 1)[0]
            user = ansi.raw(user)
            self.data_in(text=msg, type="msg", user=user, channel=channel)
Esempio n. 15
0
    def privmsg(self, user, channel, msg):
        """
        Called when the connected channel receives a message.

        Args:
            user (str): User name sending the message.
            channel (str): Channel name seeing the message.
            msg (str): The message arriving from channel.

        """
        if channel == self.nickname:
            # private message
            user = user.split("!", 1)[0]
            self.data_in(text=msg, type="privmsg", user=user, channel=channel)
        elif not msg.startswith("***"):
            # channel message
            user = user.split("!", 1)[0]
            user = ansi.raw(user)
            self.data_in(text=msg, type="msg", user=user, channel=channel)
Esempio n. 16
0
    def privmsg(self, user, channel, msg):
        """
        Called when the connected channel receives a message.
        Also called when this Bot recieves a personal message.

        Args:
            user (str): User name sending the message.
            channel (str): Channel name seeing the message. Or this Bots name
                           if recieving a personal message.
            msg (str): The message arriving from channel.
        """
        # Respond to private messages with a little bit of information.
        if channel == self.nickname:
            user = user.split('!', 1)[0]
            pm_response = ("This is an Evennia IRC bot connecting from "
                           "'%s'." % settings.SERVERNAME)
            self.send_privmsg(pm_response, user=user)

        # We pass regular channel messages to our Account Bot.
        elif not msg.startswith('***'):
            user = user.split('!', 1)[0]
            user = ansi.raw(user)
            self.data_in(text=msg, type="msg", user=user, channel=channel)
Esempio n. 17
0
    def add_callback(self):
        """Add a callback."""
        obj = self.obj
        callback_name = self.callback_name
        types = self.handler.get_events(obj)

        # Check that the callback exists
        if not callback_name.startswith(
                "chain_") and not callback_name in types:
            self.msg("The callback name {} can't be found in {} of " \
                    "typeclass {}.".format(callback_name, obj, type(obj)))
            return

        definition = types.get(callback_name, (None, "Chained event."))
        description = definition[1]
        self.msg(raw(description.strip("\n")))

        # Open the editor
        callback = self.handler.add_callback(obj,
                                             callback_name,
                                             "",
                                             self.caller,
                                             False,
                                             parameters=self.parameters)

        # Lock this callback right away
        self.handler.db.locked.append((obj, callback_name, callback["number"]))

        # Open the editor for this callback
        self.caller.db._callback = callback
        EvEditor(self.caller,
                 loadfunc=_ev_load,
                 savefunc=_ev_save,
                 quitfunc=_ev_quit,
                 key="Callback {} of {}".format(callback_name, obj),
                 persistent=True,
                 codefunc=_ev_save)
Esempio n. 18
0
    def list_callbacks(self):
        """Display the list of callbacks connected to the object."""
        obj = self.obj
        callback_name = self.callback_name
        parameters = self.parameters
        callbacks = self.handler.get_callbacks(obj)
        types = self.handler.get_events(obj)

        if callback_name:
            # Check that the callback name can be found in this object
            created = callbacks.get(callback_name)
            if created is None:
                self.msg("No callback {} has been set on {}.".format(
                    callback_name, obj))
                return

            if parameters:
                # Check that the parameter points to an existing callback
                try:
                    number = int(parameters) - 1
                    assert number >= 0
                    callback = callbacks[callback_name][number]
                except (ValueError, AssertionError, IndexError):
                    self.msg(
                        "The callback {} {} cannot be found in {}.".format(
                            callback_name, parameters, obj))
                    return

                # Display the callback's details
                author = callback.get("author")
                author = author.key if author else "|gUnknown|n"
                updated_by = callback.get("updated_by")
                updated_by = updated_by.key if updated_by else "|gUnknown|n"
                created_on = callback.get("created_on")
                created_on = created_on.strftime(
                    "%Y-%m-%d %H:%M:%S") if created_on else "|gUnknown|n"
                updated_on = callback.get("updated_on")
                updated_on = updated_on.strftime(
                    "%Y-%m-%d %H:%M:%S") if updated_on else "|gUnknown|n"
                msg = "Callback {} {} of {}:".format(callback_name, parameters,
                                                     obj)
                msg += "\nCreated by {} on {}.".format(author, created_on)
                msg += "\nUpdated by {} on {}".format(updated_by, updated_on)

                if self.is_validator:
                    if callback.get("valid"):
                        msg += "\nThis callback is |rconnected|n and active."
                    else:
                        msg += "\nThis callback |rhasn't been|n accepted yet."

                msg += "\nCallback code:\n"
                msg += raw(callback["code"])
                self.msg(msg)
                return

            # No parameter has been specified, display the table of callbacks
            cols = ["Number", "Author", "Updated", "Param"]
            if self.is_validator:
                cols.append("Valid")

            table = EvTable(*cols, width=78)
            table.reformat_column(0, align="r")
            now = datetime.now()
            for i, callback in enumerate(created):
                author = callback.get("author")
                author = author.key if author else "|gUnknown|n"
                updated_on = callback.get("updated_on")
                if updated_on is None:
                    updated_on = callback.get("created_on")

                if updated_on:
                    updated_on = "{} ago".format(
                        time_format((now - updated_on).total_seconds(),
                                    4).capitalize())
                else:
                    updated_on = "|gUnknown|n"
                parameters = callback.get("parameters", "")

                row = [str(i + 1), author, updated_on, parameters]
                if self.is_validator:
                    row.append("Yes" if callback.get("valid") else "No")
                table.add_row(*row)

            self.msg(unicode(table))
        else:
            names = list(set(list(types.keys()) + list(callbacks.keys())))
            table = EvTable("Callback name",
                            "Number",
                            "Description",
                            valign="t",
                            width=78)
            table.reformat_column(0, width=20)
            table.reformat_column(1, width=10, align="r")
            table.reformat_column(2, width=48)
            for name in sorted(names):
                number = len(callbacks.get(name, []))
                lines = sum(
                    len(e["code"].splitlines())
                    for e in callbacks.get(name, []))
                no = "{} ({})".format(number, lines)
                description = types.get(name, (None, "Chained event."))[1]
                description = description.strip("\n").splitlines()[0]
                table.add_row(name, no, description)

            self.msg(unicode(table))
Esempio n. 19
0
    def list_callbacks(self):
        """Display the list of callbacks connected to the object."""
        obj = self.obj
        callback_name = self.callback_name
        parameters = self.parameters
        callbacks = self.handler.get_callbacks(obj)
        types = self.handler.get_events(obj)

        if callback_name:
            # Check that the callback name can be found in this object
            created = callbacks.get(callback_name)
            if created is None:
                self.msg("No callback {} has been set on {}.".format(callback_name,
                                                                     obj))
                return

            if parameters:
                # Check that the parameter points to an existing callback
                try:
                    number = int(parameters) - 1
                    assert number >= 0
                    callback = callbacks[callback_name][number]
                except (ValueError, AssertionError, IndexError):
                    self.msg("The callback {} {} cannot be found in {}.".format(
                        callback_name, parameters, obj))
                    return

                # Display the callback's details
                author = callback.get("author")
                author = author.key if author else "|gUnknown|n"
                updated_by = callback.get("updated_by")
                updated_by = updated_by.key if updated_by else "|gUnknown|n"
                created_on = callback.get("created_on")
                created_on = created_on.strftime("%Y-%m-%d %H:%M:%S") if created_on else "|gUnknown|n"
                updated_on = callback.get("updated_on")
                updated_on = updated_on.strftime("%Y-%m-%d %H:%M:%S") if updated_on else "|gUnknown|n"
                msg = "Callback {} {} of {}:".format(callback_name, parameters, obj)
                msg += "\nCreated by {} on {}.".format(author, created_on)
                msg += "\nUpdated by {} on {}".format(updated_by, updated_on)

                if self.is_validator:
                    if callback.get("valid"):
                        msg += "\nThis callback is |rconnected|n and active."
                    else:
                        msg += "\nThis callback |rhasn't been|n accepted yet."

                msg += "\nCallback code:\n"
                msg += raw(callback["code"])
                self.msg(msg)
                return

            # No parameter has been specified, display the table of callbacks
            cols = ["Number", "Author", "Updated", "Param"]
            if self.is_validator:
                cols.append("Valid")

            table = EvTable(*cols, width=78)
            table.reformat_column(0, align="r")
            now = datetime.now()
            for i, callback in enumerate(created):
                author = callback.get("author")
                author = author.key if author else "|gUnknown|n"
                updated_on = callback.get("updated_on")
                if updated_on is None:
                    updated_on = callback.get("created_on")

                if updated_on:
                    updated_on = "{} ago".format(time_format(
                        (now - updated_on).total_seconds(),
                        4).capitalize())
                else:
                    updated_on = "|gUnknown|n"
                parameters = callback.get("parameters", "")

                row = [str(i + 1), author, updated_on, parameters]
                if self.is_validator:
                    row.append("Yes" if callback.get("valid") else "No")
                table.add_row(*row)

            self.msg(unicode(table))
        else:
            names = list(set(list(types.keys()) + list(callbacks.keys())))
            table = EvTable("Callback name", "Number", "Description",
                            valign="t", width=78)
            table.reformat_column(0, width=20)
            table.reformat_column(1, width=10, align="r")
            table.reformat_column(2, width=48)
            for name in sorted(names):
                number = len(callbacks.get(name, []))
                lines = sum(len(e["code"].splitlines()) for e in callbacks.get(name, []))
                no = "{} ({})".format(number, lines)
                description = types.get(name, (None, "Chained event."))[1]
                description = description.strip("\n").splitlines()[0]
                table.add_row(name, no, description)

            self.msg(unicode(table))
Esempio n. 20
0
    def edit_callback(self):
        """Edit a callback."""
        obj = self.obj
        callback_name = self.callback_name
        parameters = self.parameters
        callbacks = self.handler.get_callbacks(obj)
        types = self.handler.get_events(obj)

        # If no callback name is specified, display the list of callbacks
        if not callback_name:
            self.list_callbacks()
            return

        # Check that the callback exists
        if callback_name not in callbacks:
            self.msg("The callback name {} can't be found in {}.".format(
                callback_name, obj))
            return

        # If there's only one callback, just edit it
        if len(callbacks[callback_name]) == 1:
            number = 0
            callback = callbacks[callback_name][0]
        else:
            if not parameters:
                self.msg("Which callback do you wish to edit?  Specify a number.")
                self.list_callbacks()
                return

            # Check that the parameter points to an existing callback
            try:
                number = int(parameters) - 1
                assert number >= 0
                callback = callbacks[callback_name][number]
            except (ValueError, AssertionError, IndexError):
                self.msg("The callback {} {} cannot be found in {}.".format(
                    callback_name, parameters, obj))
                return

        # If caller can't edit without validation, forbid editing
        # others' works
        if not self.autovalid and callback["author"] is not self.caller:
            self.msg("You cannot edit this callback created by someone else.")
            return

        # If the callback is locked (edited by someone else)
        if (obj, callback_name, number) in self.handler.db.locked:
            self.msg("This callback is locked, you cannot edit it.")
            return

        self.handler.db.locked.append((obj, callback_name, number))

        # Check the definition of the callback
        definition = types.get(callback_name, (None, "Chained event."))
        description = definition[1]
        self.msg(raw(description.strip("\n")))

        # Open the editor
        callback = dict(callback)
        self.caller.db._callback = callback
        EvEditor(self.caller, loadfunc=_ev_load, savefunc=_ev_save,
                 quitfunc=_ev_quit, key="Callback {} of {}".format(
                     callback_name, obj), persistent=True, codefunc=_ev_save)
Esempio n. 21
0
    def edit_callback(self):
        """Edit a callback."""
        obj = self.obj
        callback_name = self.callback_name
        parameters = self.parameters
        callbacks = self.handler.get_callbacks(obj)
        types = self.handler.get_events(obj)

        # If no callback name is specified, display the list of callbacks
        if not callback_name:
            self.list_callbacks()
            return

        # Check that the callback exists
        if not callback_name in callbacks:
            self.msg("The callback name {} can't be found in {}.".format(
                callback_name, obj))
            return

        # If there's only one callback, just edit it
        if len(callbacks[callback_name]) == 1:
            number = 0
            callback = callbacks[callback_name][0]
        else:
            if not parameters:
                self.msg(
                    "Which callback do you wish to edit?  Specify a number.")
                self.list_callbacks()
                return

            # Check that the parameter points to an existing callback
            try:
                number = int(parameters) - 1
                assert number >= 0
                callback = callbacks[callback_name][number]
            except (ValueError, AssertionError, IndexError):
                self.msg("The callback {} {} cannot be found in {}.".format(
                    callback_name, parameters, obj))
                return

        # If caller can't edit without validation, forbid editing
        # others' works
        if not self.autovalid and callback["author"] is not self.caller:
            self.msg("You cannot edit this callback created by someone else.")
            return

        # If the callback is locked (edited by someone else)
        if (obj, callback_name, number) in self.handler.db.locked:
            self.msg("This callback is locked, you cannot edit it.")
            return

        self.handler.db.locked.append((obj, callback_name, number))

        # Check the definition of the callback
        definition = types.get(callback_name, (None, "Chained event."))
        description = definition[1]
        self.msg(raw(description.strip("\n")))

        # Open the editor
        callback = dict(callback)
        self.caller.db._callback = callback
        EvEditor(self.caller,
                 loadfunc=_ev_load,
                 savefunc=_ev_save,
                 quitfunc=_ev_quit,
                 key="Callback {} of {}".format(callback_name, obj),
                 persistent=True,
                 codefunc=_ev_save)