Clean up imports
authorJeremy Stanley <fungi@yuggoth.org>
Sat, 7 Dec 2013 23:19:08 +0000 (23:19 +0000)
committerJeremy Stanley <fungi@yuggoth.org>
Sat, 7 Dec 2013 23:19:08 +0000 (23:19 +0000)
Group all imports at the tops of files and alphabetize, for ease of
maintainabiity.

bin/git2gch
bin/mudpy
bin/test
lib/mudpy/__init__.py
lib/mudpy/data.py
lib/mudpy/misc.py
lib/mudpy/password.py
lib/mudpy/telnet.py

index 40689a9..ef16232 100755 (executable)
@@ -2,14 +2,14 @@
 # -*- coding: utf-8 -*-
 """Generates potential ChangeLog file contents from commit messages."""
 
 # -*- coding: utf-8 -*-
 """Generates potential ChangeLog file contents from commit messages."""
 
-# Copyright (c) 2010-2012 Jeremy Stanley <fungi@yuggoth.org>. Permission
+# Copyright (c) 2010-2013 Jeremy Stanley <fungi@yuggoth.org>. Permission
 # to use, copy, modify, and distribute this software is granted under
 # terms provided in the LICENSE file distributed with this software.
 
 # 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 time
 
+import git
+
 copyright = """\
 Copyright (c) 2004-2012 Jeremy Stanley <fungi@yuggoth.org>. Permission to
 use, copy, modify, and distribute this software is granted under terms
 copyright = """\
 Copyright (c) 2004-2012 Jeremy Stanley <fungi@yuggoth.org>. Permission to
 use, copy, modify, and distribute this software is granted under terms
index e946462..259c9c1 100755 (executable)
--- 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
 # 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 sys
 sys.path.append( os.path.realpath("lib") )
-import imp
+
 import mudpy
 
 # start it up
 import mudpy
 
 # start it up
index 45ce5aa..0ec3544 100755 (executable)
--- 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.
 
 # 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"),
 conversation = (
     ("Identify yourself:", "testuser"),
     ("Enter your choice:", "n"),
@@ -24,7 +26,6 @@ conversation = (
     ("Disconnecting...", ""),
 )
 
     ("Disconnecting...", ""),
 )
 
-import telnetlib
 mud = telnetlib.Telnet()
 mud.open("::1", 6669)
 for question, answer in conversation:
 mud = telnetlib.Telnet()
 mud.open("::1", 6669)
 for question, answer in conversation:
index 4224b2f..04001a0 100644 (file)
@@ -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.
 
 # 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)."""
 
 
 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
 
     # pick up the modules list from this package
     global modules
 
index de76416..7d030c2 100644 (file)
@@ -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.
 
 # 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:
 
 
 class DataFile:
 
@@ -17,14 +31,6 @@ class DataFile:
 
     def load(self):
         """Read a file and create elements accordingly."""
 
     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):
         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."""
 
     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 (
 
         # 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."""
     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:
 
     # make sure to get rid of any surrounding quotes first thing
     if file_name:
index d8c863a..2edf966 100644 (file)
@@ -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.
 
 # 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:
 
 
 class Element:
 
@@ -12,8 +28,6 @@ class Element:
 
     def __init__(self, key, universe, filename=None):
         """Set up a new 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
 
         # 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."""
 
     def getlist(self, facet, default=None):
         """Return values as list type."""
-        import mudpy.data
         if default is None:
             default = []
         value = self.get(facet)
         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."""
 
     def getdict(self, facet, default=None):
         """Return values as dict type."""
-        import mudpy.data
         if default is None:
             default = {}
         value = self.get(facet)
         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."""
 
     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))
         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."""
 
     def __init__(self, filename="", load=False):
         """Initialize the universe."""
-        import os
-        import os.path
         self.categories = {}
         self.contents = {}
         self.default_origins = {}
         self.categories = {}
         self.contents = {}
         self.default_origins = {}
@@ -456,7 +465,6 @@ class Universe:
 
     def load(self):
         """Load universe data from persistent storage."""
 
     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(
 
         # 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."""
 
     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")
 
         # 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."""
 
     def __init__(self):
         """Default values for the in-memory user variables."""
-        import mudpy.telnet
         self.account = None
         self.address = ""
         self.authenticated = False
         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."""
 
     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):
         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."""
         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:
 
         # 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."""
 
     def enqueue_input(self):
         """Process and enqueue any new input."""
-        import mudpy.telnet
-        import unicodedata
 
         # check for some input
         try:
 
         # check for some input
         try:
@@ -1059,10 +1061,6 @@ def broadcast(message, add_prompt=True):
 
 def log(message, level=0):
     """Log a message."""
 
 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")
 
     # 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."""
 
 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:
     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."""
 
 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
 
     # 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."""
 
 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 = []
 
     # 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."""
 
 def random_name():
     """Returns a random character name."""
-    import random
 
     # the vowels and consonants needed to create romaji syllables
     vowels = [
 
     # 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."""
 
 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 = {
 
     # 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."""
 
 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"):
 
     # 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."""
 
 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:
 
     # 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."""
 
 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,
 
     # 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."""
 
 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)
 
     # 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."""
 
 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)
 
     # 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."""
 
 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)
 
     # 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."""
 
 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)
 
     # 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."""
 
 def command_show(actor, parameters):
     """Show program data."""
-    import re
     message = ""
     arguments = parameters.split()
     if not parameters:
     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."""
 
 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):
 
     # 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."""
 
 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"):
 
     # 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."""
 
 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")
     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."""
 
 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):
     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."""
 
 def excepthook(excepttype, value, tracebackdata):
     """Handle uncaught exceptions."""
-    import traceback
 
     # assemble the list of errors into a single string
     message = "".join(
 
     # 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."""
 
 def sighook(what, where):
     """Handle external signals."""
-    import signal
 
     # a generic message
     message = "Caught signal: "
 
     # a generic message
     message = "Caught signal: "
@@ -2596,20 +2565,17 @@ def sighook(what, where):
 
 def override_excepthook():
     """Redefine sys.excepthook with our own."""
 
 def override_excepthook():
     """Redefine sys.excepthook with our own."""
-    import sys
     sys.excepthook = excepthook
 
 
 def assign_sighook():
     """Assign a customized handler for some signals."""
     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."""
     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:
 
     # see if a configuration file was specified
     if len(sys.argv) > 1:
index bad5579..b6b4be5 100644 (file)
@@ -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.
 
 # 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
 # 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.
     """
     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)
     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.
     """
     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"./"
     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.
     """
     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))
     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.
     """
     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""
     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)
     """
 
        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:
 
     # 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]
     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,
     algorithm, rounds, salt, hashed = encoded_hash.split(sep)[1:]
     if encoded_hash == create(
        password=password,
index 7765571..471f9fd 100644 (file)
@@ -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.
 
 # 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)
 # 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."""
 
 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
 
     # make a local copy to play with
     text = user.partial_input