mirror of
https://github.com/evennia/evennia.git
synced 2026-03-29 12:07:17 +02:00
PEP8 cleanup of the entire codebase. Unchanged are many cases of too-long lines, partly because of the rewrite they would require but also because splitting many lines up would make the code harder to read. Also the third-party libraries (idmapper, prettytable etc) were not cleaned.
This commit is contained in:
parent
30b7d2a405
commit
1ae17bcbe4
154 changed files with 5613 additions and 4054 deletions
|
|
@ -59,6 +59,7 @@ ANSI_SPACE = " "
|
|||
# Escapes
|
||||
ANSI_ESCAPES = ("{{", "%%", "\\\\")
|
||||
|
||||
|
||||
class ANSIParser(object):
|
||||
"""
|
||||
A class that parses ansi markup
|
||||
|
|
@ -75,10 +76,11 @@ class ANSIParser(object):
|
|||
# MUX-style mappings %cr %cn etc
|
||||
|
||||
self.mux_ansi_map = [
|
||||
# commented out by default; they (especially blink) are potentially annoying
|
||||
(r'%r', ANSI_RETURN),
|
||||
(r'%t', ANSI_TAB),
|
||||
(r'%b', ANSI_SPACE),
|
||||
# commented out by default; they (especially blink) are
|
||||
# potentially annoying
|
||||
(r'%r', ANSI_RETURN),
|
||||
(r'%t', ANSI_TAB),
|
||||
(r'%b', ANSI_SPACE),
|
||||
#(r'%cf', ANSI_BLINK),
|
||||
#(r'%ci', ANSI_INVERSE),
|
||||
(r'%cr', ANSI_RED),
|
||||
|
|
@ -118,18 +120,18 @@ class ANSIParser(object):
|
|||
(r'{M', normal + ANSI_MAGENTA),
|
||||
(r'{c', hilite + ANSI_CYAN),
|
||||
(r'{C', normal + ANSI_CYAN),
|
||||
(r'{w', hilite + ANSI_WHITE), # pure white
|
||||
(r'{W', normal + ANSI_WHITE), #light grey
|
||||
(r'{x', hilite + ANSI_BLACK), #dark grey
|
||||
(r'{X', normal + ANSI_BLACK), #pure black
|
||||
(r'{n', normal) #reset
|
||||
(r'{w', hilite + ANSI_WHITE), # pure white
|
||||
(r'{W', normal + ANSI_WHITE), # light grey
|
||||
(r'{x', hilite + ANSI_BLACK), # dark grey
|
||||
(r'{X', normal + ANSI_BLACK), # pure black
|
||||
(r'{n', normal) # reset
|
||||
]
|
||||
|
||||
# xterm256 {123, %c134,
|
||||
|
||||
self.xterm256_map = [
|
||||
(r'%c([0-5]{3})', self.parse_rgb), # %c123 - foreground colour
|
||||
(r'%c(b[0-5]{3})', self.parse_rgb), # %cb123 - background colour
|
||||
(r'%c(b[0-5]{3})', self.parse_rgb), # %cb123 - background colour
|
||||
(r'{([0-5]{3})', self.parse_rgb), # {123 - foreground colour
|
||||
(r'{(b[0-5]{3})', self.parse_rgb) # {b123 - background colour
|
||||
]
|
||||
|
|
@ -145,7 +147,8 @@ class ANSIParser(object):
|
|||
# prepare matching ansi codes overall
|
||||
self.ansi_regex = re.compile("\033\[[0-9;]+m")
|
||||
|
||||
# escapes - these double-chars will be replaced with a single instance of each
|
||||
# escapes - these double-chars will be replaced with a single
|
||||
# instance of each
|
||||
self.ansi_escapes = re.compile(r"(%s)" % "|".join(ANSI_ESCAPES), re.DOTALL)
|
||||
|
||||
def parse_rgb(self, rgbmatch):
|
||||
|
|
@ -174,37 +177,61 @@ class ANSIParser(object):
|
|||
#print "ANSI convert:", red, green, blue
|
||||
# xterm256 not supported, convert the rgb value to ansi instead
|
||||
if red == green and red == blue and red < 2:
|
||||
if background: return ANSI_BACK_BLACK
|
||||
elif red >= 1: return ANSI_HILITE + ANSI_BLACK
|
||||
else: return ANSI_NORMAL + ANSI_BLACK
|
||||
if background:
|
||||
return ANSI_BACK_BLACK
|
||||
elif red >= 1:
|
||||
return ANSI_HILITE + ANSI_BLACK
|
||||
else:
|
||||
return ANSI_NORMAL + ANSI_BLACK
|
||||
elif red == green and red == blue:
|
||||
if background: return ANSI_BACK_WHITE
|
||||
elif red >= 4: return ANSI_HILITE + ANSI_WHITE
|
||||
else: return ANSI_NORMAL + ANSI_WHITE
|
||||
if background:
|
||||
return ANSI_BACK_WHITE
|
||||
elif red >= 4:
|
||||
return ANSI_HILITE + ANSI_WHITE
|
||||
else:
|
||||
return ANSI_NORMAL + ANSI_WHITE
|
||||
elif red > green and red > blue:
|
||||
if background: return ANSI_BACK_RED
|
||||
elif red >= 3: return ANSI_HILITE + ANSI_RED
|
||||
else: return ANSI_NORMAL + ANSI_RED
|
||||
if background:
|
||||
return ANSI_BACK_RED
|
||||
elif red >= 3:
|
||||
return ANSI_HILITE + ANSI_RED
|
||||
else:
|
||||
return ANSI_NORMAL + ANSI_RED
|
||||
elif red == green and red > blue:
|
||||
if background: return ANSI_BACK_YELLOW
|
||||
elif red >= 3: return ANSI_HILITE + ANSI_YELLOW
|
||||
else: return ANSI_NORMAL + ANSI_YELLOW
|
||||
if background:
|
||||
return ANSI_BACK_YELLOW
|
||||
elif red >= 3:
|
||||
return ANSI_HILITE + ANSI_YELLOW
|
||||
else:
|
||||
return ANSI_NORMAL + ANSI_YELLOW
|
||||
elif red == blue and red > green:
|
||||
if background: return ANSI_BACK_MAGENTA
|
||||
elif red >= 3: return ANSI_HILITE + ANSI_MAGENTA
|
||||
else: return ANSI_NORMAL + ANSI_MAGENTA
|
||||
if background:
|
||||
return ANSI_BACK_MAGENTA
|
||||
elif red >= 3:
|
||||
return ANSI_HILITE + ANSI_MAGENTA
|
||||
else:
|
||||
return ANSI_NORMAL + ANSI_MAGENTA
|
||||
elif green > blue:
|
||||
if background: return ANSI_BACK_GREEN
|
||||
elif green >= 3: return ANSI_HILITE + ANSI_GREEN
|
||||
else: return ANSI_NORMAL + ANSI_GREEN
|
||||
if background:
|
||||
return ANSI_BACK_GREEN
|
||||
elif green >= 3:
|
||||
return ANSI_HILITE + ANSI_GREEN
|
||||
else:
|
||||
return ANSI_NORMAL + ANSI_GREEN
|
||||
elif green == blue:
|
||||
if background: return ANSI_BACK_CYAN
|
||||
elif green >= 3: return ANSI_HILITE + ANSI_CYAN
|
||||
else: return ANSI_NORMAL + ANSI_CYAN
|
||||
if background:
|
||||
return ANSI_BACK_CYAN
|
||||
elif green >= 3:
|
||||
return ANSI_HILITE + ANSI_CYAN
|
||||
else:
|
||||
return ANSI_NORMAL + ANSI_CYAN
|
||||
else: # mostly blue
|
||||
if background: return ANSI_BACK_BLUE
|
||||
elif blue >= 3: return ANSI_HILITE + ANSI_BLUE
|
||||
else: return ANSI_NORMAL + ANSI_BLUE
|
||||
if background:
|
||||
return ANSI_BACK_BLUE
|
||||
elif blue >= 3:
|
||||
return ANSI_HILITE + ANSI_BLUE
|
||||
else:
|
||||
return ANSI_NORMAL + ANSI_BLUE
|
||||
|
||||
def parse_ansi(self, string, strip_ansi=False, xterm256=False):
|
||||
"""
|
||||
|
|
@ -227,13 +254,14 @@ class ANSIParser(object):
|
|||
part = sub[0].sub(sub[1], part)
|
||||
string += "%s%s" % (part, sep[0].strip())
|
||||
if strip_ansi:
|
||||
# remove all ansi codes (including those manually inserted in string)
|
||||
# remove all ansi codes (including those manually
|
||||
# inserted in string)
|
||||
string = self.ansi_regex.sub("", string)
|
||||
return string
|
||||
|
||||
|
||||
ANSI_PARSER = ANSIParser()
|
||||
|
||||
|
||||
#
|
||||
# Access function
|
||||
#
|
||||
|
|
@ -245,8 +273,9 @@ def parse_ansi(string, strip_ansi=False, parser=ANSI_PARSER, xterm256=False):
|
|||
"""
|
||||
return parser.parse_ansi(string, strip_ansi=strip_ansi, xterm256=xterm256)
|
||||
|
||||
|
||||
def raw(string):
|
||||
"""
|
||||
Escapes a string into a form which won't be colorized by the ansi parser.
|
||||
"""
|
||||
return string.replace('{','{{').replace('%','%%')
|
||||
return string.replace('{', '{{').replace('%', '%%')
|
||||
|
|
|
|||
|
|
@ -167,16 +167,18 @@ script = create.create_script()
|
|||
|
||||
import re
|
||||
import codecs
|
||||
import traceback, sys
|
||||
from traceback import format_exc
|
||||
import traceback
|
||||
import sys
|
||||
#from traceback import format_exc
|
||||
from django.conf import settings
|
||||
from src.utils import logger
|
||||
from src.utils import utils
|
||||
from game import settings as settings_module
|
||||
#from game import settings as settings_module
|
||||
|
||||
ENCODINGS = settings.ENCODINGS
|
||||
CODE_INFO_HEADER = re.compile(r"\(.*?\)")
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Helper function
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -216,7 +218,7 @@ def read_batchfile(pythonpath, file_ending='.py'):
|
|||
continue
|
||||
|
||||
load_errors = []
|
||||
err =None
|
||||
err = None
|
||||
# We have successfully found and opened the file. Now actually
|
||||
# try to decode it using the given protocol.
|
||||
try:
|
||||
|
|
@ -246,6 +248,7 @@ def read_batchfile(pythonpath, file_ending='.py'):
|
|||
else:
|
||||
return lines
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# Batch-command processor
|
||||
|
|
@ -261,9 +264,9 @@ class BatchCommandProcessor(object):
|
|||
"""
|
||||
This parses the lines of a batchfile according to the following
|
||||
rules:
|
||||
1) # at the beginning of a line marks the end of the command before it.
|
||||
It is also a comment and any number of # can exist on subsequent
|
||||
lines (but not inside comments).
|
||||
1) # at the beginning of a line marks the end of the command before
|
||||
it. It is also a comment and any number of # can exist on
|
||||
subsequent lines (but not inside comments).
|
||||
2) #INSERT at the beginning of a line imports another
|
||||
batch-cmd file file and pastes it into the batch file as if
|
||||
it was written there.
|
||||
|
|
@ -323,10 +326,10 @@ class BatchCommandProcessor(object):
|
|||
curr_cmd = ""
|
||||
filename = line.lstrip("#INSERT").strip()
|
||||
insert_commands = self.parse_file(filename)
|
||||
if insert_commands == None:
|
||||
if insert_commands is None:
|
||||
insert_commands = ["{rINSERT ERROR: %s{n" % filename]
|
||||
commands.extend(insert_commands)
|
||||
else: #comment
|
||||
else: #comment
|
||||
if curr_cmd:
|
||||
commands.append(curr_cmd.strip())
|
||||
curr_cmd = ""
|
||||
|
|
@ -341,6 +344,7 @@ class BatchCommandProcessor(object):
|
|||
commands = [c.strip('\r\n') for c in commands]
|
||||
return commands
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# Batch-code processor
|
||||
|
|
@ -350,11 +354,14 @@ class BatchCommandProcessor(object):
|
|||
def tb_filename(tb):
|
||||
"Helper to get filename from traceback"
|
||||
return tb.tb_frame.f_code.co_filename
|
||||
|
||||
|
||||
def tb_iter(tb):
|
||||
while tb is not None:
|
||||
yield tb
|
||||
tb = tb.tb_next
|
||||
|
||||
|
||||
class BatchCodeProcessor(object):
|
||||
"""
|
||||
This implements a batch-code processor
|
||||
|
|
@ -368,7 +375,8 @@ class BatchCodeProcessor(object):
|
|||
|
||||
1) Lines starting with #HEADER starts a header block (ends other blocks)
|
||||
2) Lines starting with #CODE begins a code block (ends other blocks)
|
||||
3) #CODE headers may be of the following form: #CODE (info) objname, objname2, ...
|
||||
3) #CODE headers may be of the following form:
|
||||
#CODE (info) objname, objname2, ...
|
||||
4) Lines starting with #INSERT are on form #INSERT filename.
|
||||
3) All lines outside blocks are stripped.
|
||||
4) All excess whitespace beginning/ending a block is stripped.
|
||||
|
|
@ -378,8 +386,8 @@ class BatchCodeProcessor(object):
|
|||
# helper function
|
||||
def parse_line(line):
|
||||
"""
|
||||
Identifies the line type: block command, comment, empty or normal code.
|
||||
|
||||
Identifies the line type:
|
||||
block command, comment, empty or normal code.
|
||||
"""
|
||||
parseline = line.strip()
|
||||
|
||||
|
|
@ -432,7 +440,7 @@ class BatchCodeProcessor(object):
|
|||
# are not checking for cyclic imports!
|
||||
in_header = False
|
||||
in_code = False
|
||||
inserted_codes = self.parse_file(line) or [{'objs':"", 'info':line, 'code':""}]
|
||||
inserted_codes = self.parse_file(line) or [{'objs': "", 'info': line, 'code': ""}]
|
||||
for codedict in inserted_codes:
|
||||
codedict["inserted"] = True
|
||||
codes.extend(inserted_codes)
|
||||
|
|
@ -444,7 +452,7 @@ class BatchCodeProcessor(object):
|
|||
in_code = True
|
||||
# the line is a list of object variable names
|
||||
# (or an empty list) at this point.
|
||||
codedict = {'objs':line, 'info':info, 'code':""}
|
||||
codedict = {'objs': line, 'info': info, 'code': ""}
|
||||
codes.append(codedict)
|
||||
elif mode == 'comment' and in_header:
|
||||
continue
|
||||
|
|
@ -485,7 +493,7 @@ class BatchCodeProcessor(object):
|
|||
"""
|
||||
# define the execution environment
|
||||
environ = "settings_module.configure()"
|
||||
environdict = {"settings_module":settings}
|
||||
environdict = {"settings_module": settings}
|
||||
if extra_environ:
|
||||
for key, value in extra_environ.items():
|
||||
environdict[key] = value
|
||||
|
|
|
|||
|
|
@ -43,10 +43,12 @@ _channelhandler = None
|
|||
|
||||
|
||||
# limit symbol import from API
|
||||
__all__ = ("create_object", "create_script", "create_help_entry", "create_message", "create_channel", "create_player")
|
||||
__all__ = ("create_object", "create_script", "create_help_entry",
|
||||
"create_message", "create_channel", "create_player")
|
||||
|
||||
_GA = object.__getattribute__
|
||||
|
||||
|
||||
#
|
||||
# Game Object creation
|
||||
#
|
||||
|
|
@ -105,7 +107,8 @@ def create_object(typeclass, key=None, location=None,
|
|||
new_object = new_db_object.typeclass
|
||||
|
||||
if not _GA(new_object, "is_typeclass")(typeclass, exact=True):
|
||||
# this will fail if we gave a typeclass as input and it still gave us a default
|
||||
# this will fail if we gave a typeclass as input and it still
|
||||
# gave us a default
|
||||
SharedMemoryModel.delete(new_db_object)
|
||||
if report_to:
|
||||
_GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_object.key, typeclass,
|
||||
|
|
@ -122,14 +125,14 @@ def create_object(typeclass, key=None, location=None,
|
|||
# call the hook method. This is where all at_creation
|
||||
# customization happens as the typeclass stores custom
|
||||
# things on its database object.
|
||||
new_object.basetype_setup() # setup the basics of Exits, Characters etc.
|
||||
new_object.basetype_setup() # setup the basics of Exits, Characters etc.
|
||||
new_object.at_object_creation()
|
||||
|
||||
# custom-given perms/locks overwrite hooks
|
||||
if permissions:
|
||||
new_object.permissions.add(permissions)
|
||||
if locks:
|
||||
new_object.locks.add(locks)
|
||||
new_object.locks.add(locks)
|
||||
if aliases:
|
||||
new_object.aliases.add(aliases)
|
||||
|
||||
|
|
@ -137,7 +140,7 @@ def create_object(typeclass, key=None, location=None,
|
|||
if home:
|
||||
new_object.home = home
|
||||
else:
|
||||
new_object.home = settings.CHARACTER_DEFAULT_HOME if not nohome else None
|
||||
new_object.home = settings.CHARACTER_DEFAULT_HOME if not nohome else None
|
||||
|
||||
if location:
|
||||
new_object.move_to(location, quiet=True)
|
||||
|
|
@ -154,6 +157,7 @@ def create_object(typeclass, key=None, location=None,
|
|||
#alias for create_object
|
||||
object = create_object
|
||||
|
||||
|
||||
#
|
||||
# Script creation
|
||||
#
|
||||
|
|
@ -218,7 +222,8 @@ def create_script(typeclass, key=None, obj=None, locks=None,
|
|||
new_script = new_db_script.typeclass
|
||||
|
||||
if not _GA(new_db_script, "is_typeclass")(typeclass, exact=True):
|
||||
# this will fail if we gave a typeclass as input and it still gave us a default
|
||||
# this will fail if we gave a typeclass as input and it still
|
||||
# gave us a default
|
||||
SharedMemoryModel.delete(new_db_script)
|
||||
if report_to:
|
||||
_GA(report_to, "msg")("Error creating %s (%s): %s" % (new_db_script.key, typeclass,
|
||||
|
|
@ -243,13 +248,13 @@ def create_script(typeclass, key=None, obj=None, locks=None,
|
|||
new_script.key = key
|
||||
if locks:
|
||||
new_script.locks.add(locks)
|
||||
if interval != None:
|
||||
if interval is not None:
|
||||
new_script.interval = interval
|
||||
if start_delay != None:
|
||||
if start_delay is not None:
|
||||
new_script.start_delay = start_delay
|
||||
if repeats != None:
|
||||
if repeats is not None:
|
||||
new_script.repeats = repeats
|
||||
if persistent != None:
|
||||
if persistent is not None:
|
||||
new_script.persistent = persistent
|
||||
|
||||
# a new created script should usually be started.
|
||||
|
|
@ -261,6 +266,7 @@ def create_script(typeclass, key=None, obj=None, locks=None,
|
|||
#alias
|
||||
script = create_script
|
||||
|
||||
|
||||
#
|
||||
# Help entry creation
|
||||
#
|
||||
|
|
@ -296,6 +302,7 @@ def create_help_entry(key, entrytext, category="General", locks=None):
|
|||
# alias
|
||||
help_entry = create_help_entry
|
||||
|
||||
|
||||
#
|
||||
# Comm system methods
|
||||
#
|
||||
|
|
@ -343,6 +350,7 @@ def create_message(senderobj, message, channels=None,
|
|||
return new_message
|
||||
message = create_message
|
||||
|
||||
|
||||
def create_channel(key, aliases=None, desc=None,
|
||||
locks=None, keep_log=True,
|
||||
typeclass=None):
|
||||
|
|
@ -389,6 +397,7 @@ def create_channel(key, aliases=None, desc=None,
|
|||
|
||||
channel = create_channel
|
||||
|
||||
|
||||
#
|
||||
# Player creation methods
|
||||
#
|
||||
|
|
@ -456,7 +465,8 @@ def create_player(key, email, password,
|
|||
new_player = new_db_player.typeclass
|
||||
|
||||
if not _GA(new_db_player, "is_typeclass")(typeclass, exact=True):
|
||||
# this will fail if we gave a typeclass as input and it still gave us a default
|
||||
# this will fail if we gave a typeclass as input
|
||||
# and it still gave us a default
|
||||
SharedMemoryModel.delete(new_db_player)
|
||||
if report_to:
|
||||
_GA(report_to, "msg")("Error creating %s (%s):\n%s" % (new_db_player.key, typeclass,
|
||||
|
|
@ -465,7 +475,7 @@ def create_player(key, email, password,
|
|||
else:
|
||||
raise Exception(_GA(new_db_player, "typeclass_last_errmsg"))
|
||||
|
||||
new_player.basetype_setup() # setup the basic locks and cmdset
|
||||
new_player.basetype_setup() # setup the basic locks and cmdset
|
||||
# call hook method (may override default permissions)
|
||||
new_player.at_player_creation()
|
||||
|
||||
|
|
|
|||
|
|
@ -50,8 +50,12 @@ if uses_database("mysql") and ServerConfig.objects.get_mysql_db_version() < '5.6
|
|||
else:
|
||||
_DATESTRING = "%Y:%m:%d-%H:%M:%S:%f"
|
||||
|
||||
|
||||
def _TO_DATESTRING(obj):
|
||||
"this will only be called with valid database objects. Returns datestring on correct form."
|
||||
"""
|
||||
this will only be called with valid database objects. Returns datestring
|
||||
on correct form.
|
||||
"""
|
||||
try:
|
||||
return _GA(obj, "db_date_created").strftime(_DATESTRING)
|
||||
except AttributeError:
|
||||
|
|
@ -74,6 +78,7 @@ def _init_globals():
|
|||
# SaverList, SaverDict, SaverSet - Attribute-specific helper classes and functions
|
||||
#
|
||||
|
||||
|
||||
def _save(method):
|
||||
"method decorator that saves data to Attribute"
|
||||
def save_wrapper(self, *args, **kwargs):
|
||||
|
|
@ -83,6 +88,7 @@ def _save(method):
|
|||
return ret
|
||||
return update_wrapper(save_wrapper, method)
|
||||
|
||||
|
||||
class _SaverMutable(object):
|
||||
"""
|
||||
Parent class for properly handling of nested mutables in
|
||||
|
|
@ -95,6 +101,7 @@ class _SaverMutable(object):
|
|||
self._parent = kwargs.pop("parent", None)
|
||||
self._db_obj = kwargs.pop("db_obj", None)
|
||||
self._data = None
|
||||
|
||||
def _save_tree(self):
|
||||
"recursively traverse back up the tree, save when we reach the root"
|
||||
if self._parent:
|
||||
|
|
@ -103,6 +110,7 @@ class _SaverMutable(object):
|
|||
self._db_obj.value = self
|
||||
else:
|
||||
logger.log_errmsg("_SaverMutable %s has no root Attribute to save to." % self)
|
||||
|
||||
def _convert_mutables(self, data):
|
||||
"converts mutables to Saver* variants and assigns .parent property"
|
||||
def process_tree(item, parent):
|
||||
|
|
@ -127,19 +135,25 @@ class _SaverMutable(object):
|
|||
|
||||
def __repr__(self):
|
||||
return self._data.__repr__()
|
||||
|
||||
def __len__(self):
|
||||
return self._data.__len__()
|
||||
|
||||
def __iter__(self):
|
||||
return self._data.__iter__()
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._data.__getitem__(key)
|
||||
|
||||
@_save
|
||||
def __setitem__(self, key, value):
|
||||
self._data.__setitem__(key, self._convert_mutables(value))
|
||||
|
||||
@_save
|
||||
def __delitem__(self, key):
|
||||
self._data.__delitem__(key)
|
||||
|
||||
|
||||
class _SaverList(_SaverMutable, MutableSequence):
|
||||
"""
|
||||
A list that saves itself to an Attribute when updated.
|
||||
|
|
@ -147,14 +161,17 @@ class _SaverList(_SaverMutable, MutableSequence):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super(_SaverList, self).__init__(*args, **kwargs)
|
||||
self._data = list(*args)
|
||||
|
||||
@_save
|
||||
def __add__(self, otherlist):
|
||||
self._data = self._data.__add__(otherlist)
|
||||
return self._data
|
||||
|
||||
@_save
|
||||
def insert(self, index, value):
|
||||
self._data.insert(index, self._convert_mutables(value))
|
||||
|
||||
|
||||
class _SaverDict(_SaverMutable, MutableMapping):
|
||||
"""
|
||||
A dict that stores changes to an Attribute when updated
|
||||
|
|
@ -163,6 +180,7 @@ class _SaverDict(_SaverMutable, MutableMapping):
|
|||
super(_SaverDict, self).__init__(*args, **kwargs)
|
||||
self._data = dict(*args)
|
||||
|
||||
|
||||
class _SaverSet(_SaverMutable, MutableSet):
|
||||
"""
|
||||
A set that saves to an Attribute when updated
|
||||
|
|
@ -170,11 +188,14 @@ class _SaverSet(_SaverMutable, MutableSet):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super(_SaverSet, self).__init__(*args, **kwargs)
|
||||
self._data = set(*args)
|
||||
|
||||
def __contains__(self, value):
|
||||
return self._data.__contains__(value)
|
||||
|
||||
@_save
|
||||
def add(self, value):
|
||||
self._data.add(self._convert_mutables(value))
|
||||
|
||||
@_save
|
||||
def discard(self, value):
|
||||
self._data.discard(value)
|
||||
|
|
@ -187,14 +208,18 @@ class _SaverSet(_SaverMutable, MutableSet):
|
|||
def pack_dbobj(item):
|
||||
"""
|
||||
Check and convert django database objects to an internal representation.
|
||||
This either returns the original input item or a tuple ("__packed_dbobj__", key, creation_time, id)
|
||||
This either returns the original input item or a tuple
|
||||
("__packed_dbobj__", key, creation_time, id)
|
||||
"""
|
||||
_init_globals()
|
||||
obj = hasattr(item, 'dbobj') and item.dbobj or item
|
||||
obj = hasattr(item, 'dbobj') and item.dbobj or item
|
||||
natural_key = _FROM_MODEL_MAP[hasattr(obj, "id") and hasattr(obj, "db_date_created") and
|
||||
hasattr(obj, '__class__') and obj.__class__.__name__.lower()]
|
||||
# build the internal representation as a tuple ("__packed_dbobj__", key, creation_time, id)
|
||||
return natural_key and ('__packed_dbobj__', natural_key, _TO_DATESTRING(obj), _GA(obj, "id")) or item
|
||||
# build the internal representation as a tuple
|
||||
# ("__packed_dbobj__", key, creation_time, id)
|
||||
return natural_key and ('__packed_dbobj__', natural_key,
|
||||
_TO_DATESTRING(obj), _GA(obj, "id")) or item
|
||||
|
||||
|
||||
def unpack_dbobj(item):
|
||||
"""
|
||||
|
|
@ -208,21 +233,26 @@ def unpack_dbobj(item):
|
|||
obj = item[3] and _TO_TYPECLASS(_TO_MODEL_MAP[item[1]].objects.get(id=item[3]))
|
||||
except ObjectDoesNotExist:
|
||||
return None
|
||||
# even if we got back a match, check the sanity of the date (some databases may 're-use' the id)
|
||||
try: dbobj = obj.dbobj
|
||||
except AttributeError: dbobj = obj
|
||||
# even if we got back a match, check the sanity of the date (some
|
||||
# databases may 're-use' the id)
|
||||
try:
|
||||
dbobj = obj.dbobj
|
||||
except AttributeError:
|
||||
dbobj = obj
|
||||
return _TO_DATESTRING(dbobj) == item[2] and obj or None
|
||||
|
||||
|
||||
#
|
||||
# Access methods
|
||||
#
|
||||
|
||||
def to_pickle(data):
|
||||
"""
|
||||
This prepares data on arbitrary form to be pickled. It handles any nested structure
|
||||
and returns data on a form that is safe to pickle (including having converted any
|
||||
database models to their internal representation). We also convert any Saver*-type
|
||||
objects back to their normal representations, they are not pickle-safe.
|
||||
This prepares data on arbitrary form to be pickled. It handles any nested
|
||||
structure and returns data on a form that is safe to pickle (including
|
||||
having converted any database models to their internal representation).
|
||||
We also convert any Saver*-type objects back to their normal
|
||||
representations, they are not pickle-safe.
|
||||
"""
|
||||
def process_item(item):
|
||||
"Recursive processor and identification of data"
|
||||
|
|
@ -246,20 +276,22 @@ def to_pickle(data):
|
|||
return pack_dbobj(item)
|
||||
return process_item(data)
|
||||
|
||||
|
||||
@transaction.autocommit
|
||||
def from_pickle(data, db_obj=None):
|
||||
"""
|
||||
This should be fed a just de-pickled data object. It will be converted back
|
||||
to a form that may contain database objects again. Note that if a database
|
||||
object was removed (or changed in-place) in the database, None will be returned.
|
||||
object was removed (or changed in-place) in the database, None will be
|
||||
returned.
|
||||
|
||||
db_obj - this is the model instance (normally an Attribute) that _Saver*-type
|
||||
iterables (_SaverList etc) will save to when they update. It must have a 'value'
|
||||
property that saves assigned data to the database. Skip if not serializing onto
|
||||
a given object.
|
||||
db_obj - this is the model instance (normally an Attribute) that
|
||||
_Saver*-type iterables (_SaverList etc) will save to when they
|
||||
update. It must have a 'value' property that saves assigned data
|
||||
to the database. Skip if not serializing onto a given object.
|
||||
|
||||
If db_obj is given, this function will convert lists, dicts and sets to their
|
||||
_SaverList, _SaverDict and _SaverSet counterparts.
|
||||
If db_obj is given, this function will convert lists, dicts and sets
|
||||
to their _SaverList, _SaverDict and _SaverSet counterparts.
|
||||
|
||||
"""
|
||||
def process_item(item):
|
||||
|
|
@ -278,7 +310,8 @@ def from_pickle(data, db_obj=None):
|
|||
return set(process_item(val) for val in item)
|
||||
elif hasattr(item, '__iter__'):
|
||||
try:
|
||||
# we try to conserve the iterable class if it accepts an iterator
|
||||
# we try to conserve the iterable class if
|
||||
# it accepts an iterator
|
||||
return item.__class__(process_item(val) for val in item)
|
||||
except (AttributeError, TypeError):
|
||||
return [process_item(val) for val in item]
|
||||
|
|
@ -300,7 +333,8 @@ def from_pickle(data, db_obj=None):
|
|||
return dat
|
||||
elif dtype == dict:
|
||||
dat = _SaverDict(parent=parent)
|
||||
dat._data.update(dict((key, process_tree(val, dat)) for key, val in item.items()))
|
||||
dat._data.update(dict((key, process_tree(val, dat))
|
||||
for key, val in item.items()))
|
||||
return dat
|
||||
elif dtype == set:
|
||||
dat = _SaverSet(parent=parent)
|
||||
|
|
@ -308,7 +342,8 @@ def from_pickle(data, db_obj=None):
|
|||
return dat
|
||||
elif hasattr(item, '__iter__'):
|
||||
try:
|
||||
# we try to conserve the iterable class if it accepts an iterator
|
||||
# we try to conserve the iterable class if it
|
||||
# accepts an iterator
|
||||
return item.__class__(process_tree(val, parent) for val in item)
|
||||
except (AttributeError, TypeError):
|
||||
dat = _SaverList(parent=parent)
|
||||
|
|
@ -326,7 +361,8 @@ def from_pickle(data, db_obj=None):
|
|||
return dat
|
||||
elif dtype == dict:
|
||||
dat = _SaverDict(db_obj=db_obj)
|
||||
dat._data.update((key, process_tree(val, parent=dat)) for key, val in data.items())
|
||||
dat._data.update((key, process_tree(val, parent=dat))
|
||||
for key, val in data.items())
|
||||
return dat
|
||||
elif dtype == set:
|
||||
dat = _SaverSet(db_obj=db_obj)
|
||||
|
|
@ -334,17 +370,22 @@ def from_pickle(data, db_obj=None):
|
|||
return dat
|
||||
return process_item(data)
|
||||
|
||||
|
||||
def do_pickle(data):
|
||||
"Perform pickle to string"
|
||||
return to_str(dumps(data, protocol=PICKLE_PROTOCOL))
|
||||
|
||||
|
||||
def do_unpickle(data):
|
||||
"Retrieve pickle from pickled string"
|
||||
return loads(to_str(data))
|
||||
|
||||
|
||||
def dbserialize(data):
|
||||
"Serialize to pickled form in one step"
|
||||
return do_pickle(to_pickle(data))
|
||||
|
||||
|
||||
def dbunserialize(data, db_obj=None):
|
||||
"Un-serialize in one step. See from_pickle for help db_obj."
|
||||
return do_unpickle(from_pickle(data, db_obj=db_obj))
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ TIMEFACTOR = settings.TIME_FACTOR
|
|||
# Common real-life time measures, in seconds.
|
||||
# You should not change these.
|
||||
|
||||
REAL_TICK = max(1.0, settings.TIME_TICK) #Smallest time unit (min 1s)
|
||||
REAL_MIN = 60.0 # seconds per minute in real world
|
||||
REAL_TICK = max(1.0, settings.TIME_TICK) # Smallest time unit (min 1s)
|
||||
REAL_MIN = 60.0 # seconds per minute in real world
|
||||
|
||||
# Game-time units, in real-life seconds. These are supplied as
|
||||
# a convenient measure for determining the current in-game time,
|
||||
|
|
@ -40,6 +40,7 @@ WEEK = DAY * settings.TIME_DAY_PER_WEEK
|
|||
MONTH = WEEK * settings.TIME_WEEK_PER_MONTH
|
||||
YEAR = MONTH * settings.TIME_MONTH_PER_YEAR
|
||||
|
||||
|
||||
class GameTime(Script):
|
||||
"""
|
||||
This sets up an script that keeps track of the
|
||||
|
|
@ -51,12 +52,12 @@ class GameTime(Script):
|
|||
"""
|
||||
self.key = "sys_game_time"
|
||||
self.desc = "Keeps track of the game time"
|
||||
self.interval = REAL_MIN # update every minute
|
||||
self.interval = REAL_MIN # update every minute
|
||||
self.persistent = True
|
||||
self.start_delay = True
|
||||
self.attributes.add("game_time", 0.0) #IC time
|
||||
self.attributes.add("run_time", 0.0) #OOC time
|
||||
self.attributes.add("up_time", 0.0) #OOC time
|
||||
self.attributes.add("game_time", 0.0) # IC time
|
||||
self.attributes.add("run_time", 0.0) # OOC time
|
||||
self.attributes.add("up_time", 0.0) # OOC time
|
||||
|
||||
def at_repeat(self):
|
||||
"""
|
||||
|
|
@ -77,6 +78,7 @@ class GameTime(Script):
|
|||
"""
|
||||
self.attributes.add("up_time", 0.0)
|
||||
|
||||
|
||||
# Access routines
|
||||
|
||||
def gametime_format(seconds):
|
||||
|
|
@ -94,27 +96,29 @@ def gametime_format(seconds):
|
|||
# do this or we cancel the already counted
|
||||
# timefactor in the timer script...
|
||||
sec = int(seconds * TIMEFACTOR)
|
||||
years, sec = sec/YEAR, sec % YEAR
|
||||
months, sec = sec/MONTH, sec % MONTH
|
||||
weeks, sec = sec/WEEK, sec % WEEK
|
||||
days, sec = sec/DAY, sec % DAY
|
||||
hours, sec = sec/HOUR, sec % HOUR
|
||||
minutes, sec = sec/MIN, sec % MIN
|
||||
years, sec = sec / YEAR, sec % YEAR
|
||||
months, sec = sec / MONTH, sec % MONTH
|
||||
weeks, sec = sec / WEEK, sec % WEEK
|
||||
days, sec = sec / DAY, sec % DAY
|
||||
hours, sec = sec / HOUR, sec % HOUR
|
||||
minutes, sec = sec / MIN, sec % MIN
|
||||
return (years, months, weeks, days, hours, minutes, sec)
|
||||
|
||||
|
||||
def realtime_format(seconds):
|
||||
"""
|
||||
As gametime format, but with real time units
|
||||
"""
|
||||
sec = int(seconds)
|
||||
years, sec = sec/29030400, sec % 29030400
|
||||
months, sec = sec/2419200, sec % 2419200
|
||||
weeks, sec = sec/604800, sec % 604800
|
||||
days, sec = sec/86400, sec % 86400
|
||||
hours, sec = sec/3600, sec % 3600
|
||||
minutes, sec = sec/60, sec % 60
|
||||
years, sec = sec / 29030400, sec % 29030400
|
||||
months, sec = sec / 2419200, sec % 2419200
|
||||
weeks, sec = sec / 604800, sec % 604800
|
||||
days, sec = sec / 86400, sec % 86400
|
||||
hours, sec = sec / 3600, sec % 3600
|
||||
minutes, sec = sec / 60, sec % 60
|
||||
return (years, months, weeks, days, hours, minutes, sec)
|
||||
|
||||
|
||||
def gametime(format=False):
|
||||
"""
|
||||
Find the current in-game time (in seconds) since the start of the mud.
|
||||
|
|
@ -136,6 +140,7 @@ def gametime(format=False):
|
|||
return gametime_format(game_time)
|
||||
return game_time
|
||||
|
||||
|
||||
def runtime(format=False):
|
||||
"""
|
||||
Get the total actual time the server has been running (minus downtimes)
|
||||
|
|
@ -151,6 +156,7 @@ def runtime(format=False):
|
|||
return realtime_format(run_time)
|
||||
return run_time
|
||||
|
||||
|
||||
def uptime(format=False):
|
||||
"""
|
||||
Get the actual time the server has been running since last downtime.
|
||||
|
|
@ -170,31 +176,33 @@ def uptime(format=False):
|
|||
def gametime_to_realtime(secs=0, mins=0, hrs=0, days=0,
|
||||
weeks=0, months=0, yrs=0):
|
||||
"""
|
||||
This method helps to figure out the real-world time it will take until a in-game time
|
||||
has passed. E.g. if an event should take place a month later in-game, you will be able
|
||||
to find the number of real-world seconds this corresponds to (hint: Interval events deal
|
||||
with real life seconds).
|
||||
This method helps to figure out the real-world time it will take until an
|
||||
in-game time has passed. E.g. if an event should take place a month later
|
||||
in-game, you will be able to find the number of real-world seconds this
|
||||
corresponds to (hint: Interval events deal with real life seconds).
|
||||
|
||||
Example:
|
||||
gametime_to_realtime(days=2) -> number of seconds in real life from now after which
|
||||
2 in-game days will have passed.
|
||||
gametime_to_realtime(days=2) -> number of seconds in real life from
|
||||
now after which 2 in-game days will have passed.
|
||||
"""
|
||||
real_time = secs/TIMEFACTOR + mins*MIN + hrs*HOUR + \
|
||||
days*DAY + weeks*WEEK + months*MONTH + yrs*YEAR
|
||||
real_time = secs / TIMEFACTOR + mins * MIN + hrs * HOUR + \
|
||||
days * DAY + weeks * WEEK + months * MONTH + yrs * YEAR
|
||||
return real_time
|
||||
|
||||
|
||||
def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0,
|
||||
weeks=0, months=0, yrs=0):
|
||||
"""
|
||||
This method calculates how large an in-game time a real-world time interval would
|
||||
correspond to. This is usually a lot less interesting than the other way around.
|
||||
This method calculates how large an in-game time a real-world time
|
||||
interval would correspond to. This is usually a lot less interesting
|
||||
than the other way around.
|
||||
|
||||
Example:
|
||||
realtime_to_gametime(days=2) -> number of game-world seconds
|
||||
corresponding to 2 real days.
|
||||
"""
|
||||
game_time = TIMEFACTOR * (secs + mins*60 + hrs*3600 + days*86400 + \
|
||||
weeks*604800 + months*2419200 + yrs*29030400)
|
||||
game_time = TIMEFACTOR * (secs + mins * 60 + hrs * 3600 + days * 86400 +
|
||||
weeks * 604800 + months * 2419200 + yrs * 29030400)
|
||||
return game_time
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ a higher layer module.
|
|||
from traceback import format_exc
|
||||
from twisted.python import log
|
||||
|
||||
|
||||
def log_trace(errmsg=None):
|
||||
"""
|
||||
Log a traceback to the log. This should be called
|
||||
|
|
@ -27,7 +28,8 @@ def log_trace(errmsg=None):
|
|||
for line in errmsg.splitlines():
|
||||
log.msg('[EE] %s' % line)
|
||||
except Exception:
|
||||
log.msg('[EE] %s' % errmsg )
|
||||
log.msg('[EE] %s' % errmsg)
|
||||
|
||||
|
||||
def log_errmsg(errmsg):
|
||||
"""
|
||||
|
|
@ -43,6 +45,7 @@ def log_errmsg(errmsg):
|
|||
log.msg('[EE] %s' % line)
|
||||
#log.err('ERROR: %s' % (errormsg,))
|
||||
|
||||
|
||||
def log_warnmsg(warnmsg):
|
||||
"""
|
||||
Prints/logs any warnings that aren't critical but should be noted.
|
||||
|
|
@ -57,6 +60,7 @@ def log_warnmsg(warnmsg):
|
|||
log.msg('[WW] %s' % line)
|
||||
#log.msg('WARNING: %s' % (warnmsg,))
|
||||
|
||||
|
||||
def log_infomsg(infomsg):
|
||||
"""
|
||||
Prints any generic debugging/informative info that should appear in the log.
|
||||
|
|
@ -70,6 +74,7 @@ def log_infomsg(infomsg):
|
|||
for line in infomsg.splitlines():
|
||||
log.msg('[..] %s' % line)
|
||||
|
||||
|
||||
def log_depmsg(depmsg):
|
||||
"""
|
||||
Prints a deprecation message
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ Example: To reach the search method 'get_object_with_player'
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
# limit symbol import from API
|
||||
__all__ = ("search_object", "search_player", "search_script", "search_message", "search_channel", "search_help_entry")
|
||||
__all__ = ("search_object", "search_player", "search_script",
|
||||
"search_message", "search_channel", "search_help_entry")
|
||||
|
||||
|
||||
# import objects this way to avoid circular import problems
|
||||
|
|
@ -54,25 +55,29 @@ HelpEntry = ContentType.objects.get(app_label="help", model="helpentry").model_c
|
|||
# candidates=None,
|
||||
# exact=True):
|
||||
#
|
||||
# Search globally or in a list of candidates and return results. The result is always an Object.
|
||||
# Always returns a list.
|
||||
# Search globally or in a list of candidates and return results.
|
||||
# The result is always a list of Objects (or the empty list)
|
||||
#
|
||||
# Arguments:
|
||||
# ostring: (str) The string to compare names against. By default (if not attribute_name
|
||||
# is set), this will search object.key and object.aliases in order. Can also
|
||||
# be on the form #dbref, which will, if exact=True be matched against primary key.
|
||||
# attribute_name: (str): Use this named ObjectAttribute to match ostring against, instead
|
||||
# of the defaults.
|
||||
# typeclass (str or TypeClass): restrict matches to objects having this typeclass. This will help
|
||||
# speed up global searches.
|
||||
# candidates (list obj ObjectDBs): If supplied, search will only be performed among the candidates
|
||||
# in this list. A common list of candidates is the contents of the current location searched.
|
||||
# exact (bool): Match names/aliases exactly or partially. Partial matching matches the
|
||||
# beginning of words in the names/aliases, using a matching routine to separate
|
||||
# multiple matches in names with multiple components (so "bi sw" will match
|
||||
# "Big sword"). Since this is more expensive than exact matching, it is
|
||||
# recommended to be used together with the objlist keyword to limit the number
|
||||
# of possibilities. This value has no meaning if searching for attributes/properties.
|
||||
# ostring: (str) The string to compare names against. By default (if
|
||||
# not attribute_name is set), this will search object.key
|
||||
# and object.aliases in order. Can also be on the form #dbref,
|
||||
# which will, if exact=True be matched against primary key.
|
||||
# attribute_name: (str): Use this named ObjectAttribute to match ostring
|
||||
# against, instead of the defaults.
|
||||
# typeclass (str or TypeClass): restrict matches to objects having
|
||||
# this typeclass. This will help speed up global searches.
|
||||
# candidates (list obj ObjectDBs): If supplied, search will only be
|
||||
# performed among the candidates in this list. A common list
|
||||
# of candidates is the contents of the current location.
|
||||
# exact (bool): Match names/aliases exactly or partially. Partial
|
||||
# matching matches the beginning of words in the names/aliases,
|
||||
# using a matching routine to separate multiple matches in
|
||||
# names with multiple components (so "bi sw" will match
|
||||
# "Big sword"). Since this is more expensive than exact
|
||||
# matching, it is recommended to be used together with
|
||||
# the objlist keyword to limit the number of possibilities.
|
||||
# This keyword has no meaning if attribute_name is set.
|
||||
#
|
||||
# Returns:
|
||||
# A list of matching objects (or a list with one unique match)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
"""
|
||||
Test runner for Evennia test suite. Run with "game/manage.py test".
|
||||
Test runner for Evennia test suite. Run with "game/manage.py test".
|
||||
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.test.simple import DjangoTestSuiteRunner
|
||||
|
||||
|
||||
class EvenniaTestSuiteRunner(DjangoTestSuiteRunner):
|
||||
"""
|
||||
This test runner only runs tests on the apps specified in src/ and game/ to
|
||||
|
|
@ -13,11 +14,11 @@ class EvenniaTestSuiteRunner(DjangoTestSuiteRunner):
|
|||
"""
|
||||
def build_suite(self, test_labels, extra_tests=None, **kwargs):
|
||||
"""
|
||||
Build a test suite for Evennia. test_labels is a list of apps to test.
|
||||
Build a test suite for Evennia. test_labels is a list of apps to test.
|
||||
If not given, a subset of settings.INSTALLED_APPS will be used.
|
||||
"""
|
||||
if not test_labels:
|
||||
test_labels = [applabel.rsplit('.', 1)[1] for applabel in settings.INSTALLED_APPS
|
||||
test_labels = [applabel.rsplit('.', 1)[1] for applabel in settings.INSTALLED_APPS
|
||||
if (applabel.startswith('src.') or applabel.startswith('game.'))]
|
||||
return super(EvenniaTestSuiteRunner, self).build_suite(test_labels, extra_tests=extra_tests, **kwargs)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import re
|
|||
import cgi
|
||||
from ansi import *
|
||||
|
||||
|
||||
class TextToHTMLparser(object):
|
||||
"""
|
||||
This class describes a parser for converting from ansi to html.
|
||||
|
|
@ -36,10 +37,10 @@ class TextToHTMLparser(object):
|
|||
('purple', ANSI_MAGENTA),
|
||||
('cyan', hilite + ANSI_CYAN),
|
||||
('teal', ANSI_CYAN),
|
||||
('white', hilite + ANSI_WHITE), # pure white
|
||||
('gray', ANSI_WHITE), #light grey
|
||||
('dimgray', hilite + ANSI_BLACK), #dark grey
|
||||
('black', ANSI_BLACK), #pure black
|
||||
('white', hilite + ANSI_WHITE), # pure white
|
||||
('gray', ANSI_WHITE), # light grey
|
||||
('dimgray', hilite + ANSI_BLACK), # dark grey
|
||||
('black', ANSI_BLACK), # pure black
|
||||
]
|
||||
colorback = [
|
||||
('bgred', hilite + ANSI_BACK_RED),
|
||||
|
|
@ -61,8 +62,8 @@ class TextToHTMLparser(object):
|
|||
]
|
||||
|
||||
# make sure to escape [
|
||||
colorcodes = [(c, code.replace("[",r"\[")) for c, code in colorcodes]
|
||||
colorback = [(c, code.replace("[",r"\[")) for c, code in colorback]
|
||||
colorcodes = [(c, code.replace("[", r"\[")) for c, code in colorcodes]
|
||||
colorback = [(c, code.replace("[", r"\[")) for c, code in colorback]
|
||||
# create stop markers
|
||||
fgstop = [("", c.replace("[", r"\[")) for c in (normal, hilite, underline)]
|
||||
bgstop = [("", c.replace("[", r"\[")) for c in (normal,)]
|
||||
|
|
@ -74,7 +75,7 @@ class TextToHTMLparser(object):
|
|||
re_bgs = [(cname, re.compile("(?:%s)(.*?)(?=%s)" % (code, bgstop))) for cname, code in colorback]
|
||||
re_normal = re.compile(normal.replace("[", r"\["))
|
||||
re_hilite = re.compile("(?:%s)(.*)(?=%s)" % (hilite.replace("[", r"\["), fgstop))
|
||||
re_uline = re.compile("(?:%s)(.*?)(?=%s)" % (ANSI_UNDERLINE.replace("[",r"\["), fgstop))
|
||||
re_uline = re.compile("(?:%s)(.*?)(?=%s)" % (ANSI_UNDERLINE.replace("[", r"\["), fgstop))
|
||||
re_string = re.compile(r'(?P<htmlchars>[<&>])|(?P<space>^[ \t]+)|(?P<lineend>\r\n|\r|\n)', re.S|re.M|re.I)
|
||||
|
||||
def re_color(self, text):
|
||||
|
|
@ -126,9 +127,9 @@ class TextToHTMLparser(object):
|
|||
if c['lineend']:
|
||||
return '<br>'
|
||||
elif c['space'] == '\t':
|
||||
return ' '*self.tabstop
|
||||
return ' ' * self.tabstop
|
||||
elif c['space']:
|
||||
t = m.group().replace('\t', ' '*self.tabstop)
|
||||
t = m.group().replace('\t', ' ' * self.tabstop)
|
||||
t = t.replace(' ', ' ')
|
||||
return t
|
||||
|
||||
|
|
@ -155,6 +156,7 @@ class TextToHTMLparser(object):
|
|||
|
||||
HTML_PARSER = TextToHTMLparser()
|
||||
|
||||
|
||||
#
|
||||
# Access function
|
||||
#
|
||||
|
|
|
|||
|
|
@ -6,12 +6,19 @@ be of use when designing your own game.
|
|||
|
||||
"""
|
||||
|
||||
import os, sys, imp, types, math, re
|
||||
import textwrap, datetime, random, traceback, inspect
|
||||
import os
|
||||
import sys
|
||||
import imp
|
||||
import types
|
||||
import math
|
||||
import re
|
||||
import textwrap
|
||||
import datetime
|
||||
import random
|
||||
import traceback
|
||||
from inspect import ismodule
|
||||
from collections import defaultdict
|
||||
from twisted.internet import threads, defer, reactor
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.conf import settings
|
||||
|
||||
try:
|
||||
|
|
@ -24,6 +31,7 @@ _GA = object.__getattribute__
|
|||
_SA = object.__setattr__
|
||||
_DA = object.__delattr__
|
||||
|
||||
|
||||
def is_iter(iterable):
|
||||
"""
|
||||
Checks if an object behaves iterably. However,
|
||||
|
|
@ -39,10 +47,12 @@ def is_iter(iterable):
|
|||
except AttributeError:
|
||||
return False
|
||||
|
||||
|
||||
def make_iter(obj):
|
||||
"Makes sure that the object is always iterable."
|
||||
return not hasattr(obj, '__iter__') and [obj] or obj
|
||||
|
||||
|
||||
def fill(text, width=78, indent=0):
|
||||
"""
|
||||
Safely wrap text to a certain number of characters.
|
||||
|
|
@ -69,7 +79,8 @@ def crop(text, width=78, suffix="[...]"):
|
|||
return text
|
||||
else:
|
||||
lsuffix = len(suffix)
|
||||
return "%s%s" % (text[:width-lsuffix], suffix)
|
||||
return "%s%s" % (text[:width - lsuffix], suffix)
|
||||
|
||||
|
||||
def dedent(text):
|
||||
"""
|
||||
|
|
@ -83,6 +94,7 @@ def dedent(text):
|
|||
return ""
|
||||
return textwrap.dedent(text)
|
||||
|
||||
|
||||
def list_to_string(inlist, endsep="and", addquote=False):
|
||||
"""
|
||||
This pretty-formats a list as string output, adding
|
||||
|
|
@ -108,6 +120,7 @@ def list_to_string(inlist, endsep="and", addquote=False):
|
|||
return str(inlist[0])
|
||||
return ", ".join(str(v) for v in inlist[:-1]) + " %s %s" % (endsep, inlist[-1])
|
||||
|
||||
|
||||
def wildcard_to_regexp(instring):
|
||||
"""
|
||||
Converts a player-supplied string that may have wildcards in it to regular
|
||||
|
|
@ -123,7 +136,7 @@ def wildcard_to_regexp(instring):
|
|||
regexp_string += "^"
|
||||
|
||||
# Replace any occurances of * or ? with the appropriate groups.
|
||||
regexp_string += instring.replace("*","(.*)").replace("?", "(.{1})")
|
||||
regexp_string += instring.replace("*", "(.*)").replace("?", "(.{1})")
|
||||
|
||||
# If there's an asterisk at the end of the string, we can't impose the
|
||||
# end of string ($) limiter.
|
||||
|
|
@ -132,6 +145,7 @@ def wildcard_to_regexp(instring):
|
|||
|
||||
return regexp_string
|
||||
|
||||
|
||||
def time_format(seconds, style=0):
|
||||
"""
|
||||
Function to return a 'prettified' version of a value in seconds.
|
||||
|
|
@ -146,11 +160,11 @@ def time_format(seconds, style=0):
|
|||
# We'll just use integer math, no need for decimal precision.
|
||||
seconds = int(seconds)
|
||||
|
||||
days = seconds / 86400
|
||||
days = seconds / 86400
|
||||
seconds -= days * 86400
|
||||
hours = seconds / 3600
|
||||
hours = seconds / 3600
|
||||
seconds -= hours * 3600
|
||||
minutes = seconds / 60
|
||||
minutes = seconds / 60
|
||||
seconds -= minutes * 60
|
||||
|
||||
if style is 0:
|
||||
|
|
@ -225,6 +239,7 @@ def time_format(seconds, style=0):
|
|||
|
||||
return retval
|
||||
|
||||
|
||||
def datetime_format(dtobj):
|
||||
"""
|
||||
Takes a datetime object instance (e.g. from django's DateTimeField)
|
||||
|
|
@ -250,6 +265,7 @@ def datetime_format(dtobj):
|
|||
timestring = "%02i:%02i:%02i" % (hour, minute, second)
|
||||
return timestring
|
||||
|
||||
|
||||
def host_os_is(osname):
|
||||
"""
|
||||
Check to see if the host OS matches the query.
|
||||
|
|
@ -258,6 +274,7 @@ def host_os_is(osname):
|
|||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_evennia_version():
|
||||
"""
|
||||
Check for the evennia version info.
|
||||
|
|
@ -268,6 +285,7 @@ def get_evennia_version():
|
|||
except IOError:
|
||||
return "Unknown version"
|
||||
|
||||
|
||||
def pypath_to_realpath(python_path, file_ending='.py'):
|
||||
"""
|
||||
Converts a path on dot python form (e.g. 'src.objects.models') to
|
||||
|
|
@ -284,11 +302,13 @@ def pypath_to_realpath(python_path, file_ending='.py'):
|
|||
return "%s%s" % (path, file_ending)
|
||||
return path
|
||||
|
||||
|
||||
def dbref(dbref, reqhash=True):
|
||||
"""
|
||||
Converts/checks if input is a valid dbref.
|
||||
If reqhash is set, only input strings on the form '#N', where N is an integer
|
||||
is accepted. Otherwise strings '#N', 'N' and integers N are all accepted.
|
||||
If reqhash is set, only input strings on the form '#N', where N is an
|
||||
integer is accepted. Otherwise strings '#N', 'N' and integers N are all
|
||||
accepted.
|
||||
Output is the integer part.
|
||||
"""
|
||||
if reqhash:
|
||||
|
|
@ -301,6 +321,7 @@ def dbref(dbref, reqhash=True):
|
|||
return int(dbref) if dbref.isdigit() else None
|
||||
return dbref if isinstance(dbref, int) else None
|
||||
|
||||
|
||||
def to_unicode(obj, encoding='utf-8', force_string=False):
|
||||
"""
|
||||
This decodes a suitable object to the unicode format. Note that
|
||||
|
|
@ -335,6 +356,7 @@ def to_unicode(obj, encoding='utf-8', force_string=False):
|
|||
raise Exception("Error: '%s' contains invalid character(s) not in %s." % (obj, encoding))
|
||||
return obj
|
||||
|
||||
|
||||
def to_str(obj, encoding='utf-8', force_string=False):
|
||||
"""
|
||||
This encodes a unicode string back to byte-representation,
|
||||
|
|
@ -366,6 +388,7 @@ def to_str(obj, encoding='utf-8', force_string=False):
|
|||
raise Exception("Error: Unicode could not encode unicode string '%s'(%s) to a bytestring. " % (obj, encoding))
|
||||
return obj
|
||||
|
||||
|
||||
def validate_email_address(emailaddress):
|
||||
"""
|
||||
Checks if an email address is syntactically correct.
|
||||
|
|
@ -382,18 +405,18 @@ def validate_email_address(emailaddress):
|
|||
|
||||
# Email address must be more than 7 characters in total.
|
||||
if len(emailaddress) < 7:
|
||||
return False # Address too short.
|
||||
return False # Address too short.
|
||||
|
||||
# Split up email address into parts.
|
||||
try:
|
||||
localpart, domainname = emailaddress.rsplit('@', 1)
|
||||
host, toplevel = domainname.rsplit('.', 1)
|
||||
except ValueError:
|
||||
return False # Address does not have enough parts.
|
||||
return False # Address does not have enough parts.
|
||||
|
||||
# Check for Country code or Generic Domain.
|
||||
if len(toplevel) != 2 and toplevel not in domains:
|
||||
return False # Not a domain name.
|
||||
return False # Not a domain name.
|
||||
|
||||
for i in '-_.%+.':
|
||||
localpart = localpart.replace(i, "")
|
||||
|
|
@ -401,9 +424,9 @@ def validate_email_address(emailaddress):
|
|||
host = host.replace(i, "")
|
||||
|
||||
if localpart.isalnum() and host.isalnum():
|
||||
return True # Email address is fine.
|
||||
return True # Email address is fine.
|
||||
else:
|
||||
return False # Email address has funny characters.
|
||||
return False # Email address has funny characters.
|
||||
|
||||
|
||||
def inherits_from(obj, parent):
|
||||
|
|
@ -447,6 +470,7 @@ def server_services():
|
|||
del SESSIONS
|
||||
return server
|
||||
|
||||
|
||||
def uses_database(name="sqlite3"):
|
||||
"""
|
||||
Checks if the game is currently using a given database. This is a
|
||||
|
|
@ -460,13 +484,15 @@ def uses_database(name="sqlite3"):
|
|||
engine = settings.DATABASE_ENGINE
|
||||
return engine == "django.db.backends.%s" % name
|
||||
|
||||
|
||||
def delay(delay=2, retval=None, callback=None):
|
||||
"""
|
||||
Delay the return of a value.
|
||||
Inputs:
|
||||
delay (int) - the delay in seconds
|
||||
retval (any) - this will be returned by this function after a delay
|
||||
callback (func(retval)) - if given, this will be called with retval after delay seconds
|
||||
callback (func(retval)) - if given, this will be called with retval
|
||||
after delay seconds
|
||||
Returns:
|
||||
deferred that will fire with to_return after delay seconds
|
||||
"""
|
||||
|
|
@ -499,14 +525,15 @@ def clean_object_caches(obj):
|
|||
pass
|
||||
|
||||
# on-object property cache
|
||||
[_DA(obj, cname) for cname in obj.__dict__.keys() if cname.startswith("_cached_db_")]
|
||||
[_DA(obj, cname) for cname in obj.__dict__.keys()
|
||||
if cname.startswith("_cached_db_")]
|
||||
try:
|
||||
hashid = _GA(obj, "hashid")
|
||||
hasid = obj.hashid
|
||||
_TYPECLASSMODELS._ATTRIBUTE_CACHE[hashid] = {}
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
_PPOOL = None
|
||||
_PCMD = None
|
||||
_PROC_ERR = "A process has ended with a probable error condition: process ended by signal 9."
|
||||
|
|
@ -574,7 +601,6 @@ def run_async(to_execute, *args, **kwargs):
|
|||
deferred.addCallback(callback, **callback_kwargs)
|
||||
deferred.addErrback(errback, **errback_kwargs)
|
||||
|
||||
#
|
||||
|
||||
def check_evennia_dependencies():
|
||||
"""
|
||||
|
|
@ -631,7 +657,7 @@ def check_evennia_dependencies():
|
|||
if settings.IRC_ENABLED:
|
||||
try:
|
||||
import twisted.words
|
||||
twisted.words # set to avoid debug info about not-used import
|
||||
twisted.words # set to avoid debug info about not-used import
|
||||
except ImportError:
|
||||
errstring += "\n ERROR: IRC is enabled, but twisted.words is not installed. Please install it."
|
||||
errstring += "\n Linux Debian/Ubuntu users should install package 'python-twisted-words', others"
|
||||
|
|
@ -642,6 +668,7 @@ def check_evennia_dependencies():
|
|||
print "%s\n %s\n%s" % ("-"*78, errstring, '-'*78)
|
||||
return no_error
|
||||
|
||||
|
||||
def has_parent(basepath, obj):
|
||||
"Checks if basepath is somewhere in objs parent tree."
|
||||
try:
|
||||
|
|
@ -652,17 +679,19 @@ def has_parent(basepath, obj):
|
|||
# instance. Not sure if one should defend against this.
|
||||
return False
|
||||
|
||||
|
||||
def mod_import(module):
|
||||
"""
|
||||
A generic Python module loader.
|
||||
|
||||
Args:
|
||||
module - this can be either a Python path (dot-notation like src.objects.models),
|
||||
an absolute path (e.g. /home/eve/evennia/src/objects.models.py)
|
||||
module - this can be either a Python path (dot-notation like
|
||||
src.objects.models), an absolute path
|
||||
(e.g. /home/eve/evennia/src/objects.models.py)
|
||||
or an already import module object (e.g. models)
|
||||
Returns:
|
||||
an imported module. If the input argument was already a model, this is returned as-is,
|
||||
otherwise the path is parsed and imported.
|
||||
an imported module. If the input argument was already a model,
|
||||
this is returned as-is, otherwise the path is parsed and imported.
|
||||
Error:
|
||||
returns None. The error is also logged.
|
||||
"""
|
||||
|
|
@ -691,7 +720,7 @@ def mod_import(module):
|
|||
if not module:
|
||||
return None
|
||||
|
||||
if type(module) == types.ModuleType:
|
||||
if isinstance(module, types.ModuleType):
|
||||
# if this is already a module, we are done
|
||||
mod = module
|
||||
else:
|
||||
|
|
@ -699,8 +728,9 @@ def mod_import(module):
|
|||
try:
|
||||
mod = __import__(module, fromlist=["None"])
|
||||
except ImportError, ex:
|
||||
# check just where the ImportError happened (it could have been an erroneous
|
||||
# import inside the module as well). This is the trivial way to do it ...
|
||||
# check just where the ImportError happened (it could have been
|
||||
# an erroneous import inside the module as well). This is the
|
||||
# trivial way to do it ...
|
||||
if str(ex) != "Import by filename is not supported.":
|
||||
#log_trace("ImportError inside module '%s': '%s'" % (module, str(ex)))
|
||||
raise
|
||||
|
|
@ -726,29 +756,36 @@ def mod_import(module):
|
|||
result[0].close()
|
||||
return mod
|
||||
|
||||
|
||||
def all_from_module(module):
|
||||
"""
|
||||
Return all global-level variables from a module as a dict
|
||||
"""
|
||||
mod = mod_import(module)
|
||||
return dict((key, val) for key, val in mod.__dict__.items() if not (key.startswith("_") or ismodule(val)))
|
||||
return dict((key, val) for key, val in mod.__dict__.items()
|
||||
if not (key.startswith("_") or ismodule(val)))
|
||||
|
||||
|
||||
def variable_from_module(module, variable=None, default=None):
|
||||
"""
|
||||
Retrieve a variable or list of variables from a module. The variable(s) must be defined
|
||||
globally in the module. If no variable is given (or a list entry is None), a random variable
|
||||
is extracted from the module.
|
||||
Retrieve a variable or list of variables from a module. The variable(s)
|
||||
must be defined globally in the module. If no variable is given (or a
|
||||
list entry is None), a random variable is extracted from the module.
|
||||
|
||||
If module cannot be imported or given variable not found, default
|
||||
is returned.
|
||||
|
||||
Args:
|
||||
module (string or module)- python path, absolute path or a module
|
||||
variable (string or iterable) - single variable name or iterable of variable names to extract
|
||||
default (string) - default value to use if a variable fails to be extracted.
|
||||
variable (string or iterable) - single variable name or iterable of
|
||||
variable names to extract
|
||||
default (string) - default value to use if a variable fails
|
||||
to be extracted.
|
||||
Returns:
|
||||
a single value or a list of values depending on the type of 'variable' argument. Errors in lists
|
||||
are replaced by the 'default' argument."""
|
||||
a single value or a list of values depending on the type of
|
||||
'variable' argument. Errors in lists are replaced by the
|
||||
'default' argument.
|
||||
"""
|
||||
|
||||
if not module:
|
||||
return default
|
||||
|
|
@ -761,12 +798,14 @@ def variable_from_module(module, variable=None, default=None):
|
|||
result.append(mod.__dict__.get(var, default))
|
||||
else:
|
||||
# random selection
|
||||
mvars = [val for key, val in mod.__dict__.items() if not (key.startswith("_") or ismodule(val))]
|
||||
mvars = [val for key, val in mod.__dict__.items()
|
||||
if not (key.startswith("_") or ismodule(val))]
|
||||
result.append((mvars and random.choice(mvars)) or default)
|
||||
if len(result) == 1:
|
||||
return result[0]
|
||||
return result
|
||||
|
||||
|
||||
def string_from_module(module, variable=None, default=None):
|
||||
"""
|
||||
This is a wrapper for variable_from_module that requires return
|
||||
|
|
@ -779,6 +818,7 @@ def string_from_module(module, variable=None, default=None):
|
|||
return [(isinstance(v, basestring) and v or default) for v in val]
|
||||
return default
|
||||
|
||||
|
||||
def init_new_player(player):
|
||||
"""
|
||||
Helper method to call all hooks, set flags etc on a newly created
|
||||
|
|
@ -790,41 +830,50 @@ def init_new_player(player):
|
|||
# player.character.db.FIRST_LOGIN = True
|
||||
player.db.FIRST_LOGIN = True
|
||||
|
||||
|
||||
def string_similarity(string1, string2):
|
||||
"""
|
||||
This implements a "cosine-similarity" algorithm as described for example in
|
||||
Proceedings of the 22nd International Conference on Computation Linguistics
|
||||
(Coling 2008), pages 593-600, Manchester, August 2008
|
||||
The measure-vectors used is simply a "bag of words" type histogram (but for letters).
|
||||
Proceedings of the 22nd International Conference on Computation
|
||||
Linguistics (Coling 2008), pages 593-600, Manchester, August 2008
|
||||
The measure-vectors used is simply a "bag of words" type histogram
|
||||
(but for letters).
|
||||
|
||||
The function returns a value 0...1 rating how similar the two strings are. The strings can
|
||||
contain multiple words.
|
||||
The function returns a value 0...1 rating how similar the two strings
|
||||
are. The strings can contain multiple words.
|
||||
"""
|
||||
vocabulary = set(list(string1 + string2))
|
||||
vec1 = [string1.count(v) for v in vocabulary]
|
||||
vec2 = [string2.count(v) for v in vocabulary]
|
||||
try:
|
||||
return float(sum(vec1[i]*vec2[i] for i in range(len(vocabulary)))) / \
|
||||
return float(sum(vec1[i] * vec2[i] for i in range(len(vocabulary)))) / \
|
||||
(math.sqrt(sum(v1**2 for v1 in vec1)) * math.sqrt(sum(v2**2 for v2 in vec2)))
|
||||
except ZeroDivisionError:
|
||||
# can happen if empty-string cmdnames appear for some reason. This is a no-match.
|
||||
# can happen if empty-string cmdnames appear for some reason.
|
||||
# This is a no-match.
|
||||
return 0
|
||||
|
||||
|
||||
def string_suggestions(string, vocabulary, cutoff=0.6, maxnum=3):
|
||||
"""
|
||||
Given a string and a vocabulary, return a match or a list of suggestsion based on
|
||||
string similarity.
|
||||
Given a string and a vocabulary, return a match or a list of suggestsion
|
||||
based on string similarity.
|
||||
|
||||
Args:
|
||||
string (str)- a string to search for
|
||||
vocabulary (iterable) - a list of available strings
|
||||
cutoff (int, 0-1) - limit the similarity matches (higher, the more exact is required)
|
||||
cutoff (int, 0-1) - limit the similarity matches (higher, the more
|
||||
exact is required)
|
||||
maxnum (int) - maximum number of suggestions to return
|
||||
Returns:
|
||||
list of suggestions from vocabulary (could be empty if there are no matches)
|
||||
list of suggestions from vocabulary (could be empty if there are
|
||||
no matches)
|
||||
"""
|
||||
return [tup[1] for tup in sorted([(string_similarity(string, sugg), sugg) for sugg in vocabulary],
|
||||
key=lambda tup: tup[0], reverse=True) if tup[0] >= cutoff][:maxnum]
|
||||
return [tup[1] for tup in sorted([(string_similarity(string, sugg), sugg)
|
||||
for sugg in vocabulary],
|
||||
key=lambda tup: tup[0], reverse=True)
|
||||
if tup[0] >= cutoff][:maxnum]
|
||||
|
||||
|
||||
def string_partial_matching(alternatives, inp, ret_index=True):
|
||||
"""
|
||||
|
|
@ -837,7 +886,8 @@ def string_partial_matching(alternatives, inp, ret_index=True):
|
|||
Input:
|
||||
alternatives (list of str) - list of possible strings to match
|
||||
inp (str) - search criterion
|
||||
ret_index (bool) - return list of indices (from alternatives array) or strings
|
||||
ret_index (bool) - return list of indices (from alternatives
|
||||
array) or strings
|
||||
Returns:
|
||||
list of matching indices or strings, or an empty list
|
||||
|
||||
|
|
@ -855,7 +905,8 @@ def string_partial_matching(alternatives, inp, ret_index=True):
|
|||
# loop over parts, making sure only to visit each part once
|
||||
# (this will invalidate input in the wrong word order)
|
||||
submatch = [last_index + alt_num for alt_num, alt_word
|
||||
in enumerate(alt_words[last_index:]) if alt_word.startswith(inp_word)]
|
||||
in enumerate(alt_words[last_index:])
|
||||
if alt_word.startswith(inp_word)]
|
||||
if submatch:
|
||||
last_index = min(submatch) + 1
|
||||
score += 1
|
||||
|
|
@ -871,6 +922,7 @@ def string_partial_matching(alternatives, inp, ret_index=True):
|
|||
return matches[max(matches)]
|
||||
return []
|
||||
|
||||
|
||||
def format_table(table, extra_space=1):
|
||||
"""
|
||||
Note: src.utils.prettytable is more powerful than this, but this
|
||||
|
|
@ -909,6 +961,7 @@ def format_table(table, extra_space=1):
|
|||
for icol, col in enumerate(table)])
|
||||
return ftable
|
||||
|
||||
|
||||
def get_evennia_pids():
|
||||
"""
|
||||
Get the currently valids PIDs (Process IDs) of the Portal and Server
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue