X-Git-Url: https://mudpy.org/gitweb?p=mudpy.git;a=blobdiff_plain;f=mudpy%2Fmisc.py;h=3cd8e644c248e9a5fc8150b36ae5a0c41735615d;hp=2da1b409bad9659f60ccbfca799d22743d848877;hb=b054ba768010f5a9b17f16c06f87d11ea98465e1;hpb=46a46f095b503ba27f07521e9fef62a97ff1b078 diff --git a/mudpy/misc.py b/mudpy/misc.py index 2da1b40..3cd8e64 100644 --- a/mudpy/misc.py +++ b/mudpy/misc.py @@ -1,6 +1,6 @@ """Miscellaneous functions for the mudpy engine.""" -# Copyright (c) 2004-2017 Jeremy Stanley . Permission +# Copyright (c) 2004-2018 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. @@ -129,16 +129,25 @@ class Element: # updated data from files raise PermissionError("Altering elements in read-only files is " "disallowed") + # Coerce some values to appropriate data types + # TODO(fungi) Move these to a separate validation mechanism if facet in ["loglevel"]: value = int(value) elif facet in ["administrator"]: value = bool(value) - if not self.has_facet(facet) or not self.get(facet) == value: - node = ".".join((self.key, facet)) + + # The canonical node for this facet within its origin + node = ".".join((self.key, facet)) + + if node not in self.origin.data or self.origin.data[node] != value: + # Be careful to only update the origin's contents when required, + # since that affects whether the backing file gets written self.origin.data[node] = value - self.facethash[facet] = self.origin.data[node] self.origin.modified = True + # Make sure this facet is included in the element's facets + self.facethash[facet] = self.origin.data[node] + def append(self, facet, value): """Append value to a list.""" newlist = self.get(facet) @@ -326,6 +335,7 @@ class Universe: self.startdir = os.getcwd() self.terminate_flag = False self.userlist = [] + self.versions = None if not filename: possible_filenames = [ "etc/mudpy.yaml", @@ -515,7 +525,7 @@ class User: self.output_queue = [] self.partial_input = b"" self.password_tries = 0 - self.state = "initial" + self.state = "telopt_negotiation" self.telopts = {} def quit(self): @@ -796,7 +806,7 @@ class User: self.check_idle() # if output is paused, decrement the counter - if self.state == "initial": + if self.state == "telopt_negotiation": if self.negotiation_pause: self.negotiation_pause -= 1 else: @@ -825,13 +835,13 @@ class User: if self.output_queue: try: self.connection.send(self.output_queue[0]) - except BrokenPipeError: + except (BrokenPipeError, ConnectionResetError): if self.account and self.account.get("name"): account = self.account.get("name") else: account = "an unknown user" self.state = "disconnecting" - log("Broken pipe sending to %s." % account, 7) + log("Disconnected while sending to %s." % account, 7) del self.output_queue[0] def enqueue_input(self): @@ -873,8 +883,8 @@ class User: line = line.strip() # log non-printable characters remaining - if mudpy.telnet.is_enabled(self, mudpy.telnet.TELOPT_BINARY, - mudpy.telnet.HIM): + if not mudpy.telnet.is_enabled( + self, mudpy.telnet.TELOPT_BINARY, mudpy.telnet.HIM): asciiline = bytes([x for x in line if 32 <= x <= 126]) if line != asciiline: logline = "Non-ASCII characters from " @@ -889,7 +899,7 @@ class User: try: line = line.decode("utf-8") except UnicodeDecodeError: - logline = "Non-UTF-8 characters from " + logline = "Non-UTF-8 sequence from " if self.account and self.account.get("name"): logline += self.account.get("name") + ": " else: @@ -1106,8 +1116,10 @@ def wrap_ansi_text(text, width): # ignoring color escape sequences rel_pos = 0 - # the absolute position of the most recent whitespace character - last_whitespace = 0 + # the absolute and relative positions of the most recent whitespace + # character + last_abs_whitespace = 0 + last_rel_whitespace = 0 # whether the current character is part of a color escape sequence escape = False @@ -1121,39 +1133,37 @@ def wrap_ansi_text(text, width): # the current character is the escape character if each_character == "\x1b" and not escape: escape = True + rel_pos -= 1 # the current character is within an escape sequence elif escape: - - # the current character is m, which terminates the - # escape sequence + rel_pos -= 1 if each_character == "m": + # the current character is m, which terminates the + # escape sequence escape = False + # the current character is a space + elif each_character == " ": + last_abs_whitespace = abs_pos + last_rel_whitespace = rel_pos + # the current character is a newline, so reset the relative - # position (start a new line) + # position too (start a new line) elif each_character == "\n": rel_pos = 0 - last_whitespace = abs_pos - - # the current character meets the requested maximum line width, - # so we need to backtrack and find a space at which to wrap; - # special care is taken to avoid an off-by-one in case the - # current character is a double-width glyph - elif each_character != "\r" and ( - rel_pos >= width or ( - rel_pos >= width - 1 and glyph_columns( - each_character - ) == 2 - ) - ): + last_abs_whitespace = abs_pos + last_rel_whitespace = rel_pos - # it's always possible we landed on whitespace - if unicodedata.category(each_character) in ("Cc", "Zs"): - last_whitespace = abs_pos + # the current character meets the requested maximum line width, so we + # need to wrap unless the current word is wider than the terminal (in + # which case we let it do the wrapping instead) + if last_rel_whitespace != 0 and (rel_pos > width or ( + rel_pos > width - 1 and glyph_columns(each_character) == 2)): - # insert an eol in place of the space - text = text[:last_whitespace] + "\r\n" + text[last_whitespace + 1:] + # insert an eol in place of the last space + text = (text[:last_abs_whitespace] + "\r\n" + + text[last_abs_whitespace + 1:]) # increase the absolute position because an eol is two # characters but the space it replaced was only one @@ -1161,17 +1171,17 @@ def wrap_ansi_text(text, width): # now we're at the begining of a new line, plus the # number of characters wrapped from the previous line - rel_pos = 0 - for remaining_characters in text[last_whitespace:abs_pos]: - rel_pos += glyph_columns(remaining_characters) + rel_pos -= last_rel_whitespace + last_rel_whitespace = 0 # as long as the character is not a carriage return and the # other above conditions haven't been met, count it as a # printable character elif each_character != "\r": rel_pos += glyph_columns(each_character) - if unicodedata.category(each_character) in ("Cc", "Zs"): - last_whitespace = abs_pos + if each_character in (" ", "\n"): + last_abs_whitespace = abs_pos + last_rel_whitespace = rel_pos # increase the absolute position for every character abs_pos += 1 @@ -2093,6 +2103,8 @@ def command_show(actor, parameters): arguments = parameters.split() if not parameters: message = "What do you want to show?" + elif arguments[0] == "version": + message = repr(universe.versions) elif arguments[0] == "time": message = universe.groups["internal"]["counters"].get( "elapsed" @@ -2472,9 +2484,6 @@ def setup(): log(*logline) universe.setup_loglines = [] - # log an initial message - log("Started mudpy with command line: " + " ".join(sys.argv)) - # fork and disassociate daemonize(universe) @@ -2487,6 +2496,17 @@ def setup(): # make the pidfile create_pidfile(universe) + # load and store diagnostic info + universe.versions = mudpy.version.Versions("mudpy") + + # log startup diagnostic messages + log("On %s at %s" % (universe.versions.python_version, sys.executable), 1) + log("Import path: %s" % ", ".join(sys.path), 1) + log("Installed dependencies: %s" % universe.versions.dependencies_text, 1) + log("Other python packages: %s" % universe.versions.environment_text, 1) + log("Started %s with command line: %s" % ( + universe.versions.version, " ".join(sys.argv)), 1) + # pass the initialized universe back return universe