From d92cf2f5ca5d8c4c0cd57d2a8bd0920d6ad22382 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Wed, 16 Oct 2013 01:26:22 +0000 Subject: [PATCH 01/16] Recast to unicode when normalizing on Python 2.x * lib/mudpy/misc.py: Explicitly recast text to unicode type when passing to unicodedata.normalize on Python 2.x. --- lib/mudpy/misc.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index b39610b..d8c863a 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -1202,7 +1202,11 @@ def wrap_ansi_text(text, width): escape = False # normalize any potentially composited unicode before we count it - text = unicodedata.normalize("NFKC", text) + # TODO: remove this check after the switch to py3k + try: + text = unicodedata.normalize("NFKC", text) + except TypeError: + text = unicodedata.normalize("NFKC", unicode(text)) # iterate over each character from the begining of the text for each_character in text: -- 2.11.0 From 86ca67c3421f3ea64f60f67f4fd43acb662b2cf7 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Fri, 22 Nov 2013 10:27:38 +0000 Subject: [PATCH 02/16] Fix an incorrect TODO comment line --- lib/mudpy/password.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mudpy/password.py b/lib/mudpy/password.py index 68e1a5f..bad5579 100644 --- a/lib/mudpy/password.py +++ b/lib/mudpy/password.py @@ -162,7 +162,7 @@ def create( # number of times for i in range(2 ** rounds): hashed = algorithms[algorithm](hashed.encode("utf-8")).digest() - # TODO: remove this exception trap after the switch to py2k + # TODO: remove this check after the switch to py3k try: hashed = "".join(format(x, "02x") for x in bytes(hashed)) except ValueError: -- 2.11.0 From 2a1119763de7805f617d8b62b8f0822969f3d7df Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Sat, 7 Dec 2013 23:19:08 +0000 Subject: [PATCH 03/16] Clean up imports Group all imports at the tops of files and alphabetize, for ease of maintainabiity. --- bin/git2gch | 6 ++--- bin/mudpy | 5 ++-- bin/test | 3 ++- lib/mudpy/__init__.py | 6 +++-- lib/mudpy/data.py | 30 +++++++++++------------ lib/mudpy/misc.py | 66 +++++++++++++-------------------------------------- lib/mudpy/password.py | 14 +++++------ lib/mudpy/telnet.py | 3 ++- 8 files changed, 51 insertions(+), 82 deletions(-) diff --git a/bin/git2gch b/bin/git2gch index 40689a9..ef16232 100755 --- a/bin/git2gch +++ b/bin/git2gch @@ -2,14 +2,14 @@ # -*- coding: utf-8 -*- """Generates potential ChangeLog file contents from commit messages.""" -# Copyright (c) 2010-2012 Jeremy Stanley . Permission +# Copyright (c) 2010-2013 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. -# needs GitPython: http://gitorious.org/git-python -import git import time +import git + copyright = """\ Copyright (c) 2004-2012 Jeremy Stanley . Permission to use, copy, modify, and distribute this software is granted under terms diff --git a/bin/mudpy b/bin/mudpy index e946462..259c9c1 100755 --- a/bin/mudpy +++ b/bin/mudpy @@ -7,10 +7,11 @@ # terms provided in the LICENSE file distributed with this software. # core objects for the mudpy engine -import os.path +import imp +import os import sys sys.path.append( os.path.realpath("lib") ) -import imp + import mudpy # start it up diff --git a/bin/test b/bin/test index 45ce5aa..0ec3544 100755 --- a/bin/test +++ b/bin/test @@ -6,6 +6,8 @@ # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. +import telnetlib + conversation = ( ("Identify yourself:", "testuser"), ("Enter your choice:", "n"), @@ -24,7 +26,6 @@ conversation = ( ("Disconnecting...", ""), ) -import telnetlib mud = telnetlib.Telnet() mud.open("::1", 6669) for question, answer in conversation: diff --git a/lib/mudpy/__init__.py b/lib/mudpy/__init__.py index 4224b2f..04001a0 100644 --- a/lib/mudpy/__init__.py +++ b/lib/mudpy/__init__.py @@ -5,12 +5,14 @@ # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. +import imp + +import mudpy + def load(): """Import/reload some modules (be careful, as this can result in loops).""" - import imp - # pick up the modules list from this package global modules diff --git a/lib/mudpy/data.py b/lib/mudpy/data.py index de76416..7d030c2 100644 --- a/lib/mudpy/data.py +++ b/lib/mudpy/data.py @@ -5,6 +5,20 @@ # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. +import codecs +import os +import re +import stat +import sys + +# TODO: remove this check after the switch to py3k +try: + import configparser +except ImportError: + import ConfigParser as configparser + +import mudpy + class DataFile: @@ -17,14 +31,6 @@ class DataFile: def load(self): """Read a file and create elements accordingly.""" - import mudpy.misc - import os - import os.path - # TODO: remove this check after the switch to py3k - try: - import configparser - except ImportError: - import ConfigParser as configparser self.data = configparser.RawConfigParser() self.modified = False if os.access(self.filename, os.R_OK): @@ -101,11 +107,6 @@ class DataFile: def save(self): """Write the data, if necessary.""" - import codecs - import os - import os.path - import re - import stat # when modified, writeable and has content or the file exists if self.modified and self.is_writeable() and ( @@ -197,9 +198,6 @@ def find_file( universe=None ): """Return an absolute file path based on configuration.""" - import os - import os.path - import sys # make sure to get rid of any surrounding quotes first thing if file_name: diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index d8c863a..2edf966 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -5,6 +5,22 @@ # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. +import codecs +import ctypes +import ctypes.util +import os +import random +import re +import signal +import socket +import sys +import syslog +import time +import traceback +import unicodedata + +import mudpy + class Element: @@ -12,8 +28,6 @@ class Element: def __init__(self, key, universe, filename=None): """Set up a new element.""" - import mudpy.data - import os.path # keep track of our key name self.key = key @@ -169,7 +183,6 @@ class Element: def getlist(self, facet, default=None): """Return values as list type.""" - import mudpy.data if default is None: default = [] value = self.get(facet) @@ -180,7 +193,6 @@ class Element: def getdict(self, facet, default=None): """Return values as dict type.""" - import mudpy.data if default is None: default = {} value = self.get(facet) @@ -370,7 +382,6 @@ class Element: def portals(self): """Map the portal directions for a room to neighbors.""" - import re portals = {} if re.match("""^location:-?\d+,-?\d+,-?\d+$""", self.key): coordinates = [(int(x)) @@ -420,8 +431,6 @@ class Universe: def __init__(self, filename="", load=False): """Initialize the universe.""" - import os - import os.path self.categories = {} self.contents = {} self.default_origins = {} @@ -456,7 +465,6 @@ class Universe: def load(self): """Load universe data from persistent storage.""" - import mudpy.data # the files dict must exist and filename needs to be read-only if not hasattr( @@ -518,7 +526,6 @@ class Universe: def initialize_server_socket(self): """Create and open the listening socket.""" - import socket # need to know the local address and port number for the listener host = self.categories["internal"]["network"].get("host") @@ -574,7 +581,6 @@ class User: def __init__(self): """Default values for the in-memory user variables.""" - import mudpy.telnet self.account = None self.address = "" self.authenticated = False @@ -754,7 +760,6 @@ class User: def adjust_echoing(self): """Adjust echoing to match state menu requirements.""" - import mudpy.telnet if mudpy.telnet.is_enabled(self, mudpy.telnet.TELOPT_ECHO, mudpy.telnet.US): if menu_echo_on(self.state): @@ -780,7 +785,6 @@ class User: prepend_padding=True ): """Send arbitrary text to a connected user.""" - import mudpy.telnet # unless raw mode is on, clean it up all nice and pretty if not raw: @@ -917,8 +921,6 @@ class User: def enqueue_input(self): """Process and enqueue any new input.""" - import mudpy.telnet - import unicodedata # check for some input try: @@ -1059,10 +1061,6 @@ def broadcast(message, add_prompt=True): def log(message, level=0): """Log a message.""" - import codecs - import os.path - import syslog - import time # a couple references we need file_name = universe.categories["internal"]["logging"].get("file") @@ -1176,7 +1174,6 @@ def get_loglines(level, start, stop): def glyph_columns(character): """Convenience function to return the column width of a glyph.""" - import unicodedata if unicodedata.east_asian_width(character) in "FW": return 2 else: @@ -1185,7 +1182,6 @@ def glyph_columns(character): def wrap_ansi_text(text, width): """Wrap text with arbitrary width while ignoring ANSI colors.""" - import unicodedata # the current position in the entire text string, including all # characters, printable or otherwise @@ -1276,7 +1272,6 @@ def wrap_ansi_text(text, width): def weighted_choice(data): """Takes a dict weighted by value and returns a random key.""" - import random # this will hold our expanded list of keys from the data expanded = [] @@ -1292,7 +1287,6 @@ def weighted_choice(data): def random_name(): """Returns a random character name.""" - import random # the vowels and consonants needed to create romaji syllables vowels = [ @@ -1344,9 +1338,6 @@ def random_name(): def replace_macros(user, text, is_input=False): """Replaces macros in text output.""" - import codecs - import mudpy.data - import os.path # third person pronouns pronouns = { @@ -1443,7 +1434,6 @@ def first_word(text, separator=" "): def on_pulse(): """The things which should happen on each pulse, aside from reloads.""" - import time # open the listening socket if it hasn't been already if not hasattr(universe, "listening_socket"): @@ -1518,7 +1508,6 @@ def reload_data(): def check_for_connection(listening_socket): """Check for a waiting connection and return a new user object.""" - import mudpy.telnet # try to accept a new connection try: @@ -1735,7 +1724,6 @@ def get_choice_action(user, choice): def handle_user_input(user): """The main handler, branches to a state-specific handler.""" - import mudpy.telnet # if the user's client echo is off, send a blank line for aesthetics if mudpy.telnet.is_enabled(user, mudpy.telnet.TELOPT_ECHO, @@ -1813,7 +1801,6 @@ def handler_entering_account_name(user): def handler_checking_password(user): """Handle the login account password.""" - import mudpy.password # get the next waiting line of input input_data = user.input_queue.pop(0) @@ -1847,7 +1834,6 @@ def handler_checking_password(user): def handler_entering_new_password(user): """Handle a new password entry.""" - import mudpy.password # get the next waiting line of input input_data = user.input_queue.pop(0) @@ -1888,7 +1874,6 @@ def handler_entering_new_password(user): def handler_verifying_new_password(user): """Handle the re-entered new password for verification.""" - import mudpy.password # get the next waiting line of input input_data = user.input_queue.pop(0) @@ -2104,7 +2089,6 @@ def command_look(actor, parameters): def command_say(actor, parameters): """Speak to others in the same room.""" - import unicodedata # check for replacement macros and escape them parameters = escape_macros(parameters) @@ -2195,7 +2179,6 @@ def command_chat(actor): def command_show(actor, parameters): """Show program data.""" - import re message = "" arguments = parameters.split() if not parameters: @@ -2419,7 +2402,6 @@ def command_delete(actor, parameters): def command_error(actor, input_data): """Generic error for an unrecognized command word.""" - import random # 90% of the time use a generic error if random.randrange(10): @@ -2435,12 +2417,6 @@ def command_error(actor, input_data): def daemonize(universe): """Fork and disassociate from everything.""" - import codecs - import ctypes - import ctypes.util - import os - import os.path - import sys # only if this is what we're configured to do if universe.contents["internal:process"].getboolean("daemon"): @@ -2520,9 +2496,6 @@ def daemonize(universe): def create_pidfile(universe): """Write a file containing the current process ID.""" - import codecs - import os - import os.path pid = str(os.getpid()) log("Process ID: " + pid) file_name = universe.contents["internal:process"].get("pidfile") @@ -2537,8 +2510,6 @@ def create_pidfile(universe): def remove_pidfile(universe): """Remove the file containing the current process ID.""" - import os - import os.path file_name = universe.contents["internal:process"].get("pidfile") if file_name: if not os.path.isabs(file_name): @@ -2549,7 +2520,6 @@ def remove_pidfile(universe): def excepthook(excepttype, value, tracebackdata): """Handle uncaught exceptions.""" - import traceback # assemble the list of errors into a single string message = "".join( @@ -2571,7 +2541,6 @@ def excepthook(excepttype, value, tracebackdata): def sighook(what, where): """Handle external signals.""" - import signal # a generic message message = "Caught signal: " @@ -2596,20 +2565,17 @@ def sighook(what, where): def override_excepthook(): """Redefine sys.excepthook with our own.""" - import sys sys.excepthook = excepthook def assign_sighook(): """Assign a customized handler for some signals.""" - import signal signal.signal(signal.SIGHUP, sighook) signal.signal(signal.SIGTERM, sighook) def setup(): """This contains functions to be performed when starting the engine.""" - import sys # see if a configuration file was specified if len(sys.argv) > 1: diff --git a/lib/mudpy/password.py b/lib/mudpy/password.py index bad5579..b6b4be5 100644 --- a/lib/mudpy/password.py +++ b/lib/mudpy/password.py @@ -5,6 +5,13 @@ # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. +import base64 +import hashlib +import math +import random +import re +import struct + # convenience constants for indexing the supported hashing algorithms, # guaranteed a stable part of the interface MD5 = 0 # hashlib.md5 @@ -20,7 +27,6 @@ def _pack_bytes(numbers): This is a wrapper around struct.pack, used to turn a list of integers between 0 and 255 into a packed sequence akin to a C-style string. """ - import struct packed = b"" for number in numbers: number = int(number) @@ -34,7 +40,6 @@ def _bytes_to_text(byte_sequence): This is a wrapper around base64.b64encode with preferences appropriate for encoding Unix-style passwd hash strings. """ - import base64 return base64.b64encode( byte_sequence, b"./" @@ -50,8 +55,6 @@ def _generate_salt(salt_len=2): need and discard any excess characters over the specified length. This ensures full distribution over each character of the salt. """ - import math - import random salt = [] for i in range(int(math.ceil(salt_len * 0.75))): salt.append(random.randint(0, 255)) @@ -66,7 +69,6 @@ def upgrade_legacy_hash(legacy_hash, salt, sep="$"): facets to this function, a conforming new-style password hash will be returned. """ - import re assert re.match("^[0-9a-f]{32}$", legacy_hash), "Not a valid MD5 hexdigest" collapsed = b"" @@ -126,7 +128,6 @@ def create( create(password, algorithm=SHA256, rounds=12, salt_len=16) """ - import hashlib # if a specific salt wasn't specified, we need to generate one if not salt: @@ -183,7 +184,6 @@ def verify(password, encoded_hash): comes out the same as the encoded_hash. """ sep = encoded_hash[0] - import mudpy.misc algorithm, rounds, salt, hashed = encoded_hash.split(sep)[1:] if encoded_hash == create( password=password, diff --git a/lib/mudpy/telnet.py b/lib/mudpy/telnet.py index 7765571..471f9fd 100644 --- a/lib/mudpy/telnet.py +++ b/lib/mudpy/telnet.py @@ -5,6 +5,8 @@ # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. +import mudpy + # telnet options (from bsd's arpa/telnet.h since telnetlib's are ambiguous) TELOPT_BINARY = 0 # transmit 8-bit data by the receiver (rfc 856) TELOPT_ECHO = 1 # echo received data back to the sender (rfc 857) @@ -105,7 +107,6 @@ def disable(user, telopt, party): def negotiate_telnet_options(user): """Reply to and remove telnet negotiation options from partial_input.""" - import mudpy.misc # make a local copy to play with text = user.partial_input -- 2.11.0 From e0a20398d5cae3ea927132fb29a1bf76d0bb239a Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Mon, 20 Jan 2014 15:23:11 +0000 Subject: [PATCH 04/16] Drop support for Python 2.x Remove all derecated conditionals and fallbacks which provided workarounds for supporting Python 2.x versions. --- lib/mudpy/data.py | 9 ++------- lib/mudpy/misc.py | 11 ++--------- lib/mudpy/password.py | 8 ++------ 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/lib/mudpy/data.py b/lib/mudpy/data.py index 7d030c2..ad171d8 100644 --- a/lib/mudpy/data.py +++ b/lib/mudpy/data.py @@ -1,22 +1,17 @@ # -*- coding: utf-8 -*- """Data interface functions for the mudpy engine.""" -# Copyright (c) 2004-2013 Jeremy Stanley . Permission +# Copyright (c) 2004-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. import codecs +import configparser import os import re import stat import sys -# TODO: remove this check after the switch to py3k -try: - import configparser -except ImportError: - import ConfigParser as configparser - import mudpy diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index 2edf966..059e1be 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Miscellaneous functions for the mudpy engine.""" -# Copyright (c) 2004-2013 Jeremy Stanley . Permission +# Copyright (c) 2004-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. @@ -204,9 +204,6 @@ class Element: def set(self, facet, value): """Set values.""" if not self.has_facet(facet) or not self.get(facet) == value: - # TODO: remove this check after the switch to py3k - if repr(type(value)) == "": - value = str(value) if not type(value) is str: value = repr(value) self.origin.data.set(self.key, facet, value) @@ -1198,11 +1195,7 @@ def wrap_ansi_text(text, width): escape = False # normalize any potentially composited unicode before we count it - # TODO: remove this check after the switch to py3k - try: - text = unicodedata.normalize("NFKC", text) - except TypeError: - text = unicodedata.normalize("NFKC", unicode(text)) + text = unicodedata.normalize("NFKC", text) # iterate over each character from the begining of the text for each_character in text: diff --git a/lib/mudpy/password.py b/lib/mudpy/password.py index b6b4be5..f6c1abc 100644 --- a/lib/mudpy/password.py +++ b/lib/mudpy/password.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Password hashing functions and constants for the mudpy engine.""" -# Copyright (c) 2004-2013 Jeremy Stanley . Permission +# Copyright (c) 2004-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. @@ -163,11 +163,7 @@ def create( # number of times for i in range(2 ** rounds): hashed = algorithms[algorithm](hashed.encode("utf-8")).digest() - # TODO: remove this check after the switch to py3k - try: - hashed = "".join(format(x, "02x") for x in bytes(hashed)) - except ValueError: - hashed = "".join(format(ord(x), "02x") for x in bytes(hashed)) + hashed = "".join(format(x, "02x") for x in bytes(hashed)) # concatenate the output fields, coercing into text form as needed return "%s%s%s%s%s%s%s%s" % ( -- 2.11.0 From bf83f4661332fe3a54e41b7b9b0e18124bb97a9b Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Tue, 28 Jan 2014 16:35:02 +0000 Subject: [PATCH 05/16] Correct copyright date for changelog --- bin/git2gch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/git2gch b/bin/git2gch index ef16232..a8fd1a9 100755 --- a/bin/git2gch +++ b/bin/git2gch @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """Generates potential ChangeLog file contents from commit messages.""" -# Copyright (c) 2010-2013 Jeremy Stanley . Permission +# Copyright (c) 2010-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. @@ -11,7 +11,7 @@ import time import git copyright = """\ -Copyright (c) 2004-2012 Jeremy Stanley . Permission to +Copyright (c) 2004-2014 Jeremy Stanley . Permission to use, copy, modify, and distribute this software is granted under terms provided in the LICENSE file distributed with this software. -- 2.11.0 From f9c4c6c9f4c7f81d4d58fba30aa1db22feb65a77 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Tue, 4 Feb 2014 19:15:00 +0000 Subject: [PATCH 06/16] Remove unused event code * lib/mudpy/misc.py: Event elements weren't fully implemented, so rip out the dead code in preparation for a different implementation. --- lib/mudpy/misc.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index 059e1be..a463653 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -217,16 +217,6 @@ class Element: newlist.append(value) self.set(facet, newlist) - def new_event(self, action, when=None): - """Create, attach and enqueue an event element.""" - - # if when isn't specified, that means now - if not when: - when = self.universe.get_time() - - # events are elements themselves - event = Element("event:" + self.key + ":" + counter) - def send( self, message, @@ -432,8 +422,6 @@ class Universe: self.contents = {} self.default_origins = {} self.loglines = [] - self.pending_events_long = {} - self.pending_events_short = {} self.private_files = [] self.reload_flag = False self.startdir = os.getcwd() -- 2.11.0 From 015ea384dafbc17070d3c11e84004ca27b866eb9 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Mon, 10 Feb 2014 01:57:26 +0000 Subject: [PATCH 07/16] Fix reload to use a copy of datafile keys * lib/mudpy/misc.py: Python 2.x was more tolerant of this and it now breaks under 3.4 release candidates, but correcting it to iterate over a copy is arguably more correct anyway. --- lib/mudpy/misc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index a463653..d78eac6 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -85,7 +85,7 @@ class Element: def reload(self): """Create a new element and replace this one.""" - new_element = Element(self.key, self.universe, self.origin.filename) + Element(self.key, self.universe, self.origin.filename) del(self) def destroy(self): @@ -462,7 +462,7 @@ class Universe: # clear out all read-only files if hasattr(self, "files"): - for data_filename in self.files.keys(): + for data_filename in list(self.files.keys()): if not self.files[data_filename].is_writeable(): del self.files[data_filename] -- 2.11.0 From d339c9cd860a8381c4ae2770011c9a41bfd7b67b Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Sun, 23 Feb 2014 14:36:51 +0000 Subject: [PATCH 08/16] Clean up docstrings --- lib/mudpy/misc.py | 3 +-- lib/mudpy/password.py | 18 ++++++++++++------ lib/mudpy/telnet.py | 6 ++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index d78eac6..a8b0104 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -2588,8 +2588,7 @@ def setup(): def finish(): - """This contains functions to be performed when shutting down the - engine.""" + """These are functions performed when shutting down the engine.""" # the loop has terminated, so save persistent data universe.save() diff --git a/lib/mudpy/password.py b/lib/mudpy/password.py index f6c1abc..96a14d8 100644 --- a/lib/mudpy/password.py +++ b/lib/mudpy/password.py @@ -23,7 +23,8 @@ SHA512 = 5 # hashlib.sha512 def _pack_bytes(numbers): - """ + """Make a packed byte sequence: + This is a wrapper around struct.pack, used to turn a list of integers between 0 and 255 into a packed sequence akin to a C-style string. """ @@ -36,7 +37,8 @@ def _pack_bytes(numbers): def _bytes_to_text(byte_sequence): - """ + """Generate printable representation of 8-bit data: + This is a wrapper around base64.b64encode with preferences appropriate for encoding Unix-style passwd hash strings. """ @@ -47,7 +49,8 @@ def _bytes_to_text(byte_sequence): def _generate_salt(salt_len=2): - """ + """Generate salt for a password hash: + This simply generates a sequence of pseudo-random characters (with 6-bits of effective entropy per character). Since it relies on base64 encoding (which operates on 6-bit chunks of data), we only generate @@ -62,7 +65,8 @@ def _generate_salt(salt_len=2): def upgrade_legacy_hash(legacy_hash, salt, sep="$"): - """ + """Upgrade an older password hash: + This utility function is meant to provide a migration path for users of mudpy's legacy account-name-salted MD5 hexdigest password hashes. By passing the old passhash (as legacy_hash) and name (as salt) @@ -95,7 +99,8 @@ def create( salt_len=2, sep="$" ): - """ + """Generate a password hash: + The meat of the module, this function takes a provided password and generates a Unix-like passwd hash suitable for storage in portable, text-based data files. The password is prepended with a salt (which @@ -173,7 +178,8 @@ def create( def verify(password, encoded_hash): - """ + """Verify a password: + This simple function requires a text password and a mudpy-format password hash (as generated by the create function). It returns True if the password, hashed with the parameters from the encoded_hash, diff --git a/lib/mudpy/telnet.py b/lib/mudpy/telnet.py index 471f9fd..a4264ce 100644 --- a/lib/mudpy/telnet.py +++ b/lib/mudpy/telnet.py @@ -60,8 +60,7 @@ def send_command(user, *command): def is_enabled(user, telopt, party, state=YES): - """Returns True if the indicated Telnet option is enabled, False if - not.""" + """Indicates whether a specified Telnet option is enabled.""" if (telopt, party) in user.telopts and user.telopts[ (telopt, party) ] is state: @@ -88,8 +87,7 @@ def enable(user, telopt, party): def disable(user, telopt, party): - """Negotiates disabling a Telnet option for the indicated user's - socket.""" + """Negotiates disabling a Telnet option for the user's socket.""" if party is HIM: txneg = DONT else: -- 2.11.0 From bca875d67e9f4fad50bf47a6c9695687a6e95778 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Thu, 6 Mar 2014 02:32:27 +0000 Subject: [PATCH 09/16] Wrap long lines without using backslashes Try to align more closely with Python style guidelines on wrapping long lines. --- lib/mudpy/misc.py | 95 ++++++++++++++++++++++++++--------------------------- lib/mudpy/telnet.py | 6 ++-- 2 files changed, 49 insertions(+), 52 deletions(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index a8b0104..96957bc 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -623,8 +623,8 @@ class User: logline += self.account.get("name") else: logline += "an unknown user" - logline += " after idling too long in the " + \ - self.state + " state." + logline += (" after idling too long in the " + self.state + + " state.") log(logline, 2) self.state = "disconnecting" self.menu_seen = False @@ -788,8 +788,8 @@ class User: # with the optional eol string passed to this function # and the ansi escape to return to normal text if not just_prompt and prepend_padding: - if not self.output_queue \ - or not self.output_queue[-1].endswith(b"\r\n"): + if (not self.output_queue or not + self.output_queue[-1].endswith(b"\r\n")): output = "$(eol)" + output elif not self.output_queue[-1].endswith( b"\r\n\x1b[0m\r\n" @@ -1223,8 +1223,7 @@ def wrap_ansi_text(text, width): last_whitespace = abs_pos # insert an eol in place of the space - text = text[:last_whitespace] + \ - "\r\n" + text[last_whitespace + 1:] + text = text[:last_whitespace] + "\r\n" + text[last_whitespace + 1:] # increase the absolute position because an eol is two # characters but the space it replaced was only one @@ -2045,8 +2044,8 @@ def command_help(actor, parameters): else: output += " $(grn)" output += item + "$(nrm) - " + description + "$(eol)" - output += "$(eol)Enter \"help COMMAND\" for help on a command " \ - + "named \"COMMAND\"." + output += ("$(eol)Enter \"help COMMAND\" for help on a command " + "named \"COMMAND\".") # send the accumulated output to the user actor.send(output) @@ -2183,14 +2182,14 @@ def command_show(actor, parameters): status = "rw" else: status = "ro" - message += "$(eol) $(red)(" + status + ") $(grn)" + filename \ - + "$(nrm)" + message += ("$(eol) $(red)(" + status + ") $(grn)" + filename + + "$(nrm)") elif arguments[0] == "category": if len(arguments) != 2: message = "You must specify one category." elif arguments[1] in universe.categories: - message = "These are the elements in the \"" + arguments[1] \ - + "\" category:$(eol)" + message = ("These are the elements in the \"" + arguments[1] + + "\" category:$(eol)") elements = [ ( universe.categories[arguments[1]][x].key @@ -2205,8 +2204,8 @@ def command_show(actor, parameters): if len(arguments) != 2: message = "You must specify one file." elif arguments[1] in universe.files: - message = "These are the elements in the \"" + arguments[1] \ - + "\" file:$(eol)" + message = ("These are the elements in the \"" + arguments[1] + + "\" file:$(eol)") elements = universe.files[arguments[1]].data.sections() elements.sort() for element in elements: @@ -2218,15 +2217,14 @@ def command_show(actor, parameters): message = "You must specify one element." elif arguments[1] in universe.contents: element = universe.contents[arguments[1]] - message = "These are the properties of the \"" + arguments[1] \ - + \ - "\" element (in \"" + \ - element.origin.filename + "\"):$(eol)" + message = ("These are the properties of the \"" + arguments[1] + + "\" element (in \"" + element.origin.filename + + "\"):$(eol)") facets = element.facets() facets.sort() for facet in facets: - message += "$(eol) $(grn)" + facet + ": $(red)" \ - + escape_macros(element.get(facet)) + "$(nrm)" + message += ("$(eol) $(grn)" + facet + ": $(red)" + + escape_macros(element.get(facet)) + "$(nrm)") else: message = "Element \"" + arguments[1] + "\" does not exist." elif arguments[0] == "result": @@ -2265,8 +2263,8 @@ def command_show(actor, parameters): if level > -1 and start > -1 and stop > -1: message = get_loglines(level, start, stop) else: - message = "When specified, level must be 0-9 (default 1), " \ - + "start and stop must be >=1 (default 10 and 1)." + message = ("When specified, level must be 0-9 (default 1), " + "start and stop must be >=1 (default 10 and 1).") else: message = "I don't know what \"" + parameters + "\" is." actor.send(message) @@ -2287,18 +2285,18 @@ def command_create(actor, parameters): if element in universe.contents: message = "The \"" + element + "\" element already exists." else: - message = "You create \"" + \ - element + "\" within the universe." + message = ("You create \"" + + element + "\" within the universe.") logline = actor.owner.account.get( "name" ) + " created an element: " + element if filename: logline += " in file " + filename if filename not in universe.files: - message += " Warning: \"" + filename \ - + "\" is not yet included in any other file and will " \ - + \ - "not be read on startup unless this is remedied." + message += ( + " Warning: \"" + filename + "\" is not yet " + "included in any other file and will not be read " + "on startup unless this is remedied.") Element(element, universe, filename) log(logline, 6) elif len(arguments) > 2: @@ -2313,12 +2311,11 @@ def command_destroy(actor, parameters): message = "You must specify an element to destroy." else: if parameters not in universe.contents: - message = "The \"" + parameters + \ - "\" element does not exist." + message = "The \"" + parameters + "\" element does not exist." else: universe.contents[parameters].destroy() - message = "You destroy \"" + parameters \ - + "\" within the universe." + message = ("You destroy \"" + parameters + + "\" within the universe.") log( actor.owner.account.get( "name" @@ -2335,22 +2332,22 @@ def command_set(actor, parameters): else: arguments = parameters.split(" ", 2) if len(arguments) == 1: - message = "What facet of element \"" + arguments[0] \ - + "\" would you like to set?" + message = ("What facet of element \"" + arguments[0] + + "\" would you like to set?") elif len(arguments) == 2: - message = "What value would you like to set for the \"" \ - + arguments[1] + "\" facet of the \"" + arguments[0] \ - + "\" element?" + message = ("What value would you like to set for the \"" + + arguments[1] + "\" facet of the \"" + arguments[0] + + "\" element?") else: element, facet, value = arguments if element not in universe.contents: message = "The \"" + element + "\" element does not exist." else: universe.contents[element].set(facet, value) - message = "You have successfully (re)set the \"" + facet \ - + "\" facet of element \"" + element \ - + "\". Try \"show element " + \ - element + "\" for verification." + message = ("You have successfully (re)set the \"" + facet + + "\" facet of element \"" + element + + "\". Try \"show element " + + element + "\" for verification.") actor.send(message) @@ -2361,8 +2358,8 @@ def command_delete(actor, parameters): else: arguments = parameters.split(" ") if len(arguments) == 1: - message = "What facet of element \"" + arguments[0] \ - + "\" would you like to delete?" + message = ("What facet of element \"" + arguments[0] + + "\" would you like to delete?") elif len(arguments) != 2: message = "You may only specify an element and a facet." else: @@ -2370,14 +2367,14 @@ def command_delete(actor, parameters): if element not in universe.contents: message = "The \"" + element + "\" element does not exist." elif facet not in universe.contents[element].facets(): - message = "The \"" + element + "\" element has no \"" + facet \ - + "\" facet." + message = ("The \"" + element + "\" element has no \"" + facet + + "\" facet.") else: universe.contents[element].remove_facet(facet) - message = "You have successfully deleted the \"" + facet \ - + "\" facet of element \"" + element \ - + "\". Try \"show element " + \ - element + "\" for verification." + message = ("You have successfully deleted the \"" + facet + + "\" facet of element \"" + element + + "\". Try \"show element " + + element + "\" for verification.") actor.send(message) diff --git a/lib/mudpy/telnet.py b/lib/mudpy/telnet.py index a4264ce..dfac1ee 100644 --- a/lib/mudpy/telnet.py +++ b/lib/mudpy/telnet.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Telnet functions and constants for the mudpy engine.""" -# Copyright (c) 2004-2013 Jeremy Stanley . Permission +# Copyright (c) 2004-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. @@ -182,8 +182,8 @@ def negotiate_telnet_options(user): elif len_text > position + 4 and command is SB: telopt = ord(text[position + 2]) if telopt is TELOPT_NAWS: - user.columns = ord(text[position + 3]) * \ - 256 + ord(text[position + 4]) + user.columns = ( + ord(text[position + 3]) * 256 + ord(text[position + 4])) end_subnegotiation = text.find(telnet_proto(IAC, SE), position) if end_subnegotiation > 0: text = text[:position] + text[end_subnegotiation + 2:] -- 2.11.0 From 0a25cf895fa70fedf64131d284cfff19251b3d46 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Mon, 10 Mar 2014 13:45:46 +0000 Subject: [PATCH 10/16] Stop trying to rename the daemon process * lib/mudpy/misc.py(daemonize): Remove the hacky process table entry rewriting routines. They weren't cross-platform and never really worked well. Also, these days admins are more used to daemons in interpreted languages and know how to spot them in a process list with relative ease. --- lib/mudpy/misc.py | 44 -------------------------------------------- 1 file changed, 44 deletions(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index 96957bc..42d14c8 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -6,8 +6,6 @@ # terms provided in the LICENSE file distributed with this software. import codecs -import ctypes -import ctypes.util import os import random import re @@ -2399,48 +2397,6 @@ def daemonize(universe): # only if this is what we're configured to do if universe.contents["internal:process"].getboolean("daemon"): - # if possible, we want to rename the process to the same as the script - new_argv = b"\x00".join(x.encode("utf-8") for x in sys.argv) + b"\x00" - short_argv0 = os.path.basename(sys.argv[0]).encode("utf-8") + b"\x00" - - # attempt the linux way first - try: - argv_array = ctypes.POINTER(ctypes.c_char_p) - ctypes.pythonapi.Py_GetArgcArgv.argtypes = ( - ctypes.POINTER(ctypes.c_int), - ctypes.POINTER(argv_array) - ) - argc = argv_array() - ctypes.pythonapi.Py_GetArgcArgv( - ctypes.c_int(0), - ctypes.pointer(argc) - ) - old_argv0_size = len(argc.contents.value) - ctypes.memset(argc.contents, 0, len(new_argv) + old_argv0_size) - ctypes.memmove(argc.contents, new_argv, len(new_argv)) - ctypes.CDLL(ctypes.util.find_library("c")).prctl( - 15, - short_argv0, - 0, - 0, - 0 - ) - - except: - - # since that failed, maybe it's bsd? - try: - - # much simpler, since bsd has a libc function call for this - ctypes.CDLL(ctypes.util.find_library("c")).setproctitle( - new_argv - ) - - except: - - # that didn't work either, so just log that we couldn't - log("Failed to rename the interpreter process (cosmetic).") - # log before we start forking around, so the terminal gets the message log("Disassociating from the controlling terminal.") -- 2.11.0 From 9de758f62c858b1224ded9d3851e7a399eaeb230 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Wed, 19 Mar 2014 19:05:57 +0000 Subject: [PATCH 11/16] Fix log spamming on premature socket disconnect * lib/mudpy/misc.py(User.flush): When sending on a user's socket raises BrokenPipeError, their connection has probably dropped. Go ahead and set their state to disconnecting so the socket will be cleaned up earily. This also stops spamming the log on every failed write to a broken socket before it eventually times out in the TCP/IP stack as well. --- lib/mudpy/misc.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index 42d14c8..ed48769 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -893,14 +893,13 @@ class User: try: self.connection.send(self.output_queue[0]) del self.output_queue[0] - except: + except BrokenPipeError: if self.account and self.account.get("name"): account = self.account.get("name") else: account = "an unknown user" - log("Sending to %s raised an exception (broken pipe?)." - % account, 7) - pass + log("Broken pipe sending to %s." % account, 7) + self.state = "disconnecting" def enqueue_input(self): """Process and enqueue any new input.""" -- 2.11.0 From 835a5824f9313c9154388e0074bcfcb8b2a97a2e Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Thu, 27 Mar 2014 10:51:17 +0000 Subject: [PATCH 12/16] Tighten exceptions for new sockets and reads * lib/mudpy/misc.py(User.enqueue_input,check_for_connection): When reading from a socket or polling for a new connection, be specific about the exceptions which are benign so that we will still error out when more detrimental exceptions are raised. --- lib/mudpy/misc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index ed48769..7e8e612 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -907,7 +907,7 @@ class User: # check for some input try: raw_input = self.connection.recv(1024) - except: + except (BlockingIOError, OSError): raw_input = b"" # we got something @@ -1489,7 +1489,7 @@ def check_for_connection(listening_socket): # try to accept a new connection try: connection, address = listening_socket.accept() - except: + except BlockingIOError: return None # note that we got one -- 2.11.0 From c415d839f74f63db69a33e6a75af0ccc881fadb3 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Sat, 5 Apr 2014 01:58:58 +0000 Subject: [PATCH 13/16] Be explicit when show result raises an exception * lib/mudpy/misc.py(command_show): If an admin uses the show result command and it raises an exception, return the details in the result message. --- lib/mudpy/misc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index 7e8e612..9e6dd99 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -2230,8 +2230,9 @@ def command_show(actor, parameters): else: try: message = repr(eval(" ".join(arguments[1:]))) - except: - message = "Your expression raised an exception!" + except Exception as e: + message = ("$(red)Your expression raised an exception...$(eol)" + "$(eol)$(bld)%s$(nrm)" % e) elif arguments[0] == "log": if len(arguments) == 4: if re.match("^\d+$", arguments[3]) and int(arguments[3]) >= 0: -- 2.11.0 From 305d7d93122e1802b2ef840805309627b1f38e6e Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Thu, 10 Apr 2014 19:09:01 +0000 Subject: [PATCH 14/16] Make exception logging failures more robust * lib/mudpy/misc.py(excepthook): When an uncaught exception is raised and the attempt to log it fails, try to write it to stdout directly and also mention the new exception details. --- lib/mudpy/misc.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index 9e6dd99..02db563 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -2463,14 +2463,9 @@ def excepthook(excepttype, value, tracebackdata): # try to log it, if possible try: log(message, 9) - except: - pass - - # try to write it to stderr, if possible - try: - sys.stderr.write(message) - except: - pass + except Exception as e: + # try to write it to stderr, if possible + sys.stderr.write(message + "\nException while logging...\n%s" % e) def sighook(what, where): -- 2.11.0 From 4c837dca8fc1c1c2a1e6cd3e86916b6df89a6894 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Tue, 15 Apr 2014 13:08:54 +0000 Subject: [PATCH 15/16] Simplify coder documentation * doc/coder.txt: Stop suggesting that tarballs and epydoc documentation are updated for every commit, since currently they aren't. Gut the style guide now that the project is fully PEP-8 compliant, and suggest a couple of code checking utilities to help keep it clean. --- doc/coder.txt | 205 +++++----------------------------------------------------- 1 file changed, 15 insertions(+), 190 deletions(-) diff --git a/doc/coder.txt b/doc/coder.txt index 8dd8206..2d468e0 100644 --- a/doc/coder.txt +++ b/doc/coder.txt @@ -2,7 +2,7 @@ coder guide ============= -:Copyright: (c) 2004-2010 Jeremy Stanley . Permission +:Copyright: (c) 2004-2014 Jeremy Stanley . Permission to use, copy, modify, and distribute this software is granted under terms provided in the LICENSE file distributed with this software. @@ -37,10 +37,8 @@ fairly self-explanatory. A GNU-format_ ChangeLog file is generated automatically from repository commit logs, available at http://mudpy.org/res/src/mudpy/doc/ChangeLog -and is included in the doc directory of all tarball/zip files, exported -automatically after each new commit. The export script, `git2gch -`_, is included in the bin -directory. +and is included in the doc directory of all tarball/zip files. The +export script, git2gch, is included in the bin directory. .. _Git: http://git-scm.com/ .. _Gitweb: http://git.wiki.kernel.org/index.php/Gitweb @@ -51,18 +49,13 @@ application program interface ----------------------------- API documentation is maintained within docstrings in the mudpy source -code. Browsable API documentation (automatically generated with Epydoc_) -is maintained at http://mudpy.org/res/epydoc/ and refreshed with each -new commit. - -.. _Epydoc: http://epydoc.sourceforge.net/ +code. regression testing ------------------ -All new commits are tested using the `test -`_ regression testing script in -the bin directory of the source archive, to help ensure the software is +All new commits are tested using a regression testing script in the bin +directory of the source archive, to help ensure the software is continually usable. Any new features should be accompanied by suitable regression tests so that their functionality can be maintained properly through future releases. @@ -71,181 +64,13 @@ through future releases. style ------- -This section is primarily a summary of Guido van Rossum and Barry -Warsaw's `Style Guide for Python Code -`_, and borrows heavily from -it. Explanation of these rules and the reasoning behind them can be -found therein. When in need of sample code or other examples, any common -source code file or text document file distributed as part of mudpy -should serve as a suitable reference. - -indentation ------------ - -* Use 3 spaces per indentation level. (This is a deviation from Python - PEP-8, which encourages 4-space tab stops, and as such the project may - be reformatted to conform at some future date). - -tabs or spaces --------------- - -* Spaces only--no tabs ever. (Exception: Special file formats like `GNU - ChangeLog `_ require tabs for - historical reasons.) - -maximum line length -------------------- - -* Limit all lines to a maximum of 79 characters. - -* For flowing long blocks of text (docstrings, comments, documentation - files and text-based data files), limiting the length to 72 characters - is recommended. - -* The preferred way of wrapping long lines is by using Python's implied - line continuation inside parentheses, brackets and braces. Make sure - to indent the continued line appropriately. - -* The preferred place to break around a binary operator is *after* the - operator, not before it. - -* When bracketed code is expanded into multiple lines, its terminating - bracket is indented the same amount as the line on which its - corresponding initial bracket appears (essentially K&R style). - -* If bracketed parameters are too lengthy/numerous to fit together on - one line, all are split onto individual lines. - -blank lines ------------ - -* Separate top-level function and class definitions with two blank - lines. - -* Method definitions inside a class are separated by a single blank - line. - -* Extra blank lines may be used (sparingly) to separate groups of - related functions. - -* Blank lines may be omitted between a bunch of related one-liners (e.g. - a set of dummy implementations). - -* Use blank lines in functions, sparingly, to indicate logical sections. - -encodings ---------- - -* Always use ASCII whenever possible (decimal byte values 32 through 126 - and line termination appropriate to the developer's platform). - -* UTF-8 can be used, but only when a comment or docstring needs to - mention an author name that requires it; otherwise, using ``\x``, - ``\u`` or ``\U`` escapes is the preferred way to include non-ASCII - data in string literals. - -* Identifiers must be ASCII-only, and should use English words wherever - feasible. - -* In addition, string literals and comments must also be in ASCII. The - only exceptions are: - - - test cases testing the non-ASCII features, and - - - names of authors. - -* Any text constants in code are declared as Unicode; when actual byte - data is required instead, its entry point is documented clearly in - preparation for an eventual Python 3 transition. - -* All text handling routines operate on Unicode data, normalized as - early as possible to eliminate compositing sequences. - -imports -------- +This project follows Guido van Rossum and Barry Warsaw's `Style Guide`_ +for Python Code (a.k.a. "PEP-8"). When in need of sample code or other +examples, any common source code file or text document file distributed +as part of mudpy should serve as a suitable reference. Testing of all +new patches with the flake8_ and pylint_ utilities is also highly +recommended. -* Any module required by code in a function or method is imported - immediately following the docstring; import x from y constructs are - avoided whenever possible. (This is a deviation from Python PEP-8, - which encourages imports at the beginning of each file, allows import - x from y, and requires absolute imports between modules in the same - package; as such the project may be reformatted to conform at some - future date). - -whitespace in expressions and statements ----------------------------------------- - -* Avoid extraneous whitespace - - - immediately inside parentheses, brackets or braces; - - - immediately before a comma, semicolon, or colon; - - - immediately before the open parenthesis that starts the argument - list of a function call; - - - immediately before the open parenthesis that starts an indexing or - slicing; - - - more than one space around an assignment (or other) operator to - align it with another. - -* Always surround arithmetic, assignment, boolean and comparison - operators with a single space on either side. (Exception: don't use - spaces around the = sign when used to indicate a keyword argument or a - default parameter value.) - -* Compound statements (multiple statements on the same line) are - generally discouraged. - -source comments ---------------- - -* Don't state what you're doing, but rather why you're doing it. - -* Always keep comments up-to-date when the code changes. - -* Comments should be complete English sentences. - -* If a comment is a phrase or sentence, its first word should be - capitalized, unless it is an identifier that begins with a lower case - letter (never alter the case of identifiers). - -* You should use only one space after a sentence-ending period. - -* When writing English, Strunk and White apply. - -* Block comments generally apply to some (or all) code that follows - them, and are indented to the same level as that code. - -* Each line of a block comment starts with a # and a single space - (unless it is indented text inside the comment). - -* Paragraphs inside a block comment are separated by a line containing a - single #. - -* About in-line comments (comments on the same line as a statement): - - - Use them sparingly. - - - Separate them from the statement by at least two spaces. - - - Start them with a # and a single space. - -documentation strings ---------------------- - -* Write docstrings for all public modules, functions, classes, and - methods. - -* The """ that ends a multi-line docstring should be on a line by - itself, and preceded by a blank line. - -* For one liner docstrings, it's okay to keep the closing """ on the - same line. - -* When markup is used in embedded documentation (docstrings, comment - blocks, et cetera) it conforms to the `reStructuredText Markup - Specification `_. +.. Style Guide: http://www.python.org/dev/peps/pep-0008/ +.. flake8: https://pypi.python.org/pypi/flake8 +.. pylint: https://pypi.python.org/pypi/pylint -- 2.11.0 From 84f2fa2376d2202d09fea78e499fc644454a79ba Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Wed, 23 Apr 2014 11:16:46 +0000 Subject: [PATCH 16/16] Use area instead of location or room Since the term "location" was already used for the name of the facet identifying the area where an element is located, it was vague to also use that term for a class of elements which serve as those locations. Instead use the term "area" since it's as descriptive and shorter. Also avoid the term "room" as a synonym since areas can be outside as well as indoors. This further implies renaming the location.mpy sample file to become area.mpy instead. --- etc/mudpy.conf | 2 +- lib/mudpy/misc.py | 38 +++++++++++++++++++------------------- sample/__init__.mpy | 4 ++-- sample/{location.mpy => area.mpy} | 30 +++++++++++++++--------------- sample/prop.mpy | 4 ++-- share/archetype.mpy | 4 ++-- share/command.mpy | 6 +++--- 7 files changed, 44 insertions(+), 44 deletions(-) rename sample/{location.mpy => area.mpy} (65%) diff --git a/etc/mudpy.conf b/etc/mudpy.conf index 01db1e9..927b74b 100644 --- a/etc/mudpy.conf +++ b/etc/mudpy.conf @@ -3,7 +3,7 @@ # terms provided in the LICENSE file distributed with this software. [__control__] -default_files = { "account": "account.mpy", "actor": "actor.mpy", "command": "command.mpy", "internal": "internal.mpy", "location": "location.mpy", "menu": "menu.mpy", "other": "other.mpy", "prop": "prop.mpy" } +default_files = { "account": "account.mpy", "actor": "actor.mpy", "area": "area.mpy", "command": "command.mpy", "internal": "internal.mpy", "menu": "menu.mpy", "other": "other.mpy", "prop": "prop.mpy" } include_dirs = "sample" include_files = "archetype.mpy" private_files = "account.mpy" diff --git a/lib/mudpy/misc.py b/lib/mudpy/misc.py index 02db563..28b92fb 100644 --- a/lib/mudpy/misc.py +++ b/lib/mudpy/misc.py @@ -263,9 +263,9 @@ class Element: def update_location(self): """Make sure the location's contents contain this element.""" - location = self.get("location") - if location in self.universe.contents: - self.universe.contents[location].contents[self.key] = self + area = self.get("location") + if area in self.universe.contents: + self.universe.contents[area].contents[self.key] = self def clean_contents(self): """Make sure the element's contents aren't bogus.""" @@ -273,15 +273,15 @@ class Element: if element.get("location") != self.key: del self.contents[element.key] - def go_to(self, location): - """Relocate the element to a specific location.""" + def go_to(self, area): + """Relocate the element to a specific area.""" current = self.get("location") if current and self.key in self.universe.contents[current].contents: del universe.contents[current].contents[self.key] - if location in self.universe.contents: - self.set("location", location) - self.universe.contents[location].contents[self.key] = self - self.look_at(location) + if area in self.universe.contents: + self.set("location", area) + self.universe.contents[area].contents[self.key] = self + self.look_at(area) def go_home(self): """Relocate the element to its default location.""" @@ -366,9 +366,9 @@ class Element: self.send(message) def portals(self): - """Map the portal directions for a room to neighbors.""" + """Map the portal directions for an area to neighbors.""" portals = {} - if re.match("""^location:-?\d+,-?\d+,-?\d+$""", self.key): + if re.match("""^area:-?\d+,-?\d+,-?\d+$""", self.key): coordinates = [(int(x)) for x in self.key.split(":")[1].split(",")] directions = self.universe.categories["internal"]["directions"] @@ -382,7 +382,7 @@ class Element: for portal in self.getlist("gridlinks"): adjacent = map(lambda c, o: c + o, coordinates, offsets[portal]) - neighbor = "location:" + ",".join( + neighbor = "area:" + ",".join( [(str(x)) for x in adjacent] ) if neighbor in self.universe.contents: @@ -479,12 +479,12 @@ class Universe: # go through all elements to clear out inactive avatar locations for element in self.contents.values(): - location = element.get("location") - if element in inactive_avatars and location: - if location in self.contents and element.key in self.contents[ - location + area = element.get("location") + if element in inactive_avatars and area: + if area in self.contents and element.key in self.contents[ + area ].contents: - del self.contents[location].contents[element.key] + del self.contents[area].contents[element.key] element.set("default_location", location) element.remove_facet("location") @@ -2065,7 +2065,7 @@ def command_look(actor, parameters): def command_say(actor, parameters): - """Speak to others in the same room.""" + """Speak to others in the same area.""" # check for replacement macros and escape them parameters = escape_macros(parameters) @@ -2129,7 +2129,7 @@ def command_say(actor, parameters): # capitalize the first letter message = message[0].upper() + message[1:] - # tell the room + # tell the area if message: actor.echo_to_location( actor.get("name") + " " + action + "s, \"" + message + "\"" diff --git a/sample/__init__.mpy b/sample/__init__.mpy index 4dd2e2b..b66fbca 100644 --- a/sample/__init__.mpy +++ b/sample/__init__.mpy @@ -1,8 +1,8 @@ -# Copyright (c) 2004-2010 Jeremy Stanley . Permission +# Copyright (c) 2004-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. [__control__] -include_files = [ "location.mpy", "prop.mpy" ] +include_files = [ "area.mpy", "prop.mpy" ] read_only = yes diff --git a/sample/location.mpy b/sample/area.mpy similarity index 65% rename from sample/location.mpy rename to sample/area.mpy index 5aaa30d..e4641eb 100644 --- a/sample/location.mpy +++ b/sample/area.mpy @@ -1,45 +1,45 @@ -# Copyright (c) 2004-2010 Jeremy Stanley . Permission +# Copyright (c) 2004-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. -[location:-1,0,0] -description = This is the West Sample Location. It is merely provided as an example of what a location might look like. +[area:-1,0,0] +description = This is the West Sample Location. It is merely provided as an example of what an area might look like. gridlinks = ['east'] name = West Sample Location terrain = inside -[location:0,-1,0] -description = This is the South Sample Location. It is merely provided as an example of what a location might look like. +[area:0,-1,0] +description = This is the South Sample Location. It is merely provided as an example of what an area might look like. gridlinks = ['north'] name = South Sample Location terrain = inside -[location:0,0,-1] -description = This is the Lower Sample Location. It is merely provided as an example of what a location might look like. +[area:0,0,-1] +description = This is the Lower Sample Location. It is merely provided as an example of what an area might look like. gridlinks = ['up'] name = Lower Sample Location terrain = inside -[location:0,0,0] -description = This is the Center Sample Location. It is merely provided as an example of what a location might look like. +[area:0,0,0] +description = This is the Center Sample Location. It is merely provided as an example of what an area might look like. gridlinks = ['down', 'east', 'north', 'south', 'up', 'west'] name = Center Sample Location terrain = inside -[location:0,0,1] -description = This is the Upper Sample Location. It is merely provided as an example of what a location might look like. +[area:0,0,1] +description = This is the Upper Sample Location. It is merely provided as an example of what an area might look like. gridlinks = ['down'] name = Upper Sample Location terrain = inside -[location:0,1,0] -description = This is the North Sample Location. It is merely provided as an example of what a location might look like. +[area:0,1,0] +description = This is the North Sample Location. It is merely provided as an example of what an area might look like. gridlinks = ['south'] name = North Sample Location terrain = inside -[location:1,0,0] -description = This is the East Sample Location. It is merely provided as an example of what a location might look like. +[area:1,0,0] +description = This is the East Sample Location. It is merely provided as an example of what an area might look like. gridlinks = ['west'] name = East Sample Location terrain = inside diff --git a/sample/prop.mpy b/sample/prop.mpy index 51a981e..1075eba 100644 --- a/sample/prop.mpy +++ b/sample/prop.mpy @@ -1,9 +1,9 @@ -# Copyright (c) 2004-2010 Jeremy Stanley . Permission +# Copyright (c) 2004-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. [prop:sample_prop] impression = A sample prop sits here. keywords = sample prop -location = location:0,0,0 +location = area:0,0,0 diff --git a/share/archetype.mpy b/share/archetype.mpy index 8ee9c3e..aefa4f8 100644 --- a/share/archetype.mpy +++ b/share/archetype.mpy @@ -1,4 +1,4 @@ -# Copyright (c) 2004-2010 Jeremy Stanley . Permission +# Copyright (c) 2004-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. @@ -9,6 +9,6 @@ read_only = yes is_actor = yes [archetype:avatar] -default_location = location:0,0,0 +default_location = area:0,0,0 inherit = archetype:actor diff --git a/share/command.mpy b/share/command.mpy index feec37c..eed6a74 100644 --- a/share/command.mpy +++ b/share/command.mpy @@ -1,4 +1,4 @@ -# Copyright (c) 2004-2010 Jeremy Stanley . Permission +# Copyright (c) 2004-2014 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. @@ -21,7 +21,7 @@ help = Ways to create an element:$(eol)$(eol) create actor:fred$(eol) create action = command_delete(actor, parameters) administrative = yes description = Delete an existing facet from an element. -help = You can delete any facet of an element as follows:$(eol)$(eol) delete location:boardroom terrain +help = You can delete any facet of an element as follows:$(eol)$(eol) delete area:boardroom terrain [command:destroy] action = command_destroy(actor, parameters) @@ -64,7 +64,7 @@ help = This will reload all python modules and read-only data files. [command:say] action = command_say(actor, parameters) description = State something out loud. -help = This allows you to speak to other characters within the same room. If you end your sentence with punctuation, the message displayed will incorporate an appropriate action (ask, exclaim, et cetera). It will also correct common typographical errors, add punctuation and capitalize your sentence as needed (assuming you speak one sentence per line). For example:$(eol)$(eol) > say youre sure i went teh wrong way?$(eol) You ask, "You're sure I went the wrong way?"$(eol)$(eol)If necessary, enclose literal statements in quotation marks:$(eol)$(eol) > say "youre sure i went teh wrong way?"$(eol) You say, "youre sure i went teh wrong way?" +help = This allows you to speak to other characters within the same area. If you end your sentence with punctuation, the message displayed will incorporate an appropriate action (ask, exclaim, et cetera). It will also correct common typographical errors, add punctuation and capitalize your sentence as needed (assuming you speak one sentence per line). For example:$(eol)$(eol) > say youre sure i went teh wrong way?$(eol) You ask, "You're sure I went the wrong way?"$(eol)$(eol)If necessary, enclose literal statements in quotation marks:$(eol)$(eol) > say "youre sure i went teh wrong way?"$(eol) You say, "youre sure i went teh wrong way?" see_also = chat [command:set] -- 2.11.0