Refactor code to remove alerts as per lgtm and #1176.

This commit is contained in:
Griatch 2017-01-29 19:02:00 +01:00
parent dcde526f6d
commit 74eebfed6d
54 changed files with 226 additions and 264 deletions

View file

@ -116,13 +116,6 @@ def _init():
Evennia has fully initialized all its models. It sets up the API Evennia has fully initialized all its models. It sets up the API
in a safe environment where all models are available already. in a safe environment where all models are available already.
""" """
def imp(path, variable=True):
"Helper function"
mod, fromlist = path, "None"
if variable:
mod, fromlist = path.rsplit('.', 1)
return __import__(mod, fromlist=[fromlist])
global DefaultPlayer, DefaultObject, DefaultGuest, DefaultCharacter global DefaultPlayer, DefaultObject, DefaultGuest, DefaultCharacter
global DefaultRoom, DefaultExit, DefaultChannel, DefaultScript global DefaultRoom, DefaultExit, DefaultChannel, DefaultScript
global ObjectDB, PlayerDB, ScriptDB, ChannelDB, Msg global ObjectDB, PlayerDB, ScriptDB, ChannelDB, Msg
@ -263,9 +256,9 @@ def _init():
def __init__(self): def __init__(self):
"populate the object with commands" "populate the object with commands"
def add_cmds(module): def add_cmds(module):
"helper method for populating this object with cmds" "helper method for populating this object with cmds"
from evennia.utils import utils
cmdlist = utils.variable_from_module(module, module.__all__) cmdlist = utils.variable_from_module(module, module.__all__)
self.__dict__.update(dict([(c.__name__, c) for c in cmdlist])) self.__dict__.update(dict([(c.__name__, c) for c in cmdlist]))

View file

@ -197,8 +197,6 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype):
""" """
try: try:
local_obj_cmdsets = [None]
@inlineCallbacks @inlineCallbacks
def _get_channel_cmdset(player_or_obj): def _get_channel_cmdset(player_or_obj):
""" """
@ -568,6 +566,7 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
sysarg = yield _SEARCH_AT_RESULT([match[2] for match in matches], caller, query=matches[0][0]) sysarg = yield _SEARCH_AT_RESULT([match[2] for match in matches], caller, query=matches[0][0])
raise ExecSystemCommand(syscmd, sysarg) raise ExecSystemCommand(syscmd, sysarg)
cmdname, args, cmd = "", "", None
if len(matches) == 1: if len(matches) == 1:
# We have a unique command match. But it may still be invalid. # We have a unique command match. But it may still be invalid.
match = matches[0] match = matches[0]

View file

@ -532,6 +532,7 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)):
for thiscmd in self.commands: for thiscmd in self.commands:
if thiscmd == cmd: if thiscmd == cmd:
return thiscmd return thiscmd
return None
def count(self): def count(self):
""" """

View file

@ -182,7 +182,6 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
#instantiate the cmdset (and catch its errors) #instantiate the cmdset (and catch its errors)
if callable(cmdsetclass): if callable(cmdsetclass):
cmdsetclass = cmdsetclass(cmdsetobj) cmdsetclass = cmdsetclass(cmdsetobj)
errstring = ""
return cmdsetclass return cmdsetclass
except ImportError as err: except ImportError as err:
logger.log_trace() logger.log_trace()
@ -213,6 +212,7 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
else: else:
errstring = errstring.format(path=python_path, traceback=err, timestamp=logger.timeformat()) errstring = errstring.format(path=python_path, traceback=err, timestamp=logger.timeformat())
break break
return None # an error
if errstring: if errstring:
# returning an empty error cmdset # returning an empty error cmdset

View file

@ -563,22 +563,6 @@ class CmdDesc(COMMAND_DEFAULT_CLASS):
if not obj: if not obj:
return return
def load(caller):
return caller.db.evmenu_target.db.desc or ""
def save(caller, buf):
"""
Save line buffer to the desc prop. This should
return True if successful and also report its status to the user.
"""
caller.db.evmenu_target.db.desc = buf
caller.msg("Saved.")
return True
def quit(caller):
caller.attributes.remove("evmenu_target")
caller.msg("Exited editor.")
self.caller.db.evmenu_target = obj self.caller.db.evmenu_target = obj
# launch the editor # launch the editor
EvEditor(self.caller, loadfunc=_desc_load, savefunc=_desc_save, quitfunc=_desc_quit, key="desc", persistent=True) EvEditor(self.caller, loadfunc=_desc_load, savefunc=_desc_save, quitfunc=_desc_quit, key="desc", persistent=True)
@ -1140,6 +1124,7 @@ class CmdName(ObjManipCommand):
caller.msg("Usage: @name <obj> = <newname>[;alias;alias;...]") caller.msg("Usage: @name <obj> = <newname>[;alias;alias;...]")
return return
obj = None
if self.lhs_objs: if self.lhs_objs:
objname = self.lhs_objs[0]['name'] objname = self.lhs_objs[0]['name']
if objname.startswith("*"): if objname.startswith("*"):
@ -1224,7 +1209,7 @@ class CmdOpen(ObjManipCommand):
if len(exit_obj) > 1: if len(exit_obj) > 1:
# give error message and return # give error message and return
caller.search(exit_name, location=location, exact=True) caller.search(exit_name, location=location, exact=True)
return return None
if exit_obj: if exit_obj:
exit_obj = exit_obj[0] exit_obj = exit_obj[0]
if not exit_obj.destination: if not exit_obj.destination:
@ -1315,11 +1300,11 @@ class CmdOpen(ObjManipCommand):
back_exit_name = self.lhs_objs[1]['name'] back_exit_name = self.lhs_objs[1]['name']
back_exit_aliases = self.lhs_objs[1]['aliases'] back_exit_aliases = self.lhs_objs[1]['aliases']
back_exit_typeclass = self.lhs_objs[1]['option'] back_exit_typeclass = self.lhs_objs[1]['option']
ok = self.create_exit(back_exit_name, self.create_exit(back_exit_name,
destination, destination,
location, location,
back_exit_aliases, back_exit_aliases,
back_exit_typeclass) back_exit_typeclass)
def _convert_from_string(cmd, strobj): def _convert_from_string(cmd, strobj):
@ -1634,6 +1619,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
if "show" in self.switches: if "show" in self.switches:
string = "%s's current typeclass is %s." % (obj.name, obj.__class__) string = "%s's current typeclass is %s." % (obj.name, obj.__class__)
caller.msg(string)
return return
if self.cmdstring == "@swap": if self.cmdstring == "@swap":

View file

@ -938,7 +938,7 @@ class CmdRSS2Chan(COMMAND_DEFAULT_CLASS):
return return
try: try:
import feedparser import feedparser
feedparser # to avoid checker error of not being used assert(feedparser) # to avoid checker error of not being used
except ImportError: except ImportError:
string = ("RSS requires python-feedparser (https://pypi.python.org/pypi/feedparser). " string = ("RSS requires python-feedparser (https://pypi.python.org/pypi/feedparser). "
"Install before continuing.") "Install before continuing.")

View file

@ -11,7 +11,7 @@ import datetime
import sys import sys
import django import django
import twisted import twisted
from time import time as timemeasure import time
from django.conf import settings from django.conf import settings
from evennia.server.sessionhandler import SESSIONS from evennia.server.sessionhandler import SESSIONS
@ -191,9 +191,9 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
duration = "" duration = ""
if "time" in self.switches: if "time" in self.switches:
t0 = timemeasure() t0 = time.time()
ret = eval(pycode_compiled, {}, available_vars) ret = eval(pycode_compiled, {}, available_vars)
t1 = timemeasure() t1 = time.time()
duration = " (runtime ~ %.4f ms)" % ((t1 - t0) * 1000) duration = " (runtime ~ %.4f ms)" % ((t1 - t0) * 1000)
else: else:
ret = eval(pycode_compiled, {}, available_vars) ret = eval(pycode_compiled, {}, available_vars)

View file

@ -67,6 +67,8 @@ def _throttle(session, maxlim=None, timeout=None, storage=_LATEST_FAILED_LOGINS)
# timeout has passed. Reset faillist # timeout has passed. Reset faillist
storage[address] = [] storage[address] = []
return False return False
else:
return False
else: else:
# store the time of the latest fail # store the time of the latest fail
storage[address].append(time.time()) storage[address].append(time.time())
@ -101,24 +103,28 @@ def create_guest_player(session):
try: try:
# Find an available guest name. # Find an available guest name.
for playername in settings.GUEST_LIST: playername = None
if not PlayerDB.objects.filter(username__iexact=playername): for name in settings.GUEST_LIST:
if not PlayerDB.objects.filter(username__iexact=playername).count():
playername = name
break break
playername = None if not playername:
if playername is None:
session.msg("All guest accounts are in use. Please try again later.") session.msg("All guest accounts are in use. Please try again later.")
return True, None return True, None
else:
# build a new player with the found guest playername
password = "%016x" % getrandbits(64)
home = ObjectDB.objects.get_id(settings.GUEST_HOME)
permissions = settings.PERMISSION_GUEST_DEFAULT
typeclass = settings.BASE_CHARACTER_TYPECLASS
ptypeclass = settings.BASE_GUEST_TYPECLASS
new_player = _create_player(session, playername, password,
permissions, ptypeclass)
if new_player:
_create_character(session, new_player, typeclass,
home, permissions)
return True, new_player
password = "%016x" % getrandbits(64)
home = ObjectDB.objects.get_id(settings.GUEST_HOME)
permissions = settings.PERMISSION_GUEST_DEFAULT
typeclass = settings.BASE_CHARACTER_TYPECLASS
ptypeclass = settings.BASE_GUEST_TYPECLASS
new_player = _create_player(session, playername, password,
permissions, ptypeclass)
if new_player:
_create_character(session, new_player, typeclass,
home, permissions)
except Exception: except Exception:
# We are in the middle between logged in and -not, so we have # We are in the middle between logged in and -not, so we have
@ -566,5 +572,3 @@ def _create_character(session, new_player, typeclass, home, permissions):
except Exception as e: except Exception as e:
session.msg("There was an error creating the Character:\n%s\n If this problem persists, contact an admin." % e) session.msg("There was an error creating the Character:\n%s\n If this problem persists, contact an admin." % e)
logger.log_trace() logger.log_trace()
return False

View file

@ -105,6 +105,7 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)):
mutelist.append(subscriber) mutelist.append(subscriber)
self.db.mute_list = mutelist self.db.mute_list = mutelist
return True return True
return False
def unmute(self, subscriber): def unmute(self, subscriber):
""" """
@ -117,6 +118,7 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)):
mutelist.remove(subscriber) mutelist.remove(subscriber)
self.db.mute_list = mutelist self.db.mute_list = mutelist
return True return True
return False
def connect(self, subscriber): def connect(self, subscriber):

View file

@ -31,13 +31,13 @@ class CommError(Exception):
# helper functions # helper functions
# #
def dbref(dbref, reqhash=True): def dbref(inp, reqhash=True):
""" """
Valid forms of dbref (database reference number) are either a Valid forms of dbref (database reference number) are either a
string '#N' or an integer N. string '#N' or an integer N.
Args: Args:
dbref (int or str): A possible dbref to check syntactically. inp (int or str): A possible dbref to check syntactically.
reqhash (bool): Require an initial hash `#` to accept. reqhash (bool): Require an initial hash `#` to accept.
Returns: Returns:
@ -45,16 +45,16 @@ def dbref(dbref, reqhash=True):
dbref, otherwise `None`. dbref, otherwise `None`.
""" """
if reqhash and not (isinstance(dbref, basestring) and dbref.startswith("#")): if reqhash and not (isinstance(inp, basestring) and inp.startswith("#")):
return None return None
if isinstance(dbref, basestring): if isinstance(inp, basestring):
dbref = dbref.lstrip('#') inp = inp.lstrip('#')
try: try:
if int(dbref) < 0: if int(inp) < 0:
return None return None
except Exception: except Exception:
return None return None
return dbref return inp
def identify_object(inp): def identify_object(inp):
@ -128,6 +128,8 @@ def to_object(inp, objtype='player'):
return _ChannelDB.objects.get(id=obj) return _ChannelDB.objects.get(id=obj)
logger.log_err("%s %s %s %s %s", objtype, inp, obj, typ, type(inp)) logger.log_err("%s %s %s %s %s", objtype, inp, obj, typ, type(inp))
raise CommError() raise CommError()
# an unknown
return None
# #
# Msg manager # Msg manager

View file

@ -314,6 +314,7 @@ class TradeHandler(object):
else: else:
raise ValueError raise ValueError
return self.finish() # try to close the deal return self.finish() # try to close the deal
return False
def decline(self, party): def decline(self, party):
""" """
@ -345,6 +346,7 @@ class TradeHandler(object):
return False return False
else: else:
raise ValueError raise ValueError
return False
def finish(self, force=False): def finish(self, force=False):
""" """
@ -354,6 +356,8 @@ class TradeHandler(object):
force (bool, optional): Force cleanup regardless of if the force (bool, optional): Force cleanup regardless of if the
trade was accepted or not (if not, no goods will change trade was accepted or not (if not, no goods will change
hands but trading will stop anyway) hands but trading will stop anyway)
Returns:
result (bool): If the finish was successful.
""" """
fin = False fin = False
@ -376,6 +380,7 @@ class TradeHandler(object):
if self.partB.ndb.tradehandler: if self.partB.ndb.tradehandler:
del self.partB.ndb.tradehandler del self.partB.ndb.tradehandler
return True return True
return False
# trading commands (will go into CmdsetTrade, initialized by the # trading commands (will go into CmdsetTrade, initialized by the

View file

@ -222,7 +222,6 @@ class ExtendedRoom(DefaultRoom):
description (str): Our description. description (str): Our description.
""" """
raw_desc = self.db.raw_desc or ""
update = False update = False
# get current time and season # get current time and season

View file

@ -208,7 +208,7 @@ def example2_build_forest(x, y, **kwargs):
"""A basic room""" """A basic room"""
# If on anything other than the first iteration - Do nothing. # If on anything other than the first iteration - Do nothing.
if kwargs["iteration"] > 0: if kwargs["iteration"] > 0:
return return None
room = create_object(rooms.Room, key="forest" + str(x) + str(y)) room = create_object(rooms.Room, key="forest" + str(x) + str(y))
room.db.desc = "Basic forest room." room.db.desc = "Basic forest room."
@ -227,11 +227,12 @@ def example2_build_verticle_exit(x, y, **kwargs):
north_room = kwargs["room_dict"][(x, y-1)] north_room = kwargs["room_dict"][(x, y-1)]
south_room = kwargs["room_dict"][(x, y+1)] south_room = kwargs["room_dict"][(x, y+1)]
north = create_object(exits.Exit, key="south", # create exits in the rooms
create_object(exits.Exit, key="south",
aliases=["s"], location=north_room, aliases=["s"], location=north_room,
destination=south_room) destination=south_room)
south = create_object(exits.Exit, key="north", create_object(exits.Exit, key="north",
aliases=["n"], location=south_room, aliases=["n"], location=south_room,
destination=north_room) destination=north_room)
@ -248,11 +249,11 @@ def example2_build_horizontal_exit(x, y, **kwargs):
west_room = kwargs["room_dict"][(x-1, y)] west_room = kwargs["room_dict"][(x-1, y)]
east_room = kwargs["room_dict"][(x+1, y)] east_room = kwargs["room_dict"][(x+1, y)]
west = create_object(exits.Exit, key="east", create_object(exits.Exit, key="east",
aliases=["e"], location=west_room, aliases=["e"], location=west_room,
destination=east_room) destination=east_room)
east = create_object(exits.Exit, key="west", create_object(exits.Exit, key="west",
aliases=["w"], location=east_room, aliases=["w"], location=east_room,
destination=west_room) destination=west_room)
@ -438,29 +439,29 @@ def build_map(caller, game_map, legend, iterations=1, build_exits=True):
# north # north
if (x, y-1) in room_dict: if (x, y-1) in room_dict:
if room_dict[(x, y-1)]: if room_dict[(x, y-1)]:
exit = create_object(exits.Exit, key="north", create_object(exits.Exit, key="north",
aliases=["n"], location=location, aliases=["n"], location=location,
destination=room_dict[(x, y-1)]) destination=room_dict[(x, y-1)])
# east # east
if (x+1, y) in room_dict: if (x+1, y) in room_dict:
if room_dict[(x+1, y)]: if room_dict[(x+1, y)]:
exit = create_object(exits.Exit, key="east", create_object(exits.Exit, key="east",
aliases=["e"], location=location, aliases=["e"], location=location,
destination=room_dict[(x+1, y)]) destination=room_dict[(x+1, y)])
# south # south
if (x, y+1) in room_dict: if (x, y+1) in room_dict:
if room_dict[(x, y+1)]: if room_dict[(x, y+1)]:
exit = create_object(exits.Exit, key="south", create_object(exits.Exit, key="south",
aliases=["s"], location=location, aliases=["s"], location=location,
destination=room_dict[(x, y+1)]) destination=room_dict[(x, y+1)])
# west # west
if (x-1, y) in room_dict: if (x-1, y) in room_dict:
if room_dict[(x-1, y)]: if room_dict[(x-1, y)]:
exit = create_object(exits.Exit, key="west", create_object(exits.Exit, key="west",
aliases=["w"], location=location, aliases=["w"], location=location,
destination=room_dict[(x-1, y)]) destination=room_dict[(x-1, y)])
caller.msg("Map Created.") caller.msg("Map Created.")

View file

@ -122,13 +122,13 @@ def username(caller, string_input):
}, },
{ {
"key": "_default", "key": "_default",
"goto": "password", "goto": "ask_password",
}, },
) )
return text, options return text, options
def password(caller, string_input): def ask_password(caller, string_input):
"""Ask the user to enter the password to this player. """Ask the user to enter the password to this player.
This is assuming the user exists (see 'create_username' and This is assuming the user exists (see 'create_username' and
@ -174,7 +174,7 @@ def password(caller, string_input):
}, },
{ {
"key": "_default", "key": "_default",
"goto": "password", "goto": "ask_password",
}, },
) )
elif banned: elif banned:

View file

@ -137,13 +137,6 @@ _RE_FLAGS = re.MULTILINE + re.IGNORECASE + re.UNICODE
_RE_PREFIX = re.compile(r"^%s" % _PREFIX, re.UNICODE) _RE_PREFIX = re.compile(r"^%s" % _PREFIX, re.UNICODE)
# The num_sep is the (single-character) symbol used to separate the
# sdesc from the number when trying to separate identical sdescs from
# one another. This is the same syntax used in the rest of Evennia, so
# by default, multiple "tall" can be separated by entering 1-tall,
# 2-tall etc.
_NUM_SEP = "-"
# This regex will return groups (num, word), where num is an optional counter to # This regex will return groups (num, word), where num is an optional counter to
# separate multimatches from one another and word is the first word in the # separate multimatches from one another and word is the first word in the
# marker. So entering "/tall man" will return groups ("", "tall") # marker. So entering "/tall man" will return groups ("", "tall")
@ -244,7 +237,7 @@ def ordered_permutation_regex(sentence):
solution.append(_PREFIX + r"[0-9]*%s*%s(?=\W|$)+" % (_NUM_SEP, re_escape(" ".join(comb)).rstrip("\\"))) solution.append(_PREFIX + r"[0-9]*%s*%s(?=\W|$)+" % (_NUM_SEP, re_escape(" ".join(comb)).rstrip("\\")))
# combine into a match regex, first matching the longest down to the shortest components # combine into a match regex, first matching the longest down to the shortest components
regex = r"|".join(sorted(set(solution), key=lambda o:len(o), reverse=True)) regex = r"|".join(sorted(set(solution), key=len, reverse=True))
return regex return regex
def regex_tuple_from_key_alias(obj): def regex_tuple_from_key_alias(obj):
@ -521,7 +514,7 @@ def send_emote(sender, receivers, emote, anonymous_add="first"):
receiver_lang_mapping[key] = process_language(saytext, sender, langname) receiver_lang_mapping[key] = process_language(saytext, sender, langname)
# map the language {##num} markers. This will convert the escaped sdesc markers on # map the language {##num} markers. This will convert the escaped sdesc markers on
# the form {{#num}} to {#num} markers ready to sdescmat in the next step. # the form {{#num}} to {#num} markers ready to sdescmat in the next step.
send_emote = emote.format(**receiver_lang_mapping) sendemote = emote.format(**receiver_lang_mapping)
# handle sdesc mappings. we make a temporary copy that we can modify # handle sdesc mappings. we make a temporary copy that we can modify
try: try:
@ -547,7 +540,7 @@ def send_emote(sender, receivers, emote, anonymous_add="first"):
receiver_sdesc_mapping[rkey] = process_sdesc(receiver.key, receiver) receiver_sdesc_mapping[rkey] = process_sdesc(receiver.key, receiver)
# do the template replacement of the sdesc/recog {#num} markers # do the template replacement of the sdesc/recog {#num} markers
receiver.msg(send_emote.format(**receiver_sdesc_mapping)) receiver.msg(sendemote.format(**receiver_sdesc_mapping))
#------------------------------------------------------------ #------------------------------------------------------------
# Handlers for sdesc and recog # Handlers for sdesc and recog
@ -1340,7 +1333,7 @@ class ContribRPObject(DefaultObject):
looker (Object): Object doing the looking. looker (Object): Object doing the looking.
""" """
if not looker: if not looker:
return return ""
# get and identify all objects # get and identify all objects
visible = (con for con in self.contents if con != looker and visible = (con for con in self.contents if con != looker and
con.access(looker, "view")) con.access(looker, "view"))

View file

@ -101,7 +101,7 @@ class CmdOpen(default_cmds.CmdOpen):
# we don't create a return exit if it was already created (because # we don't create a return exit if it was already created (because
# we created a door) # we created a door)
del self.return_exit_already_created del self.return_exit_already_created
return return None
# create a new exit as normal # create a new exit as normal
new_exit = super(CmdOpen, self).create_exit(exit_name, location, destination, new_exit = super(CmdOpen, self).create_exit(exit_name, location, destination,
exit_aliases=exit_aliases, typeclass=typeclass) exit_aliases=exit_aliases, typeclass=typeclass)

View file

@ -826,13 +826,14 @@ class CmdAttack(Command):
# call enemy hook # call enemy hook
if hasattr(target, "at_hit"): if hasattr(target, "at_hit"):
# should return True if target is defeated, False otherwise. # should return True if target is defeated, False otherwise.
return target.at_hit(self.obj, self.caller, damage) target.at_hit(self.obj, self.caller, damage)
return
elif target.db.health: elif target.db.health:
target.db.health -= damage target.db.health -= damage
else: else:
# sorry, impossible to fight this enemy ... # sorry, impossible to fight this enemy ...
self.caller.msg("The enemy seems unaffacted.") self.caller.msg("The enemy seems unaffacted.")
return False return
else: else:
self.caller.msg(string + "{rYou miss.{n") self.caller.msg(string + "{rYou miss.{n")
target.msg(tstring + "{gThey miss you.{n") target.msg(tstring + "{gThey miss you.{n")

View file

@ -191,7 +191,7 @@ class CmdTutorialLook(default_cmds.CmdLook):
caller.msg(looking_at_obj.return_appearance(caller)) caller.msg(looking_at_obj.return_appearance(caller))
# the object's at_desc() method. # the object's at_desc() method.
looking_at_obj.at_desc(looker=caller) looking_at_obj.at_desc(looker=caller)
return
class TutorialRoomCmdSet(CmdSet): class TutorialRoomCmdSet(CmdSet):

View file

@ -383,6 +383,7 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs):
accessing_obj = accessing_obj.obj accessing_obj = accessing_obj.obj
if hasattr(accessing_obj, "location"): if hasattr(accessing_obj, "location"):
return attr(accessing_obj.location, accessed_obj, *args, **kwargs) return attr(accessing_obj.location, accessed_obj, *args, **kwargs)
return False
def objlocattr(accessing_obj, accessed_obj, *args, **kwargs): def objlocattr(accessing_obj, accessed_obj, *args, **kwargs):
""" """
@ -402,6 +403,7 @@ def objlocattr(accessing_obj, accessed_obj, *args, **kwargs):
accessed_obj = accessed_obj.obj accessed_obj = accessed_obj.obj
if hasattr(accessed_obj, "location"): if hasattr(accessed_obj, "location"):
return attr(accessed_obj.location, accessed_obj, *args, **kwargs) return attr(accessed_obj.location, accessed_obj, *args, **kwargs)
return False
def attr_eq(accessing_obj, accessed_obj, *args, **kwargs): def attr_eq(accessing_obj, accessed_obj, *args, **kwargs):
@ -550,6 +552,7 @@ def holds(accessing_obj, accessed_obj, *args, **kwargs):
for obj in contents: for obj in contents:
if obj.attributes.get(args[0]) == args[1]: if obj.attributes.get(args[0]) == args[1]:
return True return True
return False
def superuser(*args, **kwargs): def superuser(*args, **kwargs):

View file

@ -525,9 +525,10 @@ class LockHandler(object):
else: else:
return self._eval_access_type( return self._eval_access_type(
accessing_obj, locks, access_type) accessing_obj, locks, access_type)
else:
for access_type in locks: # if no access types was given and multiple locks were
return self._eval_access_type(accessing_obj, locks, access_type) # embedded in the lockstring we assume all must be true
return all(self._eval_access_type(accessing_obj, locks, access_type) for access_type in locks)
def _test(): def _test():

View file

@ -278,6 +278,7 @@ class ObjectDB(TypedObject):
except Exception as e: except Exception as e:
errmsg = "Error (%s): %s is not a valid location." % (str(e), location) errmsg = "Error (%s): %s is not a valid location." % (str(e), location)
raise RuntimeError(errmsg) raise RuntimeError(errmsg)
return
def __location_del(self): def __location_del(self):
"Cleanly delete the location reference" "Cleanly delete the location reference"

View file

@ -645,6 +645,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
"Simple log helper method" "Simple log helper method"
logger.log_trace() logger.log_trace()
self.msg("%s%s" % (string, "" if err is None else " (%s)" % err)) self.msg("%s%s" % (string, "" if err is None else " (%s)" % err))
return
errtxt = _("Couldn't perform move ('%s'). Contact an admin.") errtxt = _("Couldn't perform move ('%s'). Contact an admin.")
if not emit_to_obj: if not emit_to_obj:
@ -1336,7 +1337,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
looker (Object): Object doing the looking. looker (Object): Object doing the looking.
""" """
if not looker: if not looker:
return return ""
# get and identify all objects # get and identify all objects
visible = (con for con in self.contents if con != looker and visible = (con for con in self.contents if con != looker and
con.access(looker, "view")) con.access(looker, "view"))
@ -1555,6 +1556,7 @@ class DefaultCharacter(DefaultObject):
idle = [session.cmd_last_visible for session in self.sessions.all()] idle = [session.cmd_last_visible for session in self.sessions.all()]
if idle: if idle:
return time.time() - float(max(idle)) return time.time() - float(max(idle))
return None
@property @property
def connection_time(self): def connection_time(self):
@ -1565,6 +1567,7 @@ class DefaultCharacter(DefaultObject):
conn = [session.conn_time for session in self.sessions.all()] conn = [session.conn_time for session in self.sessions.all()]
if conn: if conn:
return time.time() - float(min(conn)) return time.time() - float(min(conn))
return None
# #
# Base Room object # Base Room object

View file

@ -252,23 +252,4 @@ class PlayerDBAdmin(BaseUserAdmin):
return HttpResponseRedirect(reverse("admin:players_playerdb_change", args=[obj.id])) return HttpResponseRedirect(reverse("admin:players_playerdb_change", args=[obj.id]))
return HttpResponseRedirect(reverse("admin:players_playerdb_change", args=[obj.id])) return HttpResponseRedirect(reverse("admin:players_playerdb_change", args=[obj.id]))
## TODO! Remove User reference!
#def save_formset(self, request, form, formset, change):
# """
# Run all hooks on the player object
# """
# super(PlayerDBAdmin, self).save_formset(request, form, formset, change)
# userobj = form.instance
# userobj.name = userobj.username
# if not change:
# # uname, passwd, email = str(request.POST.get(u"username")), \
# # str(request.POST.get(u"password1")), \
# # str(request.POST.get(u"email"))
# typeclass = str(request.POST.get(
# u"playerdb_set-0-db_typeclass_path"))
# create.create_player("", "", "",
# user=userobj,
# typeclass=typeclass,
# player_dbobj=userobj)
admin.site.register(PlayerDB, PlayerDBAdmin) admin.site.register(PlayerDB, PlayerDBAdmin)

View file

@ -544,6 +544,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
idle = [session.cmd_last_visible for session in self.sessions.all()] idle = [session.cmd_last_visible for session in self.sessions.all()]
if idle: if idle:
return time.time() - float(max(idle)) return time.time() - float(max(idle))
return None
@property @property
def connection_time(self): def connection_time(self):
@ -554,6 +555,7 @@ class DefaultPlayer(with_metaclass(TypeclassBase, PlayerDB)):
conn = [session.conn_time for session in self.sessions.all()] conn = [session.conn_time for session in self.sessions.all()]
if conn: if conn:
return time.time() - float(min(conn)) return time.time() - float(min(conn))
return None
## player hooks ## player hooks

View file

@ -18,9 +18,6 @@ from future.utils import with_metaclass
__all__ = ["DefaultScript", "DoNothing", "Store"] __all__ = ["DefaultScript", "DoNothing", "Store"]
_GA = object.__getattribute__
_SESSIONS = None
class ExtendedLoopingCall(LoopingCall): class ExtendedLoopingCall(LoopingCall):
""" """
LoopingCall that can start at a delay different LoopingCall that can start at a delay different
@ -241,6 +238,7 @@ class DefaultScript(ScriptBase):
return maybeDeferred(self._step_callback).addErrback(self._step_errback) return maybeDeferred(self._step_callback).addErrback(self._step_errback)
except Exception: except Exception:
logger.log_trace() logger.log_trace()
return None
# Public methods # Public methods
@ -279,6 +277,7 @@ class DefaultScript(ScriptBase):
task = self.ndb._task task = self.ndb._task
if task: if task:
return max(0, self.db_repeats - task.callcount) return max(0, self.db_repeats - task.callcount)
return None
def start(self, force_restart=False): def start(self, force_restart=False):
""" """

View file

@ -73,7 +73,7 @@ from django.core.exceptions import ObjectDoesNotExist
from evennia.scripts.scripts import ExtendedLoopingCall from evennia.scripts.scripts import ExtendedLoopingCall
from evennia.server.models import ServerConfig from evennia.server.models import ServerConfig
from evennia.utils.logger import log_trace, log_err from evennia.utils.logger import log_trace, log_err
from evennia.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj, unpack_dbobj from evennia.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj
from evennia.utils import variable_from_module from evennia.utils import variable_from_module
_GA = object.__getattribute__ _GA = object.__getattribute__
@ -568,6 +568,7 @@ class TickerHandler(object):
ticker = self.ticker_pool.tickers.get(interval, None) ticker = self.ticker_pool.tickers.get(interval, None)
if ticker: if ticker:
return {interval: ticker.subscriptions} return {interval: ticker.subscriptions}
return None
def all_display(self): def all_display(self):
""" """

View file

@ -19,8 +19,8 @@ from __future__ import print_function
# imports needed on both server and portal side # imports needed on both server and portal side
import os import os
from time import time import time
from collections import defaultdict from collections import defaultdict, namedtuple
from itertools import count from itertools import count
from cStringIO import StringIO from cStringIO import StringIO
try: try:
@ -33,9 +33,7 @@ from twisted.internet.defer import Deferred
from evennia.utils import logger from evennia.utils import logger
from evennia.utils.utils import to_str, variable_from_module from evennia.utils.utils import to_str, variable_from_module
class DummySession(object): DUMMYSESSION = namedtuple('DummySession', ['sessid'])(0)
sessid = 0
DUMMYSESSION = DummySession()
# communication bits # communication bits
# (chr(9) and chr(10) are \t and \n, so skipping them) # (chr(9) and chr(10) are \t and \n, so skipping them)
@ -350,7 +348,7 @@ class AMPProtocol(amp.AMP):
""" """
self.send_batch_counter = 0 self.send_batch_counter = 0
self.send_reset_time = time() self.send_reset_time = time.time()
self.send_mode = True self.send_mode = True
self.send_task = None self.send_task = None

View file

@ -161,13 +161,13 @@ ERROR_SETTINGS = \
1) You are not running this command from your game directory. 1) You are not running this command from your game directory.
Change directory to your game directory and try again (or Change directory to your game directory and try again (or
create a new game directory using evennia --init <dirname>) create a new game directory using evennia --init <dirname>)
2) The settings file contains a syntax error. If you see a 2) The ettings file contains a syntax error. If you see a
traceback above, review it, resolve the problem and try again. traceback above, review it, resolve the problem and try again.
3) Django is not correctly installed. This usually shows as 3) Django is not correctly installed. This usually shows as
errors mentioning 'DJANGO_SETTINGS_MODULE'. If you run a errors mentioning 'DJANGO_SETTINGS_MODULE'. If you run a
virtual machine, it might be worth to restart it to see if virtual machine, it might be worth to restart it to see if
this resolves the issue. this resolves the issue.
""".format(settingsfile=SETTINGFILE, settingspath=SETTINGS_PATH) """.format(settingspath=SETTINGS_PATH)
ERROR_INITSETTINGS = \ ERROR_INITSETTINGS = \
""" """
@ -402,7 +402,6 @@ def evennia_version():
""" """
version = "Unknown" version = "Unknown"
try: try:
import evennia
version = evennia.__version__ version = evennia.__version__
except ImportError: except ImportError:
# even if evennia is not found, we should not crash here. # even if evennia is not found, we should not crash here.
@ -594,7 +593,6 @@ def check_database():
tables = [tableinfo.name for tableinfo in tables] tables = [tableinfo.name for tableinfo in tables]
if tables and u'players_playerdb' in tables: if tables and u'players_playerdb' in tables:
# database exists and seems set up. Initialize evennia. # database exists and seems set up. Initialize evennia.
import evennia
evennia._init() evennia._init()
# Try to get Player#1 # Try to get Player#1
from evennia.players.models import PlayerDB from evennia.players.models import PlayerDB
@ -668,6 +666,7 @@ def get_pid(pidfile):
with open(pidfile, 'r') as f: with open(pidfile, 'r') as f:
pid = f.read() pid = f.read()
return pid return pid
return None
def del_pid(pidfile): def del_pid(pidfile):
@ -684,7 +683,7 @@ def del_pid(pidfile):
os.remove(pidfile) os.remove(pidfile)
def kill(pidfile, signal=SIG, succmsg="", errmsg="", def kill(pidfile, killsignal=SIG, succmsg="", errmsg="",
restart_file=SERVER_RESTART, restart=False): restart_file=SERVER_RESTART, restart=False):
""" """
Send a kill signal to a process based on PID. A customized Send a kill signal to a process based on PID. A customized
@ -693,7 +692,7 @@ def kill(pidfile, signal=SIG, succmsg="", errmsg="",
Args: Args:
pidfile (str): The path of the pidfile to get the PID from. pidfile (str): The path of the pidfile to get the PID from.
signal (int, optional): Signal identifier. killsignal (int, optional): Signal identifier for signal to send.
succmsg (str, optional): Message to log on success. succmsg (str, optional): Message to log on success.
errmsg (str, optional): Message to log on failure. errmsg (str, optional): Message to log on failure.
restart_file (str, optional): Restart file location. restart_file (str, optional): Restart file location.
@ -728,7 +727,7 @@ def kill(pidfile, signal=SIG, succmsg="", errmsg="",
else: else:
# Linux can send the SIGINT signal directly # Linux can send the SIGINT signal directly
# to the specified PID. # to the specified PID.
os.kill(int(pid), signal) os.kill(int(pid), killsignal)
except OSError: except OSError:
print("Process %(pid)s cannot be stopped. "\ print("Process %(pid)s cannot be stopped. "\
@ -751,10 +750,8 @@ def show_version_info(about=False):
version_info (str): A complete version info string. version_info (str): A complete version info string.
""" """
import os
import sys import sys
import twisted import twisted
import django
return VERSION_INFO.format( return VERSION_INFO.format(
version=EVENNIA_VERSION, about=ABOUT_INFO if about else "", version=EVENNIA_VERSION, about=ABOUT_INFO if about else "",

View file

@ -56,28 +56,28 @@ def text(session, *args, **kwargs):
#from evennia.server.profiling.timetrace import timetrace #from evennia.server.profiling.timetrace import timetrace
#text = timetrace(text, "ServerSession.data_in") #text = timetrace(text, "ServerSession.data_in")
text = args[0] if args else None txt = args[0] if args else None
#explicitly check for None since text can be an empty string, which is #explicitly check for None since text can be an empty string, which is
#also valid #also valid
if text is None: if txt is None:
return return
# this is treated as a command input # this is treated as a command input
# handle the 'idle' command # handle the 'idle' command
if text.strip() in _IDLE_COMMAND: if txt.strip() in _IDLE_COMMAND:
session.update_session_counters(idle=True) session.update_session_counters(idle=True)
return return
if session.player: if session.player:
# nick replacement # nick replacement
puppet = session.puppet puppet = session.puppet
if puppet: if puppet:
text = puppet.nicks.nickreplace(text, txt = puppet.nicks.nickreplace(txt,
categories=("inputline", "channel"), include_player=True) categories=("inputline", "channel"), include_player=True)
else: else:
text = session.player.nicks.nickreplace(text, txt = session.player.nicks.nickreplace(txt,
categories=("inputline", "channel"), include_player=False) categories=("inputline", "channel"), include_player=False)
kwargs.pop("options", None) kwargs.pop("options", None)
cmdhandler(session, text, callertype="session", session=session, **kwargs) cmdhandler(session, txt, callertype="session", session=session, **kwargs)
session.update_session_counters() session.update_session_counters()
@ -92,20 +92,20 @@ def bot_data_in(session, *args, **kwargs):
""" """
text = args[0] if args else None txt = args[0] if args else None
# Explicitly check for None since text can be an empty string, which is # Explicitly check for None since text can be an empty string, which is
# also valid # also valid
if text is None: if txt is None:
return return
# this is treated as a command input # this is treated as a command input
# handle the 'idle' command # handle the 'idle' command
if text.strip() in _IDLE_COMMAND: if txt.strip() in _IDLE_COMMAND:
session.update_session_counters(idle=True) session.update_session_counters(idle=True)
return return
kwargs.pop("options", None) kwargs.pop("options", None)
# Trigger the execute_cmd method of the corresponding bot. # Trigger the execute_cmd method of the corresponding bot.
session.player.execute_cmd(text=text, session=session) session.player.execute_cmd(text=txt, session=session)
session.update_session_counters() session.update_session_counters()

View file

@ -49,4 +49,4 @@ class ServerConfigManager(models.Manager):
if not conf: if not conf:
return default return default
return conf[0].value return conf[0].value
return None

View file

@ -70,7 +70,6 @@ class Mssp(object):
""" """
self.protocol.handshake_done() self.protocol.handshake_done()
pass
def do_mssp(self, option): def do_mssp(self, option):
""" """

View file

@ -350,7 +350,6 @@ for plugin_module in PORTAL_SERVICES_PLUGIN_MODULES:
print('-' * 50) # end of terminal output print('-' * 50) # end of terminal output
if os.name == 'nt': if os.name == 'nt':
factory.noisy = False
# Windows only: Set PID file manually # Windows only: Set PID file manually
with open(PORTAL_PIDFILE, 'w') as f: with open(PORTAL_PIDFILE, 'w') as f:
f.write(str(os.getpid())) f.write(str(os.getpid()))

View file

@ -4,8 +4,8 @@ Sessionhandler for portal sessions
from __future__ import print_function from __future__ import print_function
from __future__ import division from __future__ import division
from time import time import time
from collections import deque from collections import deque, namedtuple
from twisted.internet import reactor from twisted.internet import reactor
from django.conf import settings from django.conf import settings
from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, \ from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, \
@ -26,9 +26,7 @@ _ERROR_MAX_CHAR = settings.MAX_CHAR_LIMIT_WARNING
_CONNECTION_QUEUE = deque() _CONNECTION_QUEUE = deque()
class DummySession(object): DUMMYSESSION = namedtuple('DummySession', ['sessid'])(0)
sessid = 0
DUMMYSESSION = DummySession()
#------------------------------------------------------------ #------------------------------------------------------------
# Portal-SessionHandler class # Portal-SessionHandler class
@ -53,13 +51,13 @@ class PortalSessionHandler(SessionHandler):
super(PortalSessionHandler, self).__init__(*args, **kwargs) super(PortalSessionHandler, self).__init__(*args, **kwargs)
self.portal = None self.portal = None
self.latest_sessid = 0 self.latest_sessid = 0
self.uptime = time() self.uptime = time.time()
self.connection_time = 0 self.connection_time = 0
self.connection_last = time() self.connection_last = self.uptime
self.connection_task = None self.connection_task = None
self.command_counter = 0 self.command_counter = 0
self.command_counter_reset = time() self.command_counter_reset = self.uptime
self.command_overflow = False self.command_overflow = False
def at_server_connection(self): def at_server_connection(self):
@ -68,7 +66,7 @@ class PortalSessionHandler(SessionHandler):
At this point, the AMP connection is already established. At this point, the AMP connection is already established.
""" """
self.connection_time = time() self.connection_time = time.time()
def connect(self, session): def connect(self, session):
""" """
@ -98,7 +96,7 @@ class PortalSessionHandler(SessionHandler):
session.data_out(text=[["%s DoS protection is active. You are queued to connect in %g seconds ..." % ( session.data_out(text=[["%s DoS protection is active. You are queued to connect in %g seconds ..." % (
settings.SERVERNAME, settings.SERVERNAME,
len(_CONNECTION_QUEUE)*_MIN_TIME_BETWEEN_CONNECTS)],{}]) len(_CONNECTION_QUEUE)*_MIN_TIME_BETWEEN_CONNECTS)],{}])
now = time() now = time.time()
if (now - self.connection_last < _MIN_TIME_BETWEEN_CONNECTS) or not self.portal.amp_protocol: if (now - self.connection_last < _MIN_TIME_BETWEEN_CONNECTS) or not self.portal.amp_protocol:
if not session or not self.connection_task: if not session or not self.connection_task:
self.connection_task = reactor.callLater(_MIN_TIME_BETWEEN_CONNECTS, self.connect, None) self.connection_task = reactor.callLater(_MIN_TIME_BETWEEN_CONNECTS, self.connect, None)
@ -257,7 +255,7 @@ class PortalSessionHandler(SessionHandler):
for session in self.values(): for session in self.values():
session.disconnect(reason) session.disconnect(reason)
del session del session
self = {} self.clear()
def server_logged_in(self, session, data): def server_logged_in(self, session, data):
""" """
@ -366,7 +364,7 @@ class PortalSessionHandler(SessionHandler):
# if there is a problem to send, we continue # if there is a problem to send, we continue
pass pass
if session: if session:
now = time() now = time.time()
if self.command_counter > _MAX_COMMAND_RATE: if self.command_counter > _MAX_COMMAND_RATE:
# data throttle (anti DoS measure) # data throttle (anti DoS measure)
dT = now - self.command_counter_reset dT = now - self.command_counter_reset

View file

@ -10,13 +10,13 @@ import sys
try: try:
import OpenSSL import OpenSSL
from twisted.internet import ssl as twisted_ssl from twisted.internet import ssl as twisted_ssl
except ImportError as err: except ImportError as error:
errstr = """ errstr = """
{err} {err}
SSL requires the PyOpenSSL library: SSL requires the PyOpenSSL library:
pip install pyopenssl pip install pyopenssl
""" """
raise ImportError(errstr.format(err=err)) raise ImportError(errstr.format(err=error))
from django.conf import settings from django.conf import settings
from evennia.server.portal.telnet import TelnetProtocol from evennia.server.portal.telnet import TelnetProtocol

View file

@ -184,7 +184,6 @@ class TelnetOOB(object):
msdp_args += "{msdp_array_open}" \ msdp_args += "{msdp_array_open}" \
"{msdp_args}" \ "{msdp_args}" \
"{msdp_array_close}".format( "{msdp_array_close}".format(
msdp_var=MSDP_VAR,
msdp_array_open=MSDP_ARRAY_OPEN, msdp_array_open=MSDP_ARRAY_OPEN,
msdp_array_close=MSDP_ARRAY_CLOSE, msdp_array_close=MSDP_ARRAY_CLOSE,
msdp_args= "".join("%s%s" % ( msdp_args= "".join("%s%s" % (

View file

@ -18,8 +18,8 @@ http://localhost:8000/webclient.)
""" """
import json import json
import re import re
import time
from time import time
from twisted.web import server, resource from twisted.web import server, resource
from twisted.internet.task import LoopingCall from twisted.internet.task import LoopingCall
from django.utils.functional import Promise from django.utils.functional import Promise
@ -83,7 +83,7 @@ class WebClient(resource.Resource):
""" """
Callback for checking the connection is still alive. Callback for checking the connection is still alive.
""" """
now = time() now = time.time()
to_remove = [] to_remove = []
keep_alives = ((csessid, remove) for csessid, (t, remove) keep_alives = ((csessid, remove) for csessid, (t, remove)
in self.last_alive.iteritems() if now - t > _KEEPALIVE) in self.last_alive.iteritems() if now - t > _KEEPALIVE)
@ -170,7 +170,7 @@ class WebClient(resource.Resource):
sess.sessionhandler.connect(sess) sess.sessionhandler.connect(sess)
self.last_alive[csessid] = (time(), False) self.last_alive[csessid] = (time.time(), False)
if not self.keep_alive: if not self.keep_alive:
# the keepalive is not running; start it. # the keepalive is not running; start it.
self.keep_alive = LoopingCall(self._keepalive) self.keep_alive = LoopingCall(self._keepalive)
@ -184,7 +184,7 @@ class WebClient(resource.Resource):
client is replying to the keepalive. client is replying to the keepalive.
""" """
csessid = request.args.get('csessid')[0] csessid = request.args.get('csessid')[0]
self.last_alive[csessid] = (time(), False) self.last_alive[csessid] = (time.time(), False)
return '""' return '""'
def mode_input(self, request): def mode_input(self, request):
@ -198,7 +198,7 @@ class WebClient(resource.Resource):
""" """
csessid = request.args.get('csessid')[0] csessid = request.args.get('csessid')[0]
self.last_alive[csessid] = (time(), False) self.last_alive[csessid] = (time.time(), False)
sess = self.sessionhandler.sessions_from_csessid(csessid) sess = self.sessionhandler.sessions_from_csessid(csessid)
if sess: if sess:
sess = sess[0] sess = sess[0]
@ -218,7 +218,7 @@ class WebClient(resource.Resource):
""" """
csessid = request.args.get('csessid')[0] csessid = request.args.get('csessid')[0]
self.last_alive[csessid] = (time(), False) self.last_alive[csessid] = (time.time(), False)
dataentries = self.databuffer.get(csessid, []) dataentries = self.databuffer.get(csessid, [])
if dataentries: if dataentries:
@ -244,7 +244,6 @@ class WebClient(resource.Resource):
sess.sessionhandler.disconnect(sess) sess.sessionhandler.disconnect(sess)
except IndexError: except IndexError:
self.client_disconnect(csessid) self.client_disconnect(csessid)
pass
return '""' return '""'
def render_POST(self, request): def render_POST(self, request):

View file

@ -2,7 +2,7 @@
Trace a message through the messaging system Trace a message through the messaging system
""" """
from __future__ import print_function from __future__ import print_function
from time import time import time
def timetrace(message, idstring, tracemessage="TEST_MESSAGE", final=False): def timetrace(message, idstring, tracemessage="TEST_MESSAGE", final=False):
""" """
@ -24,11 +24,11 @@ def timetrace(message, idstring, tracemessage="TEST_MESSAGE", final=False):
prefix, tlast, t0 = message.split(None, 2) prefix, tlast, t0 = message.split(None, 2)
tlast, t0 = float(tlast), float(t0) tlast, t0 = float(tlast), float(t0)
except (IndexError, ValueError): except (IndexError, ValueError):
t0 = time() t0 = time.time()
tlast = t0 tlast = t0
t1 = t0 t1 = t0
else: else:
t1 = time() t1 = time.time()
# print to log (important!) # print to log (important!)
print("** timetrace (%s): dT=%fs, total=%fs." % (idstring, t1-tlast, t1-t0)) print("** timetrace (%s): dT=%fs, total=%fs." % (idstring, t1-tlast, t1-t0))

View file

@ -8,10 +8,9 @@ are stored on the Portal side)
""" """
from builtins import object from builtins import object
import re
import weakref import weakref
import importlib import importlib
from time import time import time
from django.utils import timezone from django.utils import timezone
from django.conf import settings from django.conf import settings
from evennia.comms.models import ChannelDB from evennia.comms.models import ChannelDB
@ -218,7 +217,7 @@ class ServerSession(Session):
self.uid = self.player.id self.uid = self.player.id
self.uname = self.player.username self.uname = self.player.username
self.logged_in = True self.logged_in = True
self.conn_time = time() self.conn_time = time.time()
self.puid = None self.puid = None
self.puppet = None self.puppet = None
self.cmdset_storage = settings.CMDSET_SESSION self.cmdset_storage = settings.CMDSET_SESSION
@ -332,7 +331,7 @@ class ServerSession(Session):
""" """
# Idle time used for timeout calcs. # Idle time used for timeout calcs.
self.cmd_last = time() self.cmd_last = time.time()
# Store the timestamp of the user's last command. # Store the timestamp of the user's last command.
if not idle: if not idle:
@ -508,7 +507,7 @@ class ServerSession(Session):
""" """
string = "Cannot assign directly to ndb object! " string = "Cannot assign directly to ndb object! "
string = "Use ndb.attr=value instead." string += "Use ndb.attr=value instead."
raise Exception(string) raise Exception(string)
#@ndb.deleter #@ndb.deleter

View file

@ -12,10 +12,10 @@ There are two similar but separate stores of sessions:
handle network communication but holds no game info. handle network communication but holds no game info.
""" """
import time
from builtins import object from builtins import object
from future.utils import listvalues from future.utils import listvalues
from time import time
from django.conf import settings from django.conf import settings
from evennia.commands.cmdhandler import CMD_LOGINSTART from evennia.commands.cmdhandler import CMD_LOGINSTART
from evennia.utils.logger import log_trace from evennia.utils.logger import log_trace
@ -89,7 +89,10 @@ def delayed_import():
if not _ScriptDB: if not _ScriptDB:
from evennia.scripts.models import ScriptDB as _ScriptDB from evennia.scripts.models import ScriptDB as _ScriptDB
# including once to avoid warnings in Python syntax checkers # including once to avoid warnings in Python syntax checkers
_ServerSession, _PlayerDB, _ServerConfig, _ScriptDB assert(_ServerSession)
assert(_PlayerDB)
assert(_ServerConfig)
assert(_ScriptDB)
#----------------------------------------------------------- #-----------------------------------------------------------
@ -565,7 +568,7 @@ class ServerSessionHandler(SessionHandler):
see if any are dead or idle. see if any are dead or idle.
""" """
tcurr = time() tcurr = time.time()
reason = _("Idle timeout exceeded, disconnecting.") reason = _("Idle timeout exceeded, disconnecting.")
for session in (session for session in self.values() for session in (session for session in self.values()
if session.logged_in and _IDLE_TIMEOUT > 0 if session.logged_in and _IDLE_TIMEOUT > 0

View file

@ -46,6 +46,7 @@ def patched_new(cls, name, bases, attrs):
# Look for an application configuration to attach the model to. # Look for an application configuration to attach the model to.
app_config = apps.get_containing_app_config(module) app_config = apps.get_containing_app_config(module)
kwargs = {}
if getattr(meta, 'app_label', None) is None: if getattr(meta, 'app_label', None) is None:
if app_config is None: if app_config is None:
@ -69,8 +70,6 @@ def patched_new(cls, name, bases, attrs):
"else was imported before its application was loaded. " % "else was imported before its application was loaded. " %
(module, name)) (module, name))
raise RuntimeError(msg) raise RuntimeError(msg)
else:
kwargs = {}
new_class.add_to_class('_meta', Options(meta, **kwargs)) new_class.add_to_class('_meta', Options(meta, **kwargs))
if not abstract: if not abstract:

View file

@ -616,18 +616,6 @@ class TypedObject(SharedMemoryModel):
self.delete = self._deleted self.delete = self._deleted
super(TypedObject, self).delete() super(TypedObject, self).delete()
#
# Memory management
#
#def flush_from_cache(self):
# """
# Flush this object instance from cache, forcing an object reload.
# Note that this will kill all temporary attributes on this object
# since it will be recreated as a new Typeclass instance.
# """
# self.__class__.flush_cached_instance(self)
# #
# Attribute storage # Attribute storage
# #

View file

@ -351,10 +351,10 @@ class BatchCodeProcessor(object):
headers = [] headers = []
codes = [] codes = []
for imatch, match in enumerate(list(_RE_CODE_OR_HEADER.finditer(text))): for imatch, match in enumerate(list(_RE_CODE_OR_HEADER.finditer(text))):
type = match.group(1) mtype = match.group(1)
istart, iend = match.span(2) istart, iend = match.span(2)
code = text[istart:iend] code = text[istart:iend]
if type == "#HEADER": if mtype == "#HEADER":
headers.append(code) headers.append(code)
else: # either #CODE or matching from start of file else: # either #CODE or matching from start of file
codes.append(code) codes.append(code)

View file

@ -293,7 +293,7 @@ def create_message(senderobj, message, channels=None,
from evennia.comms.models import Msg as _Msg from evennia.comms.models import Msg as _Msg
if not message: if not message:
# we don't allow empty messages. # we don't allow empty messages.
return return None
new_message = _Msg(db_message=message) new_message = _Msg(db_message=message)
new_message.save() new_message.save()
for sender in make_iter(senderobj): for sender in make_iter(senderobj):

View file

@ -345,7 +345,7 @@ class EvForm(object):
for il, rectline in enumerate(rect): for il, rectline in enumerate(rect):
formline = form[iy0+il] formline = form[iy0+il]
# insert new content, replacing old # insert new content, replacing old
form[iy0+il] = formline = formline[:ix0] + rectline + formline[ix0+width:] form[iy0+il] = formline[:ix0] + rectline + formline[ix0+width:]
return form return form
def map(self, cells=None, tables=None, **kwargs): def map(self, cells=None, tables=None, **kwargs):

View file

@ -238,6 +238,7 @@ class CmdEvMenuNode(Command):
# this will create a completely new menu call # this will create a completely new menu call
EvMenu(caller, *saved_options[0], **saved_options[1]) EvMenu(caller, *saved_options[0], **saved_options[1])
return True return True
return None
caller = self.caller caller = self.caller
# we store Session on the menu since this can be hard to # we store Session on the menu since this can be hard to
@ -330,7 +331,6 @@ def evtable_options_formatter(optionlist, caller=None):
table.append(" |lc%s|lt|w%s|n|le: %s" % (raw_key, raw_key, desc)) table.append(" |lc%s|lt|w%s|n|le: %s" % (raw_key, raw_key, desc))
ncols = (_MAX_TEXT_WIDTH // table_width_max) + 1 # number of ncols ncols = (_MAX_TEXT_WIDTH // table_width_max) + 1 # number of ncols
nlastcol = nlist % ncols # number of elements left in last row
# get the amount of rows needed (start with 4 rows) # get the amount of rows needed (start with 4 rows)
nrows = 4 nrows = 4
@ -779,6 +779,7 @@ class EvMenu(object):
if isinstance(ret, basestring): if isinstance(ret, basestring):
# only return a value if a string (a goto target), ignore all other returns # only return a value if a string (a goto target), ignore all other returns
return ret return ret
return None
def goto(self, nodename, raw_string): def goto(self, nodename, raw_string):
""" """

View file

@ -157,8 +157,9 @@ class ANSITextWrapper(TextWrapper):
whitespace characters to spaces. Eg. " foo\tbar\n\nbaz" whitespace characters to spaces. Eg. " foo\tbar\n\nbaz"
becomes " foo bar baz". becomes " foo bar baz".
""" """
# ignore expand_tabs/replace_whitespace until ANSISTring handles them
return text return text
##TODO: Ignore expand_tabs/replace_whitespace until ANSISTring handles them.
## - don't remove this code. /Griatch
# if self.expand_tabs: # if self.expand_tabs:
# text = text.expandtabs() # text = text.expandtabs()
# if self.replace_whitespace: # if self.replace_whitespace:
@ -1269,9 +1270,8 @@ class EvTable(object):
for ix, col in enumerate(self.worktable): for ix, col in enumerate(self.worktable):
try: try:
col.reformat(width=cwidths[ix], **options) col.reformat(width=cwidths[ix], **options)
except Exception as e: except Exception:
msg = "ix=%s, width=%s: %s" % (ix, cwidths[ix], e.message) raise
raise #Exception ("Error in horizontal allign:\n %s" % msg)
# equalize heights for each row (we must do this here, since it may have changed to fit new widths) # equalize heights for each row (we must do this here, since it may have changed to fit new widths)
cheights = [max(cell.get_height() for cell in (col[iy] for col in self.worktable)) for iy in range(nrowmax)] cheights = [max(cell.get_height() for cell in (col[iy] for col in self.worktable)) for iy in range(nrowmax)]

View file

@ -6,8 +6,7 @@ in-mud time and real-world time as well allows to get the
total runtime of the server and the current uptime. total runtime of the server and the current uptime.
""" """
from __future__ import division from __future__ import division
import time
from time import time
from django.conf import settings from django.conf import settings
from evennia.server.models import ServerConfig from evennia.server.models import ServerConfig
@ -83,10 +82,10 @@ def runtime(format=False):
into time units. into time units.
""" """
runtime = SERVER_RUNTIME + (time() - SERVER_RUNTIME_LAST_UPDATED) rtime = SERVER_RUNTIME + (time.time() - SERVER_RUNTIME_LAST_UPDATED)
if format: if format:
return _format(runtime, 31536000, 2628000, 604800, 86400, 3600, 60) return _format(rtime, 31536000, 2628000, 604800, 86400, 3600, 60)
return runtime return rtime
def uptime(format=False): def uptime(format=False):
@ -101,10 +100,10 @@ def uptime(format=False):
into time units. into time units.
""" """
uptime = time() - SERVER_START_TIME utime = time.time() - SERVER_START_TIME
if format: if format:
return _format(uptime, 31536000, 2628000, 604800, 86400, 3600, 60) return _format(utime, 31536000, 2628000, 604800, 86400, 3600, 60)
return uptime return utime
def gametime(format=False): def gametime(format=False):
@ -119,10 +118,10 @@ def gametime(format=False):
into time units. into time units.
""" """
gametime = (runtime() - GAME_TIME_OFFSET) * TIMEFACTOR gtime = (runtime() - GAME_TIME_OFFSET) * TIMEFACTOR
if format: if format:
return _format(gametime, YEAR, MONTH, WEEK, DAY, HOUR, MIN) return _format(gtime, YEAR, MONTH, WEEK, DAY, HOUR, MIN)
return gametime return gtime
def reset_gametime(): def reset_gametime():
@ -159,11 +158,11 @@ def gametime_to_realtime(secs=0, mins=0, hrs=0, days=0,
now after which 2 in-game days will have passed. now after which 2 in-game days will have passed.
""" """
realtime = (secs + mins * MIN + hrs * HOUR + days * DAY + weeks * WEEK + \ rtime = (secs + mins * MIN + hrs * HOUR + days * DAY + weeks * WEEK + \
months * MONTH + yrs * YEAR) / TIMEFACTOR months * MONTH + yrs * YEAR) / TIMEFACTOR
if format: if format:
return _format(realtime, 31536000, 2628000, 604800, 86400, 3600, 60) return _format(rtime, 31536000, 2628000, 604800, 86400, 3600, 60)
return realtime return rtime
def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0, def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0,
@ -186,9 +185,9 @@ def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0,
corresponding to 2 real days. corresponding to 2 real days.
""" """
gametime = TIMEFACTOR * (secs + mins * 60 + hrs * 3600 + days * 86400 + gtime = TIMEFACTOR * (secs + mins * 60 + hrs * 3600 + days * 86400 +
weeks * 604800 + months * 2628000 + yrs * 31536000) weeks * 604800 + months * 2628000 + yrs * 31536000)
if format: if format:
return _format(gametime, YEAR, MONTH, WEEK, DAY, HOUR, MIN) return _format(gtime, YEAR, MONTH, WEEK, DAY, HOUR, MIN)
return gametime return gtime

View file

@ -292,6 +292,7 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)):
else: else:
cls._dbclass__.__instance_cache__[key].refresh_from_db() cls._dbclass__.__instance_cache__[key].refresh_from_db()
except KeyError: except KeyError:
# No need to remove if cache doesn't contain it already
pass pass
@classmethod @classmethod
@ -367,7 +368,7 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)):
""" """
global _MONITOR_HANDLER global _MONITOR_HANDLER
if not _MONITOR_HANDLER: if not _MONITOR_HANDLER:
from evennia.scripts.monitorhandler import MONITOR_HANDLER as _MONITORHANDLER from evennia.scripts.monitorhandler import MONITOR_HANDLER as _MONITOR_HANDLER
if _IS_SUBPROCESS: if _IS_SUBPROCESS:
# we keep a store of objects modified in subprocesses so # we keep a store of objects modified in subprocesses so
@ -398,7 +399,7 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)):
for field in update_fields: for field in update_fields:
fieldname = field.name fieldname = field.name
# trigger eventual monitors # trigger eventual monitors
_MONITORHANDLER.at_update(self, fieldname) _MONITOR_HANDLER.at_update(self, fieldname)
# if a hook is defined it must be named exactly on this form # if a hook is defined it must be named exactly on this form
hookname = "at_%s_postsave" % fieldname hookname = "at_%s_postsave" % fieldname
if hasattr(self, hookname) and callable(_GA(self, hookname)): if hasattr(self, hookname) and callable(_GA(self, hookname)):

View file

@ -249,6 +249,7 @@ def tail_log_file(filename, offset, nlines, callback=None):
lines_found = lines_found[-nlines-offset:-offset if offset else None] lines_found = lines_found[-nlines-offset:-offset if offset else None]
if callback: if callback:
callback(lines_found) callback(lines_found)
return None
else: else:
return lines_found return lines_found
@ -262,6 +263,5 @@ def tail_log_file(filename, offset, nlines, callback=None):
return deferToThread(seek_file, filehandle, offset, nlines, callback).addErrback(errback) return deferToThread(seek_file, filehandle, offset, nlines, callback).addErrback(errback)
else: else:
return seek_file(filehandle, offset, nlines, callback) return seek_file(filehandle, offset, nlines, callback)
else:
return None

View file

@ -439,6 +439,7 @@ class PrettyTable(object):
def _set_field_names(self, val): def _set_field_names(self, val):
val = [self._unicode(x) for x in val] val = [self._unicode(x) for x in val]
self._validate_option("field_names", val) self._validate_option("field_names", val)
old_names = []
if self._field_names: if self._field_names:
old_names = self._field_names[:] old_names = self._field_names[:]
self._field_names = val self._field_names = val

View file

@ -101,13 +101,13 @@ objects = search_objects
# #
# Search for players # Search for players
# #
# def player_search(self, ostring): # player_search(self, ostring)
# """
# Searches for a particular player by name or # Searches for a particular player by name or
# database id. # database id.
# #
# ostring = a string or database id. # ostring = a string or database id.
# """ #
search_player = PlayerDB.objects.player_search search_player = PlayerDB.objects.player_search
search_players = search_player search_players = search_player
@ -117,15 +117,15 @@ players = search_players
# #
# Searching for scripts # Searching for scripts
# #
# def script_search(self, ostring, obj=None, only_timed=False): # script_search(self, ostring, obj=None, only_timed=False)
# """ #
# Search for a particular script. # Search for a particular script.
# #
# ostring - search criterion - a script ID or key # ostring - search criterion - a script ID or key
# obj - limit search to scripts defined on this object # obj - limit search to scripts defined on this object
# only_timed - limit search only to scripts that run # only_timed - limit search only to scripts that run
# on a timer. # on a timer.
# """ #
search_script = ScriptDB.objects.script_search search_script = ScriptDB.objects.script_search
search_scripts = search_script search_scripts = search_script
@ -135,8 +135,8 @@ scripts = search_scripts
# Searching for communication messages # Searching for communication messages
# #
# #
# def message_search(self, sender=None, receiver=None, channel=None, freetext=None): # message_search(self, sender=None, receiver=None, channel=None, freetext=None)
# """ #
# Search the message database for particular messages. At least one # Search the message database for particular messages. At least one
# of the arguments must be given to do a search. # of the arguments must be given to do a search.
# #
@ -146,7 +146,7 @@ scripts = search_scripts
# freetext - Search for a text string in a message. # freetext - Search for a text string in a message.
# NOTE: This can potentially be slow, so make sure to supply # NOTE: This can potentially be slow, so make sure to supply
# one of the other arguments to limit the search. # one of the other arguments to limit the search.
# """ #
search_message = Msg.objects.message_search search_message = Msg.objects.message_search
search_messages = search_message search_messages = search_message
@ -156,13 +156,13 @@ messages = search_messages
# #
# Search for Communication Channels # Search for Communication Channels
# #
# def channel_search(self, ostring) # channel_search(self, ostring)
# """ #
# Search the channel database for a particular channel. # Search the channel database for a particular channel.
# #
# ostring - the key or database id of the channel. # ostring - the key or database id of the channel.
# exact - requires an exact ostring match (not case sensitive) # exact - requires an exact ostring match (not case sensitive)
# """ #
search_channel = Channel.objects.channel_search search_channel = Channel.objects.channel_search
search_channels = search_channel search_channels = search_channel
@ -172,13 +172,13 @@ channels = search_channels
# #
# Find help entry objects. # Find help entry objects.
# #
# def search_help(self, ostring, help_category=None): # search_help(self, ostring, help_category=None)
# """ #
# Retrieve a search entry object. # Retrieve a search entry object.
# #
# ostring - the help topic to look for # ostring - the help topic to look for
# category - limit the search to a particular help topic # category - limit the search to a particular help topic
# """ #
search_help = HelpEntry.objects.search_help search_help = HelpEntry.objects.search_help
search_help_entry = search_help search_help_entry = search_help

View file

@ -273,6 +273,7 @@ class TextToHTMLparser(object):
text = match.group().replace('\t', '&nbsp;' * self.tabstop) text = match.group().replace('\t', '&nbsp;' * self.tabstop)
text = text.replace(' ', '&nbsp;') text = text.replace(' ', '&nbsp;')
return text return text
return None
def parse(self, text, strip_ansi=False): def parse(self, text, strip_ansi=False):
""" """

View file

@ -559,6 +559,7 @@ class WebSocketProtocol(ProtocolWrapper):
if "\r\n" in self.buf: if "\r\n" in self.buf:
request, chaff, self.buf = self.buf.partition("\r\n") request, chaff, self.buf = self.buf.partition("\r\n")
try: try:
# verb and version are never used, maybe in the future.
verb, self.location, version = request.split(" ") verb, self.location, version = request.split(" ")
except ValueError: except ValueError:
self.loseConnection() self.loseConnection()

View file

@ -344,6 +344,8 @@ def time_format(seconds, style=0):
1. "1d" 1. "1d"
2. "1 day, 8 hours, 30 minutes" 2. "1 day, 8 hours, 30 minutes"
3. "1 day, 8 hours, 30 minutes, 10 seconds" 3. "1 day, 8 hours, 30 minutes, 10 seconds"
Returns:
timeformatted (str): A pretty time string.
""" """
if seconds < 0: if seconds < 0:
seconds = 0 seconds = 0
@ -358,7 +360,8 @@ def time_format(seconds, style=0):
minutes = seconds // 60 minutes = seconds // 60
seconds -= minutes * 60 seconds -= minutes * 60
if style is 0: retval = ""
if style == 0:
""" """
Standard colon-style output. Standard colon-style output.
""" """
@ -368,7 +371,7 @@ def time_format(seconds, style=0):
retval = '%02i:%02i' % (hours, minutes,) retval = '%02i:%02i' % (hours, minutes,)
return retval return retval
elif style is 1: elif style == 1:
""" """
Simple, abbreviated form that only shows the highest time amount. Simple, abbreviated form that only shows the highest time amount.
""" """
@ -380,7 +383,7 @@ def time_format(seconds, style=0):
return '%im' % (minutes,) return '%im' % (minutes,)
else: else:
return '%is' % (seconds,) return '%is' % (seconds,)
elif style is 2: elif style == 2:
""" """
Full-detailed, long-winded format. We ignore seconds. Full-detailed, long-winded format. We ignore seconds.
""" """
@ -403,7 +406,7 @@ def time_format(seconds, style=0):
else: else:
minutes_str = '%i minutes ' % minutes minutes_str = '%i minutes ' % minutes
retval = '%s%s%s' % (days_str, hours_str, minutes_str) retval = '%s%s%s' % (days_str, hours_str, minutes_str)
elif style is 3: elif style == 3:
""" """
Full-detailed, long-winded format. Includes seconds. Full-detailed, long-winded format. Includes seconds.
""" """
@ -541,12 +544,12 @@ def pypath_to_realpath(python_path, file_ending='.py', pypath_prefixes=None):
return list(set(p for p in paths if os.path.isfile(p))) return list(set(p for p in paths if os.path.isfile(p)))
def dbref(dbref, reqhash=True): def dbref(inp, reqhash=True):
""" """
Converts/checks if input is a valid dbref. Converts/checks if input is a valid dbref.
Args: Args:
dbref (int or str): A database ref on the form N or #N. inp (int, str): A database ref on the form N or #N.
reqhash (bool, optional): Require the #N form to accept reqhash (bool, optional): Require the #N form to accept
input as a valid dbref. input as a valid dbref.
@ -556,16 +559,16 @@ def dbref(dbref, reqhash=True):
""" """
if reqhash: if reqhash:
num = (int(dbref.lstrip('#')) if (isinstance(dbref, basestring) and num = (int(inp.lstrip('#')) if (isinstance(inp, basestring) and
dbref.startswith("#") and inp.startswith("#") and
dbref.lstrip('#').isdigit()) inp.lstrip('#').isdigit())
else None) else None)
return num if num > 0 else None return num if num > 0 else None
elif isinstance(dbref, basestring): elif isinstance(inp, basestring):
dbref = dbref.lstrip('#') inp = inp.lstrip('#')
return int(dbref) if dbref.isdigit() and int(dbref) > 0 else None return int(inp) if inp.isdigit() and int(inp) > 0 else None
else: else:
return dbref if isinstance(dbref, int) else None return inp if isinstance(inp, int) else None
def dbref_to_obj(inp, objclass, raise_errors=True): def dbref_to_obj(inp, objclass, raise_errors=True):
@ -882,27 +885,27 @@ def uses_database(name="sqlite3"):
return engine == "django.db.backends.%s" % name return engine == "django.db.backends.%s" % name
def delay(delay, callback, *args, **kwargs): def delay(timedelay, callback, *args, **kwargs):
""" """
Delay the return of a value. Delay the return of a value.
Args: Args:
delay (int or float): The delay in seconds timedelay (int or float): The delay in seconds
callback (callable): Will be called with optional callback (callable): Will be called with optional
arguments after `delay` seconds. arguments after `timedelay` seconds.
args (any, optional): Will be used as arguments to callback args (any, optional): Will be used as arguments to callback
Kwargs: Kwargs:
any (any): Will be used to call the callback. any (any): Will be used to call the callback.
Returns: Returns:
deferred (deferred): Will fire fire with callback after deferred (deferred): Will fire fire with callback after
`delay` seconds. Note that if `delay()` is used in the `timedelay` seconds. Note that if `timedelay()` is used in the
commandhandler callback chain, the callback chain can be commandhandler callback chain, the callback chain can be
defined directly in the command body and don't need to be defined directly in the command body and don't need to be
specified here. specified here.
""" """
return reactor.callLater(delay, callback, *args, **kwargs) return reactor.callLater(timedelay, callback, *args, **kwargs)
_TYPECLASSMODELS = None _TYPECLASSMODELS = None
@ -1107,7 +1110,7 @@ def mod_import(module):
result = imp.find_module(modname, [path]) result = imp.find_module(modname, [path])
except ImportError: except ImportError:
logger.log_trace("Could not find module '%s' (%s.py) at path '%s'" % (modname, modname, path)) logger.log_trace("Could not find module '%s' (%s.py) at path '%s'" % (modname, modname, path))
return return None
try: try:
mod = imp.load_module(modname, *result) mod = imp.load_module(modname, *result)
except ImportError: except ImportError:
@ -1643,7 +1646,7 @@ def calledby(callerdepth=1):
us. us.
""" """
import inspect, os import inspect
stack = inspect.stack() stack = inspect.stack()
# we must step one extra level back in stack since we don't want # we must step one extra level back in stack since we don't want
# to include the call of this function itself. # to include the call of this function itself.