From b2783371729349fb84eb6381a3cc1137b84589b5 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 19 Aug 2017 23:16:36 +0200 Subject: [PATCH] Make PEP8 cleanup of line spaces and character distances as well as indents --- bin/project_rename.py | 12 +- bin/windows/evennia_launcher.py | 5 +- evennia/__init__.py | 13 +- evennia/accounts/accounts.py | 11 +- evennia/accounts/admin.py | 15 +- evennia/accounts/bots.py | 3 + evennia/accounts/manager.py | 3 +- .../accounts/migrations/0002_move_defaults.py | 4 +- evennia/accounts/models.py | 2 +- evennia/commands/cmdhandler.py | 42 ++--- evennia/commands/cmdparser.py | 15 +- evennia/commands/cmdset.py | 14 +- evennia/commands/cmdsethandler.py | 39 ++-- evennia/commands/command.py | 2 +- evennia/commands/default/account.py | 20 +-- evennia/commands/default/admin.py | 8 +- evennia/commands/default/building.py | 146 +++++++-------- evennia/commands/default/cmdset_account.py | 2 +- evennia/commands/default/cmdset_character.py | 3 +- evennia/commands/default/cmdset_session.py | 1 + evennia/commands/default/comms.py | 6 +- evennia/commands/default/general.py | 3 +- evennia/commands/default/muxcommand.py | 2 + evennia/commands/default/system.py | 28 +-- evennia/commands/default/tests.py | 21 +-- evennia/commands/default/unloggedin.py | 12 +- evennia/commands/tests.py | 93 ++++++---- evennia/comms/admin.py | 2 +- evennia/comms/channelhandler.py | 28 +-- evennia/comms/comms.py | 1 - evennia/comms/managers.py | 7 +- .../migrations/0004_auto_20150118_1631.py | 4 +- .../migrations/0005_auto_20150223_1517.py | 4 +- .../migrations/0013_auto_20170705_1726.py | 70 ++++---- evennia/comms/models.py | 34 ++-- evennia/contrib/__init__.py | 2 +- evennia/contrib/barter.py | 3 + evennia/contrib/chargen.py | 3 +- evennia/contrib/clothing.py | 21 +-- evennia/contrib/color_markups.py | 84 ++++----- evennia/contrib/custom_gametime.py | 37 ++-- evennia/contrib/dice.py | 2 + evennia/contrib/egi_client/client.py | 2 + evennia/contrib/extended_room.py | 2 + evennia/contrib/gendersub.py | 19 +- .../contrib/ingame_python/callbackhandler.py | 8 +- evennia/contrib/ingame_python/commands.py | 99 ++++++----- evennia/contrib/ingame_python/eventfuncs.py | 3 + evennia/contrib/ingame_python/scripts.py | 65 +++---- evennia/contrib/ingame_python/tests.py | 77 ++++---- evennia/contrib/ingame_python/typeclasses.py | 27 +-- evennia/contrib/ingame_python/utils.py | 16 +- evennia/contrib/mail.py | 2 +- evennia/contrib/mapbuilder.py | 52 +++--- evennia/contrib/menu_login.py | 2 +- evennia/contrib/multidescer.py | 11 +- evennia/contrib/random_string_generator.py | 3 +- evennia/contrib/rplanguage.py | 19 +- evennia/contrib/rpsystem.py | 117 ++++++------ evennia/contrib/simpledoor.py | 6 +- evennia/contrib/slow_exit.py | 3 + evennia/contrib/talking_npc.py | 5 +- evennia/contrib/tests.py | 150 +++++++++++----- .../tutorial_examples/bodyfunctions.py | 3 +- .../tutorial_examples/cmdset_red_button.py | 6 +- .../tutorial_examples/example_batch_code.py | 8 +- .../contrib/tutorial_examples/red_button.py | 1 + .../tutorial_examples/red_button_scripts.py | 23 ++- evennia/contrib/tutorial_world/__init__.py | 1 - evennia/contrib/tutorial_world/mob.py | 15 +- evennia/contrib/tutorial_world/objects.py | 10 +- evennia/contrib/tutorial_world/rooms.py | 50 +++--- evennia/contrib/unixcommand.py | 4 +- .../game_template/commands/default_cmdsets.py | 1 + .../game_template/server/conf/at_search.py | 1 + .../game_template/server/conf/cmdparser.py | 1 + .../game_template/server/conf/inlinefuncs.py | 2 +- .../game_template/server/conf/lockfuncs.py | 2 +- evennia/game_template/server/conf/mssp.py | 140 +++++++-------- .../server/conf/serversession.py | 1 + .../game_template/server/conf/web_plugins.py | 1 - evennia/game_template/typeclasses/accounts.py | 1 + evennia/game_template/typeclasses/channels.py | 1 + .../game_template/typeclasses/characters.py | 3 +- evennia/game_template/typeclasses/exits.py | 1 + evennia/game_template/typeclasses/objects.py | 1 + evennia/game_template/world/prototypes.py | 10 +- evennia/help/admin.py | 11 +- evennia/help/manager.py | 1 + evennia/help/models.py | 4 +- evennia/locks/lockfuncs.py | 17 +- evennia/locks/lockhandler.py | 34 ++-- evennia/objects/admin.py | 16 +- evennia/objects/manager.py | 2 +- .../migrations/0004_auto_20150118_1622.py | 4 +- .../migrations/0008_auto_20170705_1736.py | 1 + evennia/objects/models.py | 7 +- evennia/objects/objects.py | 32 ++-- evennia/scripts/admin.py | 8 +- evennia/scripts/manager.py | 1 + .../migrations/0002_auto_20150118_1625.py | 4 +- .../migrations/0004_auto_20150306_1354.py | 2 + .../migrations/0006_auto_20150310_2249.py | 2 + evennia/scripts/models.py | 3 +- evennia/scripts/monitorhandler.py | 3 +- evennia/scripts/scripthandler.py | 8 +- evennia/scripts/scripts.py | 6 +- evennia/scripts/taskhandler.py | 20 +-- evennia/scripts/tests.py | 1 + evennia/scripts/tickerhandler.py | 27 +-- evennia/server/admin.py | 2 + evennia/server/amp.py | 8 +- evennia/server/deprecations.py | 13 +- evennia/server/evennia_launcher.py | 34 ++-- evennia/server/evennia_runner.py | 15 +- evennia/server/inputfuncs.py | 55 +++--- evennia/server/manager.py | 1 + evennia/server/portal/irc.py | 2 +- evennia/server/portal/mssp.py | 166 +++++++++--------- evennia/server/portal/mxp.py | 10 +- evennia/server/portal/naws.py | 17 +- evennia/server/portal/portal.py | 2 + evennia/server/portal/portalsessionhandler.py | 5 +- evennia/server/portal/rss.py | 3 + evennia/server/portal/ssh.py | 4 +- evennia/server/portal/ssl.py | 1 + evennia/server/portal/suppress_ga.py | 2 + evennia/server/portal/telnet.py | 7 +- evennia/server/portal/telnet_oob.py | 24 +-- evennia/server/portal/ttype.py | 5 +- evennia/server/portal/webclient.py | 3 +- evennia/server/portal/webclient_ajax.py | 7 +- evennia/server/profiling/dummyrunner.py | 22 ++- .../server/profiling/dummyrunner_settings.py | 90 ++++++---- evennia/server/profiling/memplot.py | 26 +-- evennia/server/profiling/settings_mixin.py | 4 +- evennia/server/profiling/test_queries.py | 8 +- evennia/server/profiling/timetrace.py | 5 +- evennia/server/server.py | 44 ++--- evennia/server/serversession.py | 3 + evennia/server/session.py | 4 +- evennia/server/sessionhandler.py | 48 ++--- evennia/server/tests.py | 1 + evennia/server/webserver.py | 3 + evennia/settings_default.py | 86 ++++----- evennia/typeclasses/__init__.py | 1 - evennia/typeclasses/admin.py | 1 + evennia/typeclasses/managers.py | 4 +- .../migrations/0005_auto_20160625_1812.py | 1 + ...o_add_dbmodel_value_for_tags_attributes.py | 3 +- .../0007_tag_migrations_may_be_slow.py | 1 + .../migrations/0008_lock_and_perm_rename.py | 9 +- .../0009_rename_player_cmdsets_typeclasses.py | 6 +- evennia/typeclasses/models.py | 24 ++- evennia/typeclasses/signals.py | 3 - evennia/typeclasses/tags.py | 38 ++-- evennia/utils/ansi.py | 21 ++- evennia/utils/batchprocessors.py | 2 + evennia/utils/create.py | 45 +++-- evennia/utils/dbserialize.py | 20 ++- evennia/utils/eveditor.py | 18 +- evennia/utils/evform.py | 61 ++++--- evennia/utils/evform_test.py | 2 - evennia/utils/evmenu.py | 18 +- evennia/utils/evmore.py | 17 +- evennia/utils/evtable.py | 22 +-- evennia/utils/gametime.py | 14 +- evennia/utils/idmapper/manager.py | 1 + evennia/utils/idmapper/models.py | 57 ++++-- evennia/utils/idmapper/tests.py | 9 +- evennia/utils/inlinefuncs.py | 16 +- evennia/utils/logger.py | 13 +- evennia/utils/picklefield.py | 13 +- evennia/utils/prettytable.py | 40 ++--- evennia/utils/search.py | 4 + evennia/utils/spawner.py | 56 +++--- evennia/utils/test_resources.py | 4 +- evennia/utils/tests.py | 32 ++-- evennia/utils/text2html.py | 89 +++++----- evennia/utils/txws.py | 23 ++- evennia/utils/utils.py | 76 ++++---- evennia/web/__init__.py | 1 - evennia/web/urls.py | 6 +- evennia/web/utils/backends.py | 70 ++++---- evennia/web/utils/general_context.py | 11 +- evennia/web/webclient/urls.py | 2 +- evennia/web/website/templatetags/addclass.py | 1 + evennia/web/website/urls.py | 20 +-- setup.py | 1 + 189 files changed, 2039 insertions(+), 1583 deletions(-) diff --git a/bin/project_rename.py b/bin/project_rename.py index 9d55bdcfc..e6176857d 100644 --- a/bin/project_rename.py +++ b/bin/project_rename.py @@ -47,6 +47,7 @@ manually later. # Helper functions + def _green(string): if USE_COLOR: return "%s%s%s" % (ANSI_GREEN, string, ANSI_NORMAL) @@ -98,12 +99,12 @@ def _case_sensitive_replace(string, old, new): all_upper = False # special cases - keep remaing case) if new_word.lower() in CASE_WORD_EXCEPTIONS: - result.append(new_word[ind+1:]) + result.append(new_word[ind + 1:]) # append any remaining characters from new elif all_upper: - result.append(new_word[ind+1:].upper()) + result.append(new_word[ind + 1:].upper()) else: - result.append(new_word[ind+1:].lower()) + result.append(new_word[ind + 1:].lower()) out.append("".join(result)) # if we have more new words than old ones, just add them verbatim out.extend([new_word for ind, new_word in enumerate(new_words) if ind >= len(old_words)]) @@ -278,7 +279,7 @@ def rename_in_file(path, in_list, out_list, is_interactive): raw_input(_HELP_TEXT.format(sources=in_list, targets=out_list)) elif ret.startswith("i"): # ignore one or more lines - ignores = [int(ind)-1 for ind in ret[1:].split(',') if ind.strip().isdigit()] + ignores = [int(ind) - 1 for ind in ret[1:].split(',') if ind.strip().isdigit()] if not ignores: raw_input("Ignore example: i 2,7,34,133\n (return to continue)") continue @@ -287,12 +288,11 @@ def rename_in_file(path, in_list, out_list, is_interactive): continue - if __name__ == "__main__": import argparse parser = argparse.ArgumentParser( - description="Rename text in a source tree, or a single file") + description="Rename text in a source tree, or a single file") parser.add_argument('-i', '--input', action='append', help="Source word to rename (quote around multiple words)") diff --git a/bin/windows/evennia_launcher.py b/bin/windows/evennia_launcher.py index 1784b05fe..ba11eb827 100755 --- a/bin/windows/evennia_launcher.py +++ b/bin/windows/evennia_launcher.py @@ -5,12 +5,13 @@ the python bin directory and makes the 'evennia' program available on the command %path%. """ -import os, sys +import os +import sys # for pip install -e sys.path.insert(0, os.path.abspath(os.getcwd())) # main library path sys.path.insert(0, os.path.join(sys.prefix, "Lib", "site-packages")) -from evennia.server.evennia_launcher import main +from evennia.server.evennia_launcher import main main() diff --git a/evennia/__init__.py b/evennia/__init__.py index b1442c498..6333b101e 100644 --- a/evennia/__init__.py +++ b/evennia/__init__.py @@ -109,9 +109,11 @@ def _create_version(): pass return version + __version__ = _create_version() del _create_version + def _init(): """ This function is called automatically by the launcher only after @@ -124,7 +126,7 @@ def _init(): global Command, CmdSet, default_cmds, syscmdkeys, InterruptCommand global search_object, search_script, search_account, search_channel, search_help, search_tag global create_object, create_script, create_account, create_channel, create_message, create_help_entry - global settings,lockfuncs, logger, utils, gametime, ansi, spawn, managers + global settings, lockfuncs, logger, utils, gametime, ansi, spawn, managers global contrib, TICKER_HANDLER, MONITOR_HANDLER, SESSION_HANDLER, CHANNEL_HANDLER, TASK_HANDLER from .accounts.accounts import DefaultAccount @@ -191,6 +193,7 @@ def _init(): Parent for other containers """ + def _help(self): "Returns list of contents" names = [name for name in self.__class__.__dict__ if not name.startswith('_')] @@ -198,7 +201,6 @@ def _init(): print(self.__doc__ + "-" * 60 + "\n" + ", ".join(names)) help = property(_help) - class DBmanagers(_EvContainer): """ Links to instantiated database managers. @@ -241,7 +243,6 @@ def _init(): managers = DBmanagers() del DBmanagers - class DefaultCmds(_EvContainer): """ This container holds direct shortcuts to all default commands in Evennia. @@ -266,8 +267,8 @@ def _init(): self.__dict__.update(dict([(c.__name__, c) for c in cmdlist])) from .commands.default import (admin, batchprocess, - building, comms, general, - account, help, system, unloggedin) + building, comms, general, + account, help, system, unloggedin) add_cmds(admin) add_cmds(building) add_cmds(batchprocess) @@ -282,7 +283,6 @@ def _init(): default_cmds = DefaultCmds() del DefaultCmds - class SystemCmds(_EvContainer): """ Creating commands with keys set to these constants will make @@ -313,6 +313,7 @@ def _init(): del SystemCmds del _EvContainer + del object del absolute_import del print_function diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 865aad57b..7e896e1c1 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -528,7 +528,7 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)): """ result = super(DefaultAccount, self).access(accessing_obj, access_type=access_type, - default=default, no_superuser_bypass=no_superuser_bypass) + default=default, no_superuser_bypass=no_superuser_bypass) self.at_access(result, accessing_obj, access_type, **kwargs) return result @@ -879,10 +879,10 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)): result.append(nsess == 1 and "\n\n|wConnected session:|n" or "\n\n|wConnected sessions (%i):|n" % nsess) for isess, sess in enumerate(sessions): csessid = sess.sessid - addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) - and str(sess.address[0]) or str(sess.address)) - result.append("\n %s %s" % (session.sessid == csessid and "|w* %s|n" % (isess + 1) - or " %s" % (isess + 1), addr)) + addr = "%s (%s)" % (sess.protocol_key, isinstance(sess.address, tuple) and + str(sess.address[0]) or str(sess.address)) + result.append("\n %s %s" % (session.sessid == csessid and "|w* %s|n" % (isess + 1) or + " %s" % (isess + 1), addr)) result.append("\n\n |whelp|n - more commands") result.append("\n |wooc |n - talk on public channel") @@ -928,6 +928,7 @@ class DefaultGuest(DefaultAccount): This class is used for guest logins. Unlike Accounts, Guests and their characters are deleted after disconnection. """ + def at_post_login(self, session=None, **kwargs): """ In theory, guests only have one character regardless of which diff --git a/evennia/accounts/admin.py b/evennia/accounts/admin.py index ce068dfd6..5678db112 100644 --- a/evennia/accounts/admin.py +++ b/evennia/accounts/admin.py @@ -155,7 +155,7 @@ class AccountInline(admin.StackedInline): fieldsets = ( ("In-game Permissions and Locks", {'fields': ('db_lock_storage',), - #{'fields': ('db_permissions', 'db_lock_storage'), + #{'fields': ('db_permissions', 'db_lock_storage'), 'description': "These are permissions/locks for in-game use. " "They are unrelated to website access rights."}), ("In-game Account data", @@ -215,11 +215,11 @@ class AccountDBAdmin(BaseUserAdmin): 'db_lock_storage'), 'description': 'These are attributes that are more relevant ' 'to gameplay.'})) - # ('Game Options', {'fields': ( - # 'db_typeclass_path', 'db_cmdset_storage', - # 'db_permissions', 'db_lock_storage'), - # 'description': 'These are attributes that are ' - # 'more relevant to gameplay.'})) + # ('Game Options', {'fields': ( + # 'db_typeclass_path', 'db_cmdset_storage', + # 'db_permissions', 'db_lock_storage'), + # 'description': 'These are attributes that are ' + # 'more relevant to gameplay.'})) add_fieldsets = ( (None, @@ -240,7 +240,7 @@ class AccountDBAdmin(BaseUserAdmin): """ obj.save() if not change: - #calling hooks for new account + # calling hooks for new account obj.set_class_from_typeclass(typeclass_path=settings.BASE_ACCOUNT_TYPECLASS) obj.basetype_setup() obj.at_account_creation() @@ -252,4 +252,5 @@ class AccountDBAdmin(BaseUserAdmin): return HttpResponseRedirect(reverse("admin:accounts_accountdb_change", args=[obj.id])) return HttpResponseRedirect(reverse("admin:accounts_accountdb_change", args=[obj.id])) + admin.site.register(AccountDB, AccountDBAdmin) diff --git a/evennia/accounts/bots.py b/evennia/accounts/bots.py index 091370ef0..642673bef 100644 --- a/evennia/accounts/bots.py +++ b/evennia/accounts/bots.py @@ -28,6 +28,7 @@ class BotStarter(DefaultScript): into gear when it is initialized. """ + def at_script_creation(self): """ Called once, when script is created. @@ -148,6 +149,7 @@ class IRCBot(Bot): Bot for handling IRC connections. """ + def start(self, ev_channel=None, irc_botname=None, irc_channel=None, irc_network=None, irc_port=None, irc_ssl=None): """ Start by telling the portal to start a new session. @@ -359,6 +361,7 @@ class RSSBot(Bot): its feed at regular intervals. """ + def start(self, ev_channel=None, rss_url=None, rss_rate=None): """ Start by telling the portal to start a new RSS session diff --git a/evennia/accounts/manager.py b/evennia/accounts/manager.py index 8156f0395..c612cf930 100644 --- a/evennia/accounts/manager.py +++ b/evennia/accounts/manager.py @@ -38,6 +38,7 @@ class AccountDBManager(TypedObjectManager, UserManager): #swap_character """ + def num_total_accounts(self): """ Get total number of accounts. @@ -91,7 +92,7 @@ class AccountDBManager(TypedObjectManager, UserManager): tdelta = datetime.timedelta(days) start_date = end_date - tdelta return self.filter(last_login__range=( - start_date, end_date)).order_by('-last_login') + start_date, end_date)).order_by('-last_login') def get_account_from_email(self, uemail): """ diff --git a/evennia/accounts/migrations/0002_move_defaults.py b/evennia/accounts/migrations/0002_move_defaults.py index 9fc8f0d4c..461525a4c 100644 --- a/evennia/accounts/migrations/0002_move_defaults.py +++ b/evennia/accounts/migrations/0002_move_defaults.py @@ -3,12 +3,14 @@ from __future__ import unicode_literals from django.db import models, migrations + def convert_defaults(apps, schema_editor): AccountDB = apps.get_model("accounts", "AccountDB") for account in AccountDB.objects.filter(db_typeclass_path="src.accounts.account.Account"): account.db_typeclass_path = "typeclasses.accounts.Account" account.save() + class Migration(migrations.Migration): dependencies = [ @@ -16,5 +18,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(convert_defaults), + migrations.RunPython(convert_defaults), ] diff --git a/evennia/accounts/models.py b/evennia/accounts/models.py index 6b09064f1..bf391b2d8 100644 --- a/evennia/accounts/models.py +++ b/evennia/accounts/models.py @@ -89,7 +89,7 @@ class AccountDB(TypedObject, AbstractUser): help_text="If player is connected to game or not") # database storage of persistant cmdsets. db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True, - help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.") + help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_CHARACTER.") # marks if this is a "virtual" bot account object db_is_bot = models.BooleanField(default=False, verbose_name="is_bot", help_text="Used to identify irc/rss bots") diff --git a/evennia/commands/cmdhandler.py b/evennia/commands/cmdhandler.py index a7299df01..016bbee09 100644 --- a/evennia/commands/cmdhandler.py +++ b/evennia/commands/cmdhandler.py @@ -88,41 +88,41 @@ _SEARCH_AT_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit # is the normal "production message to echo to the account. _ERROR_UNTRAPPED = ( -""" + """ An untrapped error occurred. """, -""" + """ An untrapped error occurred. Please file a bug report detailing the steps to reproduce. """) _ERROR_CMDSETS = ( -""" + """ A cmdset merger-error occurred. This is often due to a syntax error in one of the cmdsets to merge. """, -""" + """ A cmdset merger-error occurred. Please file a bug report detailing the steps to reproduce. """) _ERROR_NOCMDSETS = ( -""" + """ No command sets found! This is a critical bug that can have multiple causes. """, -""" + """ No command sets found! This is a sign of a critical bug. If disconnecting/reconnecting doesn't" solve the problem, try to contact the server admin through" some other means for assistance. """) _ERROR_CMDHANDLER = ( -""" + """ A command handler bug occurred. If this is not due to a local change, please file a bug report with the Evennia project, including the traceback and steps to reproduce. """, -""" + """ A command handler bug occurred. Please notify staff - they should likely file a bug report with the Evennia project. """) @@ -234,19 +234,23 @@ class NoCmdSets(Exception): class ExecSystemCommand(Exception): "Run a system command" + def __init__(self, syscmd, sysarg): self.args = (syscmd, sysarg) # needed by exception error handling self.syscmd = syscmd self.sysarg = sysarg + class ErrorReported(Exception): "Re-raised when a subsructure already reported the error" + def __init__(self, raw_string): self.args = (raw_string,) self.raw_string = raw_string # Helper function + @inlineCallbacks def get_and_merge_cmdsets(caller, session, account, obj, callertype, raw_string): """ @@ -318,11 +322,11 @@ def get_and_merge_cmdsets(caller, session, account, obj, callertype, raw_string) # the no_superuser_bypass must be True) local_obj_cmdsets = \ yield list(chain.from_iterable( - lobj.cmdset.cmdset_stack for lobj in local_objlist - if (lobj.cmdset.current and + lobj.cmdset.cmdset_stack for lobj in local_objlist + if (lobj.cmdset.current and lobj.access(caller, access_type='call', no_superuser_bypass=True)))) for cset in local_obj_cmdsets: - #This is necessary for object sets, or we won't be able to + # This is necessary for object sets, or we won't be able to # separate the command sets from each other in a busy room. We # only keep the setting if duplicates were set to False/True # explicitly. @@ -333,7 +337,6 @@ def get_and_merge_cmdsets(caller, session, account, obj, callertype, raw_string) _msg_err(caller, _ERROR_CMDSETS) raise ErrorReported(raw_string) - @inlineCallbacks def _get_cmdsets(obj): """ @@ -346,7 +349,7 @@ def get_and_merge_cmdsets(caller, session, account, obj, callertype, raw_string) _msg_err(caller, _ERROR_CMDSETS) raise ErrorReported(raw_string) try: - returnValue((obj.cmdset.current, list(obj.cmdset.cmdset_stack))) + returnValue((obj.cmdset.current, list(obj.cmdset.cmdset_stack))) except AttributeError: returnValue(((None, None, None), [])) @@ -550,9 +553,9 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess cmd.session = session cmd.account = account cmd.raw_string = unformatted_raw_string - #cmd.obj # set via on-object cmdset handler for each command, - # since this may be different for every command when - # merging multuple cmdsets + # cmd.obj # set via on-object cmdset handler for each command, + # since this may be different for every command when + # merging multuple cmdsets if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'): # cmd.obj is automatically made available by the cmdhandler. @@ -615,7 +618,6 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess finally: _COMMAND_NESTING[called_by] -= 1 - raw_string = to_unicode(raw_string, force_string=True) session, account, obj = session, None, None @@ -653,7 +655,7 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess else: # no explicit cmdobject given, figure it out cmdset = yield get_and_merge_cmdsets(caller, session, account, obj, - callertype, raw_string) + callertype, raw_string) if not cmdset: # this is bad and shouldn't happen. raise NoCmdSets @@ -701,8 +703,8 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess # fallback to default error text sysarg = _("Command '%s' is not available.") % raw_string suggestions = string_suggestions(raw_string, - cmdset.get_all_cmd_keys_and_aliases(caller), - cutoff=0.7, maxnum=3) + cmdset.get_all_cmd_keys_and_aliases(caller), + cutoff=0.7, maxnum=3) if suggestions: sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True) else: diff --git a/evennia/commands/cmdparser.py b/evennia/commands/cmdparser.py index 100e19311..e104233e4 100644 --- a/evennia/commands/cmdparser.py +++ b/evennia/commands/cmdparser.py @@ -14,6 +14,7 @@ from evennia.utils.logger import log_trace _MULTIMATCH_REGEX = re.compile(settings.SEARCH_MULTIMATCH_REGEX, re.I + re.U) _CMD_IGNORE_PREFIXES = settings.CMD_IGNORE_PREFIXES + def cmdparser(raw_string, cmdset, caller, match_index=None): """ This function is called by the cmdhandler once it has @@ -82,10 +83,10 @@ def cmdparser(raw_string, cmdset, caller, match_index=None): # use the cmdname as-is for cmd in cmdset: matches.extend([create_match(cmdname, raw_string, cmd, cmdname) - for cmdname in [cmd.key] + cmd.aliases - if cmdname and l_raw_string.startswith(cmdname.lower()) - and (not cmd.arg_regex or - cmd.arg_regex.match(l_raw_string[len(cmdname):]))]) + for cmdname in [cmd.key] + cmd.aliases + if cmdname and l_raw_string.startswith(cmdname.lower()) and + (not cmd.arg_regex or + cmd.arg_regex.match(l_raw_string[len(cmdname):]))]) else: # strip prefixes set in settings for cmd in cmdset: @@ -133,7 +134,7 @@ def cmdparser(raw_string, cmdset, caller, match_index=None): # See if it helps to analyze the match with preserved case but only if # it leaves at least one match. trimmed = [match for match in matches - if raw_string.startswith(match[0])] + if raw_string.startswith(match[0])] if trimmed: matches = trimmed @@ -151,10 +152,10 @@ def cmdparser(raw_string, cmdset, caller, match_index=None): quality = [mat[4] for mat in matches] matches = matches[-quality.count(quality[-1]):] - if len(matches) > 1 and match_index != None and 0 < match_index <= len(matches): + if len(matches) > 1 and match_index is not None and 0 < match_index <= len(matches): # We couldn't separate match by quality, but we have an # index argument to tell us which match to use. - matches = [matches[match_index-1]] + matches = [matches[match_index - 1]] # no matter what we have at this point, we have to return it. return matches diff --git a/evennia/commands/cmdset.py b/evennia/commands/cmdset.py index 7035d2fba..c363f87f8 100644 --- a/evennia/commands/cmdset.py +++ b/evennia/commands/cmdset.py @@ -51,7 +51,7 @@ class _CmdSetMeta(type): cls.key = cls.__name__ cls.path = "%s.%s" % (cls.__module__, cls.__name__) - if not type(cls.key_mergetypes) == dict: + if not isinstance(cls.key_mergetypes, dict): cls.key_mergetypes = {} super(_CmdSetMeta, cls).__init__(*args, **kwargs) @@ -188,7 +188,7 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)): # initialize system self.at_cmdset_creation() - self._contains_cache = WeakKeyDictionary()#{} + self._contains_cache = WeakKeyDictionary() # {} # Priority-sensitive merge operations for cmdsets @@ -214,7 +214,7 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)): cmdset_c.commands.extend(cmdset_b.commands) else: cmdset_c.commands.extend([cmd for cmd in cmdset_b - if not cmd in cmdset_a]) + if cmd not in cmdset_a]) return cmdset_c def _intersect(self, cmdset_a, cmdset_b): @@ -280,7 +280,7 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)): """ cmdset_c = cmdset_a._duplicate() - cmdset_c.commands = [cmd for cmd in cmdset_b if not cmd in cmdset_a] + cmdset_c.commands = [cmd for cmd in cmdset_b if cmd not in cmdset_a] return cmdset_c def _instantiate(self, cmd): @@ -411,7 +411,7 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)): cmdset_c = self._replace(self, cmdset_a) elif mergetype == "Remove": cmdset_c = self._remove(self, cmdset_a) - else: # Union + else: # Union cmdset_c = self._union(self, cmdset_a) # pass through options whenever they are set, unless the higher-prio @@ -426,7 +426,7 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)): # This is used for diagnosis. cmdset_c.actual_mergetype = mergetype - #print "__add__ for %s (prio %i) called with %s (prio %i)." % (self.key, self.priority, cmdset_a.key, cmdset_a.priority) + # print "__add__ for %s (prio %i) called with %s (prio %i)." % (self.key, self.priority, cmdset_a.key, cmdset_a.priority) # return the system commands to the cmdset cmdset_c.add(sys_commands) @@ -604,7 +604,7 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)): names = [] if caller: [names.extend(cmd._keyaliases) for cmd in self.commands - if cmd.access(caller)] + if cmd.access(caller)] else: [names.extend(cmd._keyaliases) for cmd in self.commands] return names diff --git a/evennia/commands/cmdsethandler.py b/evennia/commands/cmdsethandler.py index 84ac50172..09216bc48 100644 --- a/evennia/commands/cmdsethandler.py +++ b/evennia/commands/cmdsethandler.py @@ -86,32 +86,32 @@ _CMDSET_FALLBACKS = settings.CMDSET_FALLBACKS # Output strings _ERROR_CMDSET_IMPORT = _( -"""{traceback} + """{traceback} Error loading cmdset '{path}' (Traceback was logged {timestamp})""") _ERROR_CMDSET_KEYERROR = _( -"""Error loading cmdset: No cmdset class '{classname}' in '{path}'. + """Error loading cmdset: No cmdset class '{classname}' in '{path}'. (Traceback was logged {timestamp})""") _ERROR_CMDSET_SYNTAXERROR = _( -"""{traceback} + """{traceback} SyntaxError encountered when loading cmdset '{path}'. (Traceback was logged {timestamp})""") _ERROR_CMDSET_EXCEPTION = _( -"""{traceback} + """{traceback} Compile/Run error when loading cmdset '{path}'.", (Traceback was logged {timestamp})""") _ERROR_CMDSET_FALLBACK = _( -""" + """ Error encountered for cmdset at path '{path}'. Replacing with fallback '{fallback_path}'. """) _ERROR_CMDSET_NO_FALLBACK = _( -"""Fallback path '{fallback_path}' failed to generate a cmdset.""" + """Fallback path '{fallback_path}' failed to generate a cmdset.""" ) @@ -122,6 +122,7 @@ class _ErrorCmdSet(CmdSet): key = "_CMDSET_ERROR" errmessage = "Error when loading cmdset." + class _EmptyCmdSet(CmdSet): """ This cmdset represents an empty cmdset @@ -130,6 +131,7 @@ class _EmptyCmdSet(CmdSet): priority = -101 mergetype = "Union" + def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False): """ This helper function is used by the cmdsethandler to load a cmdset @@ -154,11 +156,11 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False): """ python_paths = [path] + ["%s.%s" % (prefix, path) - for prefix in _CMDSET_PATHS if not path.startswith(prefix)] + for prefix in _CMDSET_PATHS if not path.startswith(prefix)] errstring = "" for python_path in python_paths: - if "." in path: + if "." in path: modpath, classname = python_path.rsplit(".", 1) else: raise ImportError("The path '%s' is not on the form modulepath.ClassName" % path) @@ -191,7 +193,7 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False): continue _CACHED_CMDSETS[python_path] = cmdsetclass - #instantiate the cmdset (and catch its errors) + # instantiate the cmdset (and catch its errors) if callable(cmdsetclass): cmdsetclass = cmdsetclass(cmdsetobj) return cmdsetclass @@ -235,7 +237,7 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False): err_cmdset = _ErrorCmdSet() err_cmdset.errmessage = errstring return err_cmdset - return None # undefined error + return None # undefined error # classes @@ -278,7 +280,7 @@ class CmdSetHandler(object): self.permanent_paths = [""] if init_true: - self.update(init_mode=True) #is then called from the object __init__. + self.update(init_mode=True) # is then called from the object __init__. def __str__(self): """ @@ -311,8 +313,8 @@ class CmdSetHandler(object): if mergelist: tmpstring = _(" : {current}") string += tmpstring.format(mergelist="+".join(mergelist), - mergetype=mergetype, prio=self.current.priority, - current=self.current) + mergetype=mergetype, prio=self.current.priority, + current=self.current) else: permstring = "non-perm" if self.current.permanent: @@ -322,7 +324,7 @@ class CmdSetHandler(object): prio=self.current.priority, permstring=permstring, keylist=", ".join(cmd.key for - cmd in sorted(self.current, key=lambda o: o.key))) + cmd in sorted(self.current, key=lambda o: o.key))) return string.strip() def _import_cmdset(self, cmdset_path, emit_to_obj=None): @@ -542,7 +544,6 @@ class CmdSetHandler(object): # legacy alias delete_default = remove_default - def all(self): """ Show all cmdsets. @@ -588,16 +589,16 @@ class CmdSetHandler(object): else: print [cset.path for cset in self.cmdset_stack], cmdset.path return any([cset for cset in self.cmdset_stack - if cset.path == cmdset.path]) + if cset.path == cmdset.path]) else: # try it as a path or key if must_be_default: return self.cmdset_stack and ( - self.cmdset_stack[0].key == cmdset or - self.cmdset_stack[0].path == cmdset) + self.cmdset_stack[0].key == cmdset or + self.cmdset_stack[0].path == cmdset) else: return any([cset for cset in self.cmdset_stack - if cset.path == cmdset or cset.key == cmdset]) + if cset.path == cmdset or cset.key == cmdset]) # backwards-compatability alias has_cmdset = has diff --git a/evennia/commands/command.py b/evennia/commands/command.py index 2b62e6221..48a4b132d 100644 --- a/evennia/commands/command.py +++ b/evennia/commands/command.py @@ -60,7 +60,7 @@ def _init_command(cls, **kwargs): if "cmd:" not in cls.locks: cls.locks = "cmd:all();" + cls.locks for lockstring in cls.locks.split(';'): - if lockstring and not ':' in lockstring: + if lockstring and ':' not in lockstring: lockstring = "cmd:%s" % lockstring temp.append(lockstring) cls.lock_storage = ";".join(temp) diff --git a/evennia/commands/default/account.py b/evennia/commands/default/account.py index 51693fe18..ced83c6e2 100644 --- a/evennia/commands/default/account.py +++ b/evennia/commands/default/account.py @@ -364,7 +364,7 @@ class CmdSessions(COMMAND_DEFAULT_CLASS): for sess in sorted(sessions, key=lambda x: x.sessid): char = account.get_puppet(sess) table.add_row(str(sess.sessid), str(sess.protocol_key), - type(sess.address) == tuple and sess.address[0] or sess.address, + isinstance(sess.address, tuple) and sess.address[0] or sess.address, char and str(char) or "None", char and str(char.location) or "N/A") self.msg("|wYour current session(s):|n\n%s" % table) @@ -508,7 +508,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS): options["SCREENHEIGHT"] = options["SCREENHEIGHT"][0] else: options["SCREENHEIGHT"] = " \n".join("%s : %s" % (screenid, size) - for screenid, size in options["SCREENHEIGHT"].iteritems()) + for screenid, size in options["SCREENHEIGHT"].iteritems()) options.pop("TTYPE", None) header = ("Name", "Value", "Saved") if saved_options else ("Name", "Value") @@ -552,7 +552,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS): flags[new_name] = new_val self.msg("Option |w%s|n was changed from '|w%s|n' to '|w%s|n'." % (new_name, old_val, new_val)) return {new_name: new_val} - except Exception, err: + except Exception as err: self.msg("|rCould not set option |w%s|r:|n %s" % (new_name, err)) return False @@ -578,7 +578,7 @@ class CmdOption(COMMAND_DEFAULT_CLASS): val = self.rhs.strip() optiondict = False if val and name in validators: - optiondict = update(name, val, validators[name]) + optiondict = update(name, val, validators[name]) else: self.msg("|rNo option named '|w%s|r'." % name) if optiondict: @@ -664,7 +664,7 @@ class CmdQuit(COMMAND_DEFAULT_CLASS): if nsess == 2: account.msg("|RQuitting|n. One session is still connected.", session=self.session) elif nsess > 2: - account.msg("|RQuitting|n. %i sessions are still connected." % (nsess-1), session=self.session) + account.msg("|RQuitting|n. %i sessions are still connected." % (nsess - 1), session=self.session) else: # we are quitting the last available session account.msg("|RQuitting|n. Hope to see you again, soon.", session=self.session) @@ -732,7 +732,7 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS): for code, _ in ap.ansi_map[self.slice_dark_bg]] bright_bg = ["%s%s|n" % (code.replace("\\", ""), code.replace("|", "||").replace("\\", "")) for code, _ in ap.ansi_xterm256_bright_bg_map[self.slice_bright_bg]] - dark_fg.extend(["" for _ in range(len(bright_fg)-len(dark_fg))]) + dark_fg.extend(["" for _ in range(len(bright_fg) - len(dark_fg))]) table = utils.format_table([bright_fg, dark_fg, bright_bg, dark_bg]) string = "ANSI colors:" for row in table: @@ -751,16 +751,16 @@ class CmdColorTest(COMMAND_DEFAULT_CLASS): # foreground table table[ir].append("|%i%i%i%s|n" % (ir, ig, ib, "||%i%i%i" % (ir, ig, ib))) # background table - table[6+ir].append("|%i%i%i|[%i%i%i%s|n" - % (5 - ir, 5 - ig, 5 - ib, ir, ig, ib, "||[%i%i%i" % (ir, ig, ib))) + table[6 + ir].append("|%i%i%i|[%i%i%i%s|n" + % (5 - ir, 5 - ig, 5 - ib, ir, ig, ib, "||[%i%i%i" % (ir, ig, ib))) table = self.table_format(table) string = "Xterm256 colors (if not all hues show, your client might not report that it can handle xterm256):" string += "\n" + "\n".join("".join(row) for row in table) table = [[], [], [], [], [], [], [], [], [], [], [], []] for ibatch in range(4): for igray in range(6): - letter = chr(97 + (ibatch*6 + igray)) - inverse = chr(122 - (ibatch*6 + igray)) + letter = chr(97 + (ibatch * 6 + igray)) + inverse = chr(122 - (ibatch * 6 + igray)) table[0 + igray].append("|=%s%s |n" % (letter, "||=%s" % letter)) table[6 + igray].append("|=%s|[=%s%s |n" % (inverse, letter, "||[=%s" % letter)) for igray in range(6): diff --git a/evennia/commands/default/admin.py b/evennia/commands/default/admin.py index 473d57aa2..9aff9a396 100644 --- a/evennia/commands/default/admin.py +++ b/evennia/commands/default/admin.py @@ -171,9 +171,9 @@ class CmdBan(COMMAND_DEFAULT_CLASS): if not banlist: banlist = [] - if not self.args or (self.switches - and not any(switch in ('ip', 'name') - for switch in self.switches)): + if not self.args or (self.switches and + not any(switch in ('ip', 'name') + for switch in self.switches)): self.caller.msg(list_bans(banlist)) return @@ -430,7 +430,7 @@ class CmdNewPassword(COMMAND_DEFAULT_CLASS): self.msg("%s - new password set to '%s'." % (account.name, self.rhs)) if account.character != caller: account.msg("%s has changed your password to '%s'." % (caller.name, - self.rhs)) + self.rhs)) class CmdPerm(COMMAND_DEFAULT_CLASS): diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index c16cb841d..582ab1f90 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -41,6 +41,7 @@ _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH _PROTOTYPE_PARENTS = None + class ObjManipCommand(COMMAND_DEFAULT_CLASS): """ This is a parent class for some of the defining objmanip commands @@ -73,7 +74,7 @@ class ObjManipCommand(COMMAND_DEFAULT_CLASS): super(ObjManipCommand, self).parse() obj_defs = ([], []) # stores left- and right-hand side of '=' - obj_attrs = ([], []) # " + obj_attrs = ([], []) # " for iside, arglist in enumerate((self.lhslist, self.rhslist)): # lhslist/rhslist is already split by ',' at this point @@ -88,8 +89,8 @@ class ObjManipCommand(COMMAND_DEFAULT_CLASS): objdef, attrs = [part.strip() for part in objdef.split('/', 1)] attrs = [part.strip().lower() for part in attrs.split('/') if part.strip()] # store data - obj_defs[iside].append({"name":objdef, 'option':option, 'aliases':aliases}) - obj_attrs[iside].append({"name":objdef, 'attrs':attrs}) + obj_defs[iside].append({"name": objdef, 'option': option, 'aliases': aliases}) + obj_attrs[iside].append({"name": objdef, 'attrs': attrs}) # store for future access self.lhs_objs = obj_defs[0] @@ -242,9 +243,9 @@ class CmdCopy(ObjManipCommand): return copiedobj = ObjectDB.objects.copy_object(from_obj, - new_key=to_obj_name, - new_location=to_obj_location, - new_aliases=to_obj_aliases) + new_key=to_obj_name, + new_location=to_obj_location, + new_aliases=to_obj_aliases) if copiedobj: string = "Copied %s to '%s' (aliases: %s)." % (from_obj_name, to_obj_name, to_obj_aliases) @@ -350,7 +351,7 @@ class CmdCpAttr(ObjManipCommand): if not from_obj or not to_objs: caller.msg("You have to supply both source object and target(s).") return - #copy to all to_obj:ects + # copy to all to_obj:ects if "move" in self.switches: clear = True else: @@ -387,7 +388,7 @@ class CmdCpAttr(ObjManipCommand): value = self.get_attr(from_obj, from_attr) to_obj.attributes.add(to_attr, value) if (clear and not (from_obj == to_obj and - from_attr == to_attr)): + from_attr == to_attr)): from_obj.attributes.remove(from_attr) result.append("\nMoved %s.%s -> %s.%s. (value: %s)" % (from_obj.name, from_attr, @@ -522,6 +523,7 @@ class CmdCreate(ObjManipCommand): def _desc_load(caller): return caller.db.evmenu_target.db.desc or "" + def _desc_save(caller, buf): """ Save line buffer to the desc prop. This should @@ -531,10 +533,12 @@ def _desc_save(caller, buf): caller.msg("Saved.") return True + def _desc_quit(caller): caller.attributes.remove("evmenu_target") caller.msg("Exited editor.") + class CmdDesc(COMMAND_DEFAULT_CLASS): """ describe an object or the current room. @@ -642,12 +646,12 @@ class CmdDestroy(COMMAND_DEFAULT_CLASS): objname = obj.name if not (obj.access(caller, "control") or obj.access(caller, 'delete')): return "\nYou don't have permission to delete %s." % objname - if obj.account and not 'override' in self.switches: + if obj.account and 'override' not in self.switches: return "\nObject %s is controlled by an active account. Use /override to delete anyway." % objname if obj.dbid == int(settings.DEFAULT_HOME.lstrip("#")): return "\nYou are trying to delete |c%s|n, which is set as DEFAULT_HOME. " \ - "Re-point settings.DEFAULT_HOME to another " \ - "object before continuing." % objname + "Re-point settings.DEFAULT_HOME to another " \ + "object before continuing." % objname had_exits = hasattr(obj, "exits") and obj.exits had_objs = hasattr(obj, "contents") and any(obj for obj in obj.contents @@ -749,7 +753,7 @@ class CmdDig(ObjManipCommand): if new_room.aliases.all(): alias_string = " (%s)" % ", ".join(new_room.aliases.all()) room_string = "Created room %s(%s)%s of type %s." % (new_room, - new_room.dbref, alias_string, typeclass) + new_room.dbref, alias_string, typeclass) # create exit to room @@ -763,7 +767,7 @@ class CmdDig(ObjManipCommand): "\nNo exit created to new room." elif not location: exit_to_string = \ - "\nYou cannot create an exit from a None-location." + "\nYou cannot create an exit from a None-location." else: # Build the exit to the new room from the current one typeclass = to_exit["option"] @@ -796,18 +800,18 @@ class CmdDig(ObjManipCommand): "\nNo back exit created." elif not location: exit_back_string = \ - "\nYou cannot create an exit back to a None-location." + "\nYou cannot create an exit back to a None-location." else: typeclass = back_exit["option"] if not typeclass: typeclass = settings.BASE_EXIT_TYPECLASS new_back_exit = create.create_object(typeclass, - back_exit["name"], - new_room, - aliases=back_exit["aliases"], - locks=lockstring, - destination=location, - report_to=caller) + back_exit["name"], + new_room, + aliases=back_exit["aliases"], + locks=lockstring, + destination=location, + report_to=caller) alias_string = "" if new_back_exit.aliases.all(): alias_string = " (%s)" % ", ".join(new_back_exit.aliases.all()) @@ -821,6 +825,7 @@ class CmdDig(ObjManipCommand): if new_room and ('teleport' in self.switches or "tel" in self.switches): caller.move_to(new_room) + class CmdTunnel(COMMAND_DEFAULT_CLASS): """ create new rooms in cardinal directions only @@ -893,7 +898,7 @@ class CmdTunnel(COMMAND_DEFAULT_CLASS): if "tel" in self.switches: telswitch = "/teleport" backstring = "" - if not "oneway" in self.switches: + if "oneway" not in self.switches: backstring = ", %s;%s" % (backname, backshort) # build the string we will use to call @dig @@ -951,7 +956,7 @@ class CmdLink(COMMAND_DEFAULT_CLASS): string = "" if not obj.destination: - string += "Note: %s(%s) did not have a destination set before. Make sure you linked the right thing." % (obj.name,obj.dbref) + string += "Note: %s(%s) did not have a destination set before. Make sure you linked the right thing." % (obj.name, obj.dbref) if "twoway" in self.switches: if not (target.location and obj.location): string = "To create a two-way link, %s and %s must both have a location" % (obj, target) @@ -1199,7 +1204,7 @@ class CmdOpen(ObjManipCommand): # a custom member method to chug out exits and do checks def create_exit(self, exit_name, location, destination, - exit_aliases=None, typeclass=None): + exit_aliases=None, typeclass=None): """ Helper function to avoid code duplication. At this point we know destination is a valid location @@ -1484,10 +1489,11 @@ class CmdSetAttribute(ObjManipCommand): old_value = obj.attributes.get(attr) if old_value is not None and not isinstance(old_value, basestring): typ = type(old_value).__name__ - self.caller.msg("|RWARNING! Saving this buffer will overwrite the "\ + self.caller.msg("|RWARNING! Saving this buffer will overwrite the " "current attribute (of type %s) with a string!|n" % typ) return str(old_value) return old_value + def save(caller, buf): "Called when editor saves its buffer." obj.attributes.add(attr, buf) @@ -1495,7 +1501,6 @@ class CmdSetAttribute(ObjManipCommand): # start the editor EvEditor(self.caller, load, save, key="%s/%s" % (obj, attr)) - def func(self): "Implement the set attribute - a limited form of @py." @@ -1523,7 +1528,7 @@ class CmdSetAttribute(ObjManipCommand): if "edit" in self.switches: # edit in the line editor if len(attrs) > 1: - caller.msg("The Line editor can only be applied " \ + caller.msg("The Line editor can only be applied " "to one attribute at a time.") return self.edit_handler(obj, attrs[0]) @@ -1538,7 +1543,7 @@ class CmdSetAttribute(ObjManipCommand): continue result.append(self.view_attr(obj, attr)) # we view it without parsing markup. - self.caller.msg("".join(result).strip(), options={"raw":True}) + self.caller.msg("".join(result).strip(), options={"raw": True}) return else: # deleting the attribute(s) @@ -1644,7 +1649,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS): return is_same = obj.is_typeclass(new_typeclass, exact=True) - if is_same and not 'force' in self.switches: + if is_same and 'force' not in self.switches: string = "%s already has the typeclass '%s'. Use /force to override." % (obj.name, new_typeclass) else: update = "update" in self.switches @@ -1654,14 +1659,14 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS): # we let this raise exception if needed obj.swap_typeclass(new_typeclass, clean_attributes=reset, - clean_cmdsets=reset, run_start_hooks=hooks) + clean_cmdsets=reset, run_start_hooks=hooks) if is_same: string = "%s updated its existing typeclass (%s).\n" % (obj.name, obj.path) else: string = "%s changed typeclass from %s to %s.\n" % (obj.name, - old_typeclass_path, - obj.typeclass_path) + old_typeclass_path, + obj.typeclass_path) if update: string += "Only the at_object_creation hook was run (update mode)." else: @@ -1797,8 +1802,8 @@ class CmdLock(ObjManipCommand): # we have a = separator, so we are assigning a new lock if self.switches: swi = ", ".join(self.switches) - caller.msg("Switch(es) |w%s|n can not be used with a "\ - "lock assignment. Use e.g. " \ + caller.msg("Switch(es) |w%s|n can not be used with a " + "lock assignment. Use e.g. " "|w@lock/del objname/locktype|n instead." % swi) return @@ -1851,7 +1856,7 @@ class CmdExamine(ObjManipCommand): """ key = "@examine" - aliases = ["@ex","exam"] + aliases = ["@ex", "exam"] locks = "cmd:perm(examine) or perm(Builder)" help_category = "Building" arg_regex = r"(/\w+?(\s|$))|\s|$" @@ -1913,7 +1918,7 @@ class CmdExamine(ObjManipCommand): string += "\n|wAliases|n: %s" % (", ".join(utils.make_iter(str(obj.aliases)))) if hasattr(obj, "sessions") and obj.sessions.all(): string += "\n|wSession id(s)|n: %s" % (", ".join("#%i" % sess.sessid - for sess in obj.sessions.all())) + for sess in obj.sessions.all())) if hasattr(obj, "email") and obj.email: string += "\n|wEmail|n: |c%s|n" % obj.email if hasattr(obj, "has_account") and obj.has_account: @@ -1957,20 +1962,19 @@ class CmdExamine(ObjManipCommand): locks_string = " Default" string += "\n|wLocks|n:%s" % locks_string - if not (len(obj.cmdset.all()) == 1 and obj.cmdset.current.key == "_EMPTY_CMDSET"): # all() returns a 'stack', so make a copy to sort. stored_cmdsets = sorted(obj.cmdset.all(), key=lambda x: x.priority, reverse=True) - string += "\n|wStored Cmdset(s)|n:\n %s" % ("\n ".join("%s [%s] (%s, prio %s)" % \ - (cmdset.path, cmdset.key, cmdset.mergetype, cmdset.priority) - for cmdset in stored_cmdsets if cmdset.key != "_EMPTY_CMDSET")) + string += "\n|wStored Cmdset(s)|n:\n %s" % ("\n ".join("%s [%s] (%s, prio %s)" % + (cmdset.path, cmdset.key, cmdset.mergetype, cmdset.priority) + for cmdset in stored_cmdsets if cmdset.key != "_EMPTY_CMDSET")) # this gets all components of the currently merged set all_cmdsets = [(cmdset.key, cmdset) for cmdset in avail_cmdset.merged_from] # we always at least try to add account- and session sets since these are ignored # if we merge on the object level. if hasattr(obj, "account") and obj.account: - all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.account.cmdset.all()]) + all_cmdsets.extend([(cmdset.key, cmdset) for cmdset in obj.account.cmdset.all()]) if obj.sessions.count(): # if there are more sessions than one on objects it's because of multisession mode 3. # we only show the first session's cmdset here (it is -in principle- possible that @@ -1986,14 +1990,13 @@ class CmdExamine(ObjManipCommand): pass all_cmdsets = [cmdset for cmdset in dict(all_cmdsets).values()] all_cmdsets.sort(key=lambda x: x.priority, reverse=True) - string += "\n|wMerged Cmdset(s)|n:\n %s" % ("\n ".join("%s [%s] (%s, prio %s)" % \ - (cmdset.path, cmdset.key, cmdset.mergetype, cmdset.priority) - for cmdset in all_cmdsets)) - + string += "\n|wMerged Cmdset(s)|n:\n %s" % ("\n ".join("%s [%s] (%s, prio %s)" % + (cmdset.path, cmdset.key, cmdset.mergetype, cmdset.priority) + for cmdset in all_cmdsets)) # list the commands available to this object avail_cmdset = sorted([cmd.key for cmd in avail_cmdset - if cmd.access(obj, "cmd")]) + if cmd.access(obj, "cmd")]) cmdsetstr = utils.fill(", ".join(avail_cmdset), indent=2) string += "\n|wCommands available to %s (result of Merged CmdSets)|n:\n %s" % (obj.key, cmdsetstr) @@ -2005,7 +2008,7 @@ class CmdExamine(ObjManipCommand): # display Tags tags_string = utils.fill(", ".join("%s[%s]" % (tag, category) - for tag, category in obj.tags.all(return_key_and_category=True)), indent=5) + for tag, category in obj.tags.all(return_key_and_category=True)), indent=5) if tags_string: string += "\n|wTags[category]|n: %s" % tags_string.strip() @@ -2029,7 +2032,7 @@ class CmdExamine(ObjManipCommand): string += "\n|wContents|n: %s" % ", ".join(["%s(%s)" % (cont.name, cont.dbref) for cont in obj.contents if cont not in exits and cont not in pobjs]) separator = "-" * _DEFAULT_WIDTH - #output info + # output info return '%s\n%s\n%s' % (separator, string.strip(), separator) def func(self): @@ -2053,7 +2056,7 @@ class CmdExamine(ObjManipCommand): if hasattr(caller, "location"): obj = caller.location if not obj.access(caller, 'examine'): - #If we don't have special info access, just look at the object instead. + # If we don't have special info access, just look at the object instead. self.msg(caller.at_look(obj)) return # using callback for printing result whenever function returns. @@ -2070,20 +2073,20 @@ class CmdExamine(ObjManipCommand): obj_attrs = objdef['attrs'] self.account_mode = utils.inherits_from(caller, "evennia.accounts.accounts.DefaultAccount") or \ - "account" in self.switches or obj_name.startswith('*') + "account" in self.switches or obj_name.startswith('*') if self.account_mode: try: obj = caller.search_account(obj_name.lstrip('*')) except AttributeError: # this means we are calling examine from an account object - obj = caller.search(obj_name.lstrip('*'), search_object = 'object' in self.switches) + obj = caller.search(obj_name.lstrip('*'), search_object='object' in self.switches) else: obj = caller.search(obj_name) if not obj: continue if not obj.access(caller, 'examine'): - #If we don't have special info access, just look + # If we don't have special info access, just look # at the object instead. self.msg(caller.at_look(obj)) continue @@ -2184,7 +2187,7 @@ class CmdFind(COMMAND_DEFAULT_CLASS): elif not low <= int(result[0].id) <= high: string += "\n |RNo match found for '%s' in #dbref interval.|n" % (searchstring) else: - result=result[0] + result = result[0] string += "\n|g %s - %s|n" % (result.get_display_name(caller), result.path) else: # Not an account/dbref search but a wider search; build a queryset. @@ -2192,7 +2195,7 @@ class CmdFind(COMMAND_DEFAULT_CLASS): if "exact" in switches: keyquery = Q(db_key__iexact=searchstring, id__gte=low, id__lte=high) aliasquery = Q(db_tags__db_key__iexact=searchstring, - db_tags__db_tagtype__iexact="alias",id__gte=low, id__lte=high) + db_tags__db_tagtype__iexact="alias", id__gte=low, id__lte=high) else: keyquery = Q(db_key__istartswith=searchstring, id__gte=low, id__lte=high) aliasquery = Q(db_tags__db_key__istartswith=searchstring, @@ -2283,14 +2286,14 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS): if obj_to_teleport.has_account: caller.msg("Cannot teleport a puppeted object " "(%s, puppeted by %s) to a None-location." % ( - obj_to_teleport.key, obj_to_teleport.account)) + obj_to_teleport.key, obj_to_teleport.account)) return caller.msg("Teleported %s -> None-location." % obj_to_teleport) if obj_to_teleport.location and not tel_quietly: obj_to_teleport.location.msg_contents("%s teleported %s into nothingness." % (caller, obj_to_teleport), exclude=caller) - obj_to_teleport.location=None + obj_to_teleport.location = None return # not teleporting to None location @@ -2397,7 +2400,7 @@ class CmdScript(COMMAND_DEFAULT_CLASS): obj.get_display_name(caller))) script.stop() obj.scripts.validate() - else: # rhs exists + else: # rhs exists if not self.switches: # adding a new script, and starting it ok = obj.scripts.add(self.rhs, autostart=True) @@ -2478,16 +2481,16 @@ class CmdTag(COMMAND_DEFAULT_CLASS): nobjs = len(objs) if nobjs > 0: catstr = " (category: '|w%s|n')" % category if category else \ - ("" if nobjs == 1 else " (may have different tag categories)") + ("" if nobjs == 1 else " (may have different tag categories)") matchstr = ", ".join(o.get_display_name(self.caller) for o in objs) string = "Found |w%i|n object%s with tag '|w%s|n'%s:\n %s" % (nobjs, - "s" if nobjs > 1 else "", - tag, - catstr, matchstr) + "s" if nobjs > 1 else "", + tag, + catstr, matchstr) else: string = "No objects found with tag '%s%s'." % (tag, - " (category: %s)" % category if category else "") + " (category: %s)" % category if category else "") self.caller.msg(string) return if "del" in self.switches: @@ -2504,14 +2507,14 @@ class CmdTag(COMMAND_DEFAULT_CLASS): if obj.tags.get(tag, category=category): obj.tags.remove(tag, category=category) string = "Removed tag '%s'%s from %s." % ( - tag, - " (category: %s)" % category if category else "", - obj) + tag, + " (category: %s)" % category if category else "", + obj) else: string = "No tag '%s'%s to delete on %s." % ( - tag, - " (category: %s)" % category if category else "", - obj) + tag, + " (category: %s)" % category if category else "", + obj) else: # no tag specified, clear all tags old_tags = ["%s%s" % (tag, " (category: %s" % category if category else "") @@ -2550,7 +2553,7 @@ class CmdTag(COMMAND_DEFAULT_CLASS): categories = [" (category: %s)" % tup[1] if tup[1] else "" for tup in tagtuples] if ntags: string = "Tag%s on %s: %s" % ("s" if ntags > 1 else "", obj, - ", ".join("'%s'%s" % (tags[i], categories[i]) for i in range(ntags))) + ", ".join("'%s'%s" % (tags[i], categories[i]) for i in range(ntags))) else: string = "No tags attached to %s." % obj self.caller.msg(string) @@ -2561,6 +2564,7 @@ class CmdTag(COMMAND_DEFAULT_CLASS): # Reload the server and the prototypes should be available. # + class CmdSpawn(COMMAND_DEFAULT_CLASS): """ spawn objects from prototype @@ -2608,7 +2612,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): "Helper to show a list of available prototypes" prots = ", ".join(sorted(prototypes.keys())) return "\nAvailable prototypes (case sensistive): %s" % \ - ("\n" + utils.fill(prots) if prots else "None") + ("\n" + utils.fill(prots) if prots else "None") prototypes = spawn(return_prototypes=True) if not self.args: @@ -2628,7 +2632,6 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): self.caller.msg(string) return - if isinstance(prototype, basestring): # A prototype key keystr = prototype @@ -2647,9 +2650,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): self.caller.msg("The prototype must be a prototype key or a Python dictionary.") return - if not "noloc" in self.switches and not "location" in prototype: + if "noloc" in self.switches and not "location" not in prototype: prototype["location"] = self.caller.location for obj in spawn(prototype): self.caller.msg("Spawned %s." % obj.get_display_name(self.caller)) - diff --git a/evennia/commands/default/cmdset_account.py b/evennia/commands/default/cmdset_account.py index 12c25080e..4e357a2ce 100644 --- a/evennia/commands/default/cmdset_account.py +++ b/evennia/commands/default/cmdset_account.py @@ -31,7 +31,7 @@ class AccountCmdSet(CmdSet): self.add(account.CmdOOC()) self.add(account.CmdCharCreate()) self.add(account.CmdCharDelete()) - #self.add(account.CmdSessions()) + # self.add(account.CmdSessions()) self.add(account.CmdWho()) self.add(account.CmdOption()) self.add(account.CmdQuit()) diff --git a/evennia/commands/default/cmdset_character.py b/evennia/commands/default/cmdset_character.py index f1d023fcf..cfc8a30ca 100644 --- a/evennia/commands/default/cmdset_character.py +++ b/evennia/commands/default/cmdset_character.py @@ -9,6 +9,7 @@ from evennia.commands.default import general, help, admin, system from evennia.commands.default import building from evennia.commands.default import batchprocess + class CharacterCmdSet(CmdSet): """ Implements the default command set. @@ -46,7 +47,7 @@ class CharacterCmdSet(CmdSet): self.add(system.CmdAbout()) self.add(system.CmdTime()) self.add(system.CmdServerLoad()) - #self.add(system.CmdPs()) + # self.add(system.CmdPs()) self.add(system.CmdTickers()) # Admin commands diff --git a/evennia/commands/default/cmdset_session.py b/evennia/commands/default/cmdset_session.py index af6a1c42b..c5a782124 100644 --- a/evennia/commands/default/cmdset_session.py +++ b/evennia/commands/default/cmdset_session.py @@ -4,6 +4,7 @@ This module stores session-level commands. from evennia.commands.cmdset import CmdSet from evennia.commands.default import account + class SessionCmdSet(CmdSet): """ Sets up the unlogged cmdset. diff --git a/evennia/commands/default/comms.py b/evennia/commands/default/comms.py index ae086cb7b..e2f5b6aa2 100644 --- a/evennia/commands/default/comms.py +++ b/evennia/commands/default/comms.py @@ -297,7 +297,7 @@ class CmdChannels(COMMAND_DEFAULT_CLASS): clower = chan.key.lower() nicks = caller.nicks.get(category="channel", return_obj=True) comtable.add_row(*["%s%s" % (chan.key, chan.aliases.all() and - "(%s)" % ",".join(chan.aliases.all()) or ""), + "(%s)" % ",".join(chan.aliases.all()) or ""), "%s" % ",".join(nick.db_key for nick in make_iter(nicks) if nick and nick.value[3].lower() == clower), chan.db.desc]) @@ -614,7 +614,7 @@ class CmdClock(COMMAND_DEFAULT_CLASS): # Try to add the lock try: channel.locks.add(self.rhs) - except LockException, err: + except LockException as err: self.msg(err) return string = "Lock(s) applied. " @@ -890,7 +890,7 @@ class CmdIRC2Chan(COMMAND_DEFAULT_CLASS): self.rhs = self.rhs.replace('#', ' ') # to avoid Python comment issues try: irc_network, irc_port, irc_channel, irc_botname = \ - [part.strip() for part in self.rhs.split(None, 4)] + [part.strip() for part in self.rhs.split(None, 4)] irc_channel = "#%s" % irc_channel except Exception: string = "IRC bot definition '%s' is not valid." % self.rhs diff --git a/evennia/commands/default/general.py b/evennia/commands/default/general.py index 3cbf77122..93e35efe3 100644 --- a/evennia/commands/default/general.py +++ b/evennia/commands/default/general.py @@ -170,7 +170,7 @@ class CmdNick(COMMAND_DEFAULT_CLASS): # we are given a index in nicklist delindex = int(arg) if 0 < delindex <= len(nicklist): - oldnick = nicklist[delindex-1] + oldnick = nicklist[delindex - 1] _, _, old_nickstring, old_replstring = oldnick.value else: errstring += "Not a valid nick index." @@ -419,6 +419,7 @@ class CmdSay(COMMAND_DEFAULT_CLASS): # Call the at_after_say hook on the character caller.at_say(speech) + class CmdWhisper(COMMAND_DEFAULT_CLASS): """ Speak privately as your character to another diff --git a/evennia/commands/default/muxcommand.py b/evennia/commands/default/muxcommand.py index 425da509a..5d8d4b289 100644 --- a/evennia/commands/default/muxcommand.py +++ b/evennia/commands/default/muxcommand.py @@ -22,6 +22,7 @@ class MuxCommand(Command): used by Evennia to create the automatic help entry for the command, so make sure to document consistently here. """ + def has_perm(self, srcobj): """ This is called by the cmdhandler to determine @@ -191,6 +192,7 @@ class MuxAccountCommand(MuxCommand): creating a new property "character" that is set only if a character is actually attached to this Account and Session. """ + def parse(self): """ We run the parent parser as usual, then fix the result diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index 9a5356bc0..49c8d4803 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -133,11 +133,11 @@ def _py_code(caller, buf): """ measure_time = caller.db._py_measure_time string = "Executing code%s ..." % ( - " (measure timing)" if measure_time else "") + " (measure timing)" if measure_time else "") caller.msg(string) _run_code_snippet(caller, buf, mode="exec", - measure_time=measure_time, - show_input=False) + measure_time=measure_time, + show_input=False) return True @@ -147,7 +147,7 @@ def _py_quit(caller): def _run_code_snippet(caller, pycode, mode="eval", measure_time=False, - show_input=True): + show_input=True): """ Run code and try to display information to the caller. @@ -166,18 +166,18 @@ def _run_code_snippet(caller, pycode, mode="eval", measure_time=False, # import useful variables import evennia available_vars = { - 'self': caller, - 'me': caller, - 'here': getattr(caller, "location", None), - 'evennia': evennia, - 'ev': evennia, - 'inherits_from': utils.inherits_from, + 'self': caller, + 'me': caller, + 'here': getattr(caller, "location", None), + 'evennia': evennia, + 'ev': evennia, + 'inherits_from': utils.inherits_from, } if show_input: try: caller.msg(">>> %s" % pycode, session=session, - options={"raw": True}) + options={"raw": True}) except TypeError: caller.msg(">>> %s" % pycode, options={"raw": True}) @@ -256,8 +256,8 @@ class CmdPy(COMMAND_DEFAULT_CLASS): if "edit" in self.switches: caller.db._py_measure_time = "time" in self.switches EvEditor(self.caller, loadfunc=_py_load, savefunc=_py_code, - quitfunc=_py_quit, key="Python exec: :w or :!", persistent=True, - codefunc=_py_code) + quitfunc=_py_quit, key="Python exec: :w or :!", persistent=True, + codefunc=_py_code) return if not pycode: @@ -719,7 +719,7 @@ class CmdServerLoad(COMMAND_DEFAULT_CLASS): now, _ = _IDMAPPER.cache_size() string = "The Idmapper cache freed |w{idmapper}|n database objects.\n" \ "The Python garbage collector freed |w{gc}|n Python instances total." - self.caller.msg(string.format(idmapper=(prev-now), gc=nflushed)) + self.caller.msg(string.format(idmapper=(prev - now), gc=nflushed)) return # display active processes diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index a4e694e3c..ffbb2d8c3 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -59,7 +59,7 @@ class CommandTest(EvenniaTest): cmdobj.caller = caller cmdobj.cmdname = cmdstring if cmdstring else cmdobj.key cmdobj.raw_cmdname = cmdobj.cmdname - cmdobj.cmdstring = cmdobj.cmdname # deprecated + cmdobj.cmdstring = cmdobj.cmdname # deprecated cmdobj.args = args cmdobj.cmdset = cmdset cmdobj.session = SESSIONS.session_from_sessid(1) @@ -79,17 +79,17 @@ class CommandTest(EvenniaTest): pass finally: # clean out evtable sugar. We only operate on text-type - stored_msg = [args[0] if args and args[0] else kwargs.get("text",utils.to_str(kwargs, force_string=True)) - for name, args, kwargs in receiver.msg.mock_calls] + stored_msg = [args[0] if args and args[0] else kwargs.get("text", utils.to_str(kwargs, force_string=True)) + for name, args, kwargs in receiver.msg.mock_calls] # Get the first element of a tuple if msg received a tuple instead of a string stored_msg = [smsg[0] if isinstance(smsg, tuple) else smsg for smsg in stored_msg] if msg is not None: returned_msg = "||".join(_RE.sub("", mess) for mess in stored_msg) returned_msg = ansi.parse_ansi(returned_msg, strip_ansi=noansi).strip() if msg == "" and returned_msg or not returned_msg.startswith(msg.strip()): - sep1 = "\n" + "="*30 + "Wanted message" + "="*34 + "\n" - sep2 = "\n" + "="*30 + "Returned message" + "="*32 + "\n" - sep3 = "\n" + "="*78 + sep1 = "\n" + "=" * 30 + "Wanted message" + "=" * 34 + "\n" + sep2 = "\n" + "=" * 30 + "Returned message" + "=" * 32 + "\n" + sep3 = "\n" + "=" * 78 retval = sep1 + msg.strip() + sep2 + returned_msg + sep3 raise AssertionError(retval) else: @@ -271,7 +271,7 @@ class TestBuilding(CommandTest): def test_typeclass(self): self.call(building.CmdTypeclass(), "Obj = evennia.objects.objects.DefaultExit", - "Obj changed typeclass from evennia.objects.objects.DefaultObject to evennia.objects.objects.DefaultExit.") + "Obj changed typeclass from evennia.objects.objects.DefaultObject to evennia.objects.objects.DefaultExit.") def test_lock(self): self.call(building.CmdLock(), "Obj = test:perm(Developer)", "Added lock 'test:perm(Developer)' to Obj.") @@ -294,10 +294,10 @@ class TestComms(CommandTest): def test_toggle_com(self): self.call(comms.CmdAddCom(), "tc = testchan", "You are already connected to channel testchan. You can now", receiver=self.account) - self.call(comms.CmdDelCom(), "tc", "Your alias 'tc' for channel testchan was cleared.", receiver=self.account) + self.call(comms.CmdDelCom(), "tc", "Your alias 'tc' for channel testchan was cleared.", receiver=self.account) def test_channels(self): - self.call(comms.CmdChannels(), "" ,"Available channels (use comlist,addcom and delcom to manage", receiver=self.account) + self.call(comms.CmdChannels(), "", "Available channels (use comlist,addcom and delcom to manage", receiver=self.account) def test_all_com(self): self.call(comms.CmdAllCom(), "", "Available channels (use comlist,addcom and delcom to manage", receiver=self.account) @@ -322,7 +322,7 @@ class TestComms(CommandTest): self.call(comms.CmdCBoot(), "", "Usage: @cboot[/quiet] = [:reason]", receiver=self.account) def test_cdestroy(self): - self.call(comms.CmdCdestroy(), "testchan" ,"[testchan] TestAccount: testchan is being destroyed. Make sure to change your aliases.|Channel 'testchan' was destroyed.", receiver=self.account) + self.call(comms.CmdCdestroy(), "testchan", "[testchan] TestAccount: testchan is being destroyed. Make sure to change your aliases.|Channel 'testchan' was destroyed.", receiver=self.account) class TestBatchProcess(CommandTest): @@ -332,6 +332,7 @@ class TestBatchProcess(CommandTest): # we make sure to delete the button again here to stop the running reactor self.call(building.CmdDestroy(), "button", "button was destroyed.") + class CmdInterrupt(Command): key = "interrupt" diff --git a/evennia/commands/default/unloggedin.py b/evennia/commands/default/unloggedin.py index d29e236df..429948b0f 100644 --- a/evennia/commands/default/unloggedin.py +++ b/evennia/commands/default/unloggedin.py @@ -147,7 +147,7 @@ def create_normal_account(session, name, password): account (Account): the account which was created from the name and password. """ # check for too many login errors too quick. - if _throttle(session, maxlim=5, timeout=5*60): + if _throttle(session, maxlim=5, timeout=5 * 60): # timeout is 5 minutes. session.msg("|RYou made too many connection attempts. Try again in a few minutes.|n") return None @@ -168,8 +168,8 @@ def create_normal_account(session, name, password): # Check IP and/or name bans bans = ServerConfig.objects.conf("server_bans") - if bans and (any(tup[0] == account.name.lower() for tup in bans) - or + if bans and (any(tup[0] == account.name.lower() for tup in bans) or + any(tup[2].match(session.address) for tup in bans if tup[2])): # this is a banned IP or name! string = "|rYou have been banned and cannot continue from here." \ @@ -209,7 +209,7 @@ class CmdUnconnectedConnect(COMMAND_DEFAULT_CLASS): session = self.caller # check for too many login errors too quick. - if _throttle(session, maxlim=5, timeout=5*60, storage=_LATEST_FAILED_LOGINS): + if _throttle(session, maxlim=5, timeout=5 * 60, storage=_LATEST_FAILED_LOGINS): # timeout is 5 minutes. session.msg("|RYou made too many connection attempts. Try again in a few minutes.|n") return @@ -301,8 +301,8 @@ class CmdUnconnectedCreate(COMMAND_DEFAULT_CLASS): # Check IP and/or name bans bans = ServerConfig.objects.conf("server_bans") - if bans and (any(tup[0] == accountname.lower() for tup in bans) - or + if bans and (any(tup[0] == accountname.lower() for tup in bans) or + any(tup[2].match(session.address) for tup in bans if tup[2])): # this is a banned IP or name! string = "|rYou have been banned and cannot continue from here." \ diff --git a/evennia/commands/tests.py b/evennia/commands/tests.py index 6e44fb51a..0e465e377 100644 --- a/evennia/commands/tests.py +++ b/evennia/commands/tests.py @@ -12,46 +12,67 @@ from evennia.commands.command import Command class _CmdA(Command): key = "A" + def __init__(self, cmdset, *args, **kwargs): super(_CmdA, self).__init__(*args, **kwargs) self.from_cmdset = cmdset + + class _CmdB(Command): key = "B" + def __init__(self, cmdset, *args, **kwargs): super(_CmdB, self).__init__(*args, **kwargs) self.from_cmdset = cmdset + + class _CmdC(Command): key = "C" + def __init__(self, cmdset, *args, **kwargs): super(_CmdC, self).__init__(*args, **kwargs) self.from_cmdset = cmdset + + class _CmdD(Command): key = "D" + def __init__(self, cmdset, *args, **kwargs): super(_CmdD, self).__init__(*args, **kwargs) self.from_cmdset = cmdset + class _CmdSetA(CmdSet): key = "A" - def at_cmdset_creation(self): + + def at_cmdset_creation(self): self.add(_CmdA("A")) self.add(_CmdB("A")) self.add(_CmdC("A")) self.add(_CmdD("A")) + + class _CmdSetB(CmdSet): key = "B" - def at_cmdset_creation(self): + + def at_cmdset_creation(self): self.add(_CmdA("B")) self.add(_CmdB("B")) self.add(_CmdC("B")) + + class _CmdSetC(CmdSet): key = "C" - def at_cmdset_creation(self): + + def at_cmdset_creation(self): self.add(_CmdA("C")) self.add(_CmdB("C")) + + class _CmdSetD(CmdSet): key = "D" - def at_cmdset_creation(self): + + def at_cmdset_creation(self): self.add(_CmdA("D")) self.add(_CmdB("D")) self.add(_CmdC("D")) @@ -59,8 +80,10 @@ class _CmdSetD(CmdSet): # testing Command Sets + class TestCmdSetMergers(TestCase): "Test merging of cmdsets" + def setUp(self): super(TestCmdSetMergers, self).setUp() self.cmdset_a = _CmdSetA() @@ -70,16 +93,16 @@ class TestCmdSetMergers(TestCase): def test_union(self): a, c = self.cmdset_a, self.cmdset_c - cmdset_f = a + c # same-prio + cmdset_f = a + c # same-prio self.assertEqual(len(cmdset_f.commands), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 2) - cmdset_f = c + a # same-prio, inverse order + cmdset_f = c + a # same-prio, inverse order self.assertEqual(len(cmdset_f.commands), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 0) a.priority = 1 - cmdset_f = a + c # high prio A + cmdset_f = a + c # high prio A self.assertEqual(len(cmdset_f.commands), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 0) @@ -87,16 +110,16 @@ class TestCmdSetMergers(TestCase): def test_intersect(self): a, c = self.cmdset_a, self.cmdset_c a.mergetype = "Intersect" - cmdset_f = a + c # same-prio - c's Union kicks in + cmdset_f = a + c # same-prio - c's Union kicks in self.assertEqual(len(cmdset_f.commands), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 2) - cmdset_f = c + a # same-prio - a's Intersect kicks in + cmdset_f = c + a # same-prio - a's Intersect kicks in self.assertEqual(len(cmdset_f.commands), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 0) a.priority = 1 - cmdset_f = a + c # high prio A, intersect kicks in + cmdset_f = a + c # high prio A, intersect kicks in self.assertEqual(len(cmdset_f.commands), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 0) @@ -104,16 +127,16 @@ class TestCmdSetMergers(TestCase): def test_replace(self): a, c = self.cmdset_a, self.cmdset_c c.mergetype = "Replace" - cmdset_f = a + c # same-prio. C's Replace kicks in + cmdset_f = a + c # same-prio. C's Replace kicks in self.assertEqual(len(cmdset_f.commands), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 0) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 2) - cmdset_f = c + a # same-prio. A's Union kicks in + cmdset_f = c + a # same-prio. A's Union kicks in self.assertEqual(len(cmdset_f.commands), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 0) c.priority = 1 - cmdset_f = c + a # c higher prio. C's Replace kicks in + cmdset_f = c + a # c higher prio. C's Replace kicks in self.assertEqual(len(cmdset_f.commands), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 0) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 2) @@ -121,16 +144,16 @@ class TestCmdSetMergers(TestCase): def test_remove(self): a, c = self.cmdset_a, self.cmdset_c c.mergetype = "Remove" - cmdset_f = a + c # same-prio. C's Remove kicks in + cmdset_f = a + c # same-prio. C's Remove kicks in self.assertEqual(len(cmdset_f.commands), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 0) - cmdset_f = c + a # same-prio. A's Union kicks in + cmdset_f = c + a # same-prio. A's Union kicks in self.assertEqual(len(cmdset_f.commands), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 4) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 0) c.priority = 1 - cmdset_f = c + a # c higher prio. C's Remove kicks in + cmdset_f = c + a # c higher prio. C's Remove kicks in self.assertEqual(len(cmdset_f.commands), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "A"), 2) self.assertEqual(sum(1 for cmd in cmdset_f.commands if cmd.from_cmdset == "C"), 0) @@ -138,15 +161,15 @@ class TestCmdSetMergers(TestCase): def test_order(self): "Merge in reverse- and forward orders, same priorities" a, b, c, d = self.cmdset_a, self.cmdset_b, self.cmdset_c, self.cmdset_d - cmdset_f = d + c + b + a # merge in reverse order of priority + cmdset_f = d + c + b + a # merge in reverse order of priority self.assertEqual(cmdset_f.priority, 0) self.assertEqual(cmdset_f.mergetype, "Union") self.assertEqual(len(cmdset_f.commands), 4) self.assertTrue(all(True for cmd in cmdset_f.commands if cmd.from_cmdset == "A")) - cmdset_f = a + b + c + d # merge in order of priority + cmdset_f = a + b + c + d # merge in order of priority self.assertEqual(cmdset_f.priority, 0) self.assertEqual(cmdset_f.mergetype, "Union") - self.assertEqual(len(cmdset_f.commands), 4) # duplicates setting from A transfers + self.assertEqual(len(cmdset_f.commands), 4) # duplicates setting from A transfers self.assertTrue(all(True for cmd in cmdset_f.commands if cmd.from_cmdset == "D")) def test_priority_order(self): @@ -156,12 +179,12 @@ class TestCmdSetMergers(TestCase): b.priority = 1 c.priority = 0 d.priority = -1 - cmdset_f = d + c + b + a # merge in reverse order of priority + cmdset_f = d + c + b + a # merge in reverse order of priority self.assertEqual(cmdset_f.priority, 2) self.assertEqual(cmdset_f.mergetype, "Union") self.assertEqual(len(cmdset_f.commands), 4) self.assertTrue(all(True for cmd in cmdset_f.commands if cmd.from_cmdset == "A")) - cmdset_f = a + b + c + d # merge in order of priority + cmdset_f = a + b + c + d # merge in order of priority self.assertEqual(cmdset_f.priority, 2) self.assertEqual(cmdset_f.mergetype, "Union") self.assertEqual(len(cmdset_f.commands), 4) @@ -176,13 +199,13 @@ class TestCmdSetMergers(TestCase): a.no_objs = True a.no_channels = True a.duplicates = True - cmdset_f = d + c + b + a # reverse, same-prio + cmdset_f = d + c + b + a # reverse, same-prio self.assertTrue(cmdset_f.no_exits) self.assertTrue(cmdset_f.no_objs) self.assertTrue(cmdset_f.no_channels) self.assertTrue(cmdset_f.duplicates) self.assertEqual(len(cmdset_f.commands), 8) - cmdset_f = a + b + c + d # forward, same-prio + cmdset_f = a + b + c + d # forward, same-prio self.assertTrue(cmdset_f.no_exits) self.assertTrue(cmdset_f.no_objs) self.assertTrue(cmdset_f.no_channels) @@ -192,13 +215,13 @@ class TestCmdSetMergers(TestCase): b.priority = 1 c.priority = 0 d.priority = -1 - cmdset_f = d + c + b + a # reverse, A top priority + cmdset_f = d + c + b + a # reverse, A top priority self.assertTrue(cmdset_f.no_exits) self.assertTrue(cmdset_f.no_objs) self.assertTrue(cmdset_f.no_channels) self.assertTrue(cmdset_f.duplicates) self.assertEqual(len(cmdset_f.commands), 4) - cmdset_f = a + b + c + d # forward, A top priority. This never happens in practice. + cmdset_f = a + b + c + d # forward, A top priority. This never happens in practice. self.assertTrue(cmdset_f.no_exits) self.assertTrue(cmdset_f.no_objs) self.assertTrue(cmdset_f.no_channels) @@ -208,13 +231,13 @@ class TestCmdSetMergers(TestCase): b.priority = 0 c.priority = 1 d.priority = 2 - cmdset_f = d + c + b + a # reverse, A low prio. This never happens in practice. + cmdset_f = d + c + b + a # reverse, A low prio. This never happens in practice. self.assertTrue(cmdset_f.no_exits) self.assertTrue(cmdset_f.no_objs) self.assertTrue(cmdset_f.no_channels) self.assertFalse(cmdset_f.duplicates) self.assertEqual(len(cmdset_f.commands), 4) - cmdset_f = a + b + c + d # forward, A low prio + cmdset_f = a + b + c + d # forward, A low prio self.assertTrue(cmdset_f.no_exits) self.assertTrue(cmdset_f.no_objs) self.assertTrue(cmdset_f.no_channels) @@ -224,7 +247,7 @@ class TestCmdSetMergers(TestCase): b.no_objs = False d.duplicates = False # higher-prio sets will change the option up the chain - cmdset_f = a + b + c + d # forward, A low prio + cmdset_f = a + b + c + d # forward, A low prio self.assertFalse(cmdset_f.no_exits) self.assertFalse(cmdset_f.no_objs) self.assertTrue(cmdset_f.no_channels) @@ -235,15 +258,19 @@ class TestCmdSetMergers(TestCase): c.priority = 0 d.priority = 0 c.duplicates = True - cmdset_f = d + b + c + a # two last mergers duplicates=True + cmdset_f = d + b + c + a # two last mergers duplicates=True self.assertEqual(len(cmdset_f.commands), 10) # test cmdhandler functions + from evennia.commands import cmdhandler from twisted.trial.unittest import TestCase as TwistedTestCase + + class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest): "Test the cmdhandler.get_and_merge_cmdsets function." + def setUp(self): super(TestGetAndMergeCmdSets, self).setUp() self.cmdset_a = _CmdSetA() @@ -261,6 +288,7 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest): a.no_channels = True self.set_cmdsets(self.session, a) deferred = cmdhandler.get_and_merge_cmdsets(self.session, self.session, None, None, "session", "") + def _callback(cmdset): self.assertEqual(cmdset.key, "A") deferred.addCallback(_callback) @@ -273,6 +301,7 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest): self.set_cmdsets(self.account, a) deferred = cmdhandler.get_and_merge_cmdsets(self.account, None, self.account, None, "account", "") # get_and_merge_cmdsets converts to lower-case internally. + def _callback(cmdset): pcmdset = AccountCmdSet() pcmdset.at_cmdset_creation() @@ -286,7 +315,8 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest): self.set_cmdsets(self.obj1, self.cmdset_a) deferred = cmdhandler.get_and_merge_cmdsets(self.obj1, None, None, self.obj1, "object", "") # get_and_merge_cmdsets converts to lower-case internally. - _callback = lambda cmdset: self.assertEqual(sum(1 for cmd in cmdset.commands if cmd.key in ("a", "b", "c", "d")), 4) + + def _callback(cmdset): return self.assertEqual(sum(1 for cmd in cmdset.commands if cmd.key in ("a", "b", "c", "d")), 4) deferred.addCallback(_callback) return deferred @@ -296,6 +326,7 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest): a.no_channels = True self.set_cmdsets(self.obj1, a, b, c, d) deferred = cmdhandler.get_and_merge_cmdsets(self.obj1, None, None, self.obj1, "object", "") + def _callback(cmdset): self.assertTrue(cmdset.no_exits) self.assertTrue(cmdset.no_channels) @@ -315,6 +346,7 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest): a, b, c, d = self.cmdset_a, self.cmdset_b, self.cmdset_c, self.cmdset_d self.set_cmdsets(self.account, a, b, c, d) deferred = cmdhandler.get_and_merge_cmdsets(self.session, self.session, self.account, self.char1, "session", "") + def _callback(cmdset): pcmdset = AccountCmdSet() pcmdset.at_cmdset_creation() @@ -332,6 +364,7 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest): d.duplicates = True self.set_cmdsets(self.obj1, a, b, c, d) deferred = cmdhandler.get_and_merge_cmdsets(self.obj1, None, None, self.obj1, "object", "") + def _callback(cmdset): self.assertEqual(len(cmdset.commands), 9) deferred.addCallback(_callback) diff --git a/evennia/comms/admin.py b/evennia/comms/admin.py index 606474799..29a02507c 100644 --- a/evennia/comms/admin.py +++ b/evennia/comms/admin.py @@ -59,7 +59,7 @@ class ChannelAdmin(admin.ModelAdmin): list_select_related = True fieldsets = ( (None, {'fields': (('db_key',), 'db_lock_storage', 'db_subscriptions')}), - ) + ) def subscriptions(self, obj): """ diff --git a/evennia/comms/channelhandler.py b/evennia/comms/channelhandler.py index b5f79515b..8683c0c9e 100644 --- a/evennia/comms/channelhandler.py +++ b/evennia/comms/channelhandler.py @@ -34,6 +34,7 @@ from django.utils.translation import ugettext as _ _CHANNEL_COMMAND_CLASS = None _CHANNELDB = None + class ChannelCommand(command.Command): """ {channelkey} channel @@ -130,8 +131,9 @@ class ChannelCommand(command.Command): if self.history_start is not None: # Try to view history log_file = channel.attributes.get("log_file", default="channel_%s.log" % channel.key) - send_msg = lambda lines: self.msg("".join(line.split("[-]", 1)[1] - if "[-]" in line else line for line in lines)) + + def send_msg(lines): return self.msg("".join(line.split("[-]", 1)[1] + if "[-]" in line else line for line in lines)) tail_log_file(log_file, self.history_start, 20, callback=send_msg) else: caller = caller if not hasattr(caller, 'account') else caller.account @@ -164,6 +166,7 @@ class ChannelHandler(object): evennia.create_channel()) """ + def __init__(self): """ Initializes the channel handler's internal state. @@ -208,12 +211,12 @@ class ChannelHandler(object): # map the channel to a searchable command cmd = _CHANNEL_COMMAND_CLASS( - key=channel.key.strip().lower(), - aliases=channel.aliases.all(), - locks="cmd:all();%s" % channel.locks, - help_category="Channel names", - obj=channel, - is_channel=True) + key=channel.key.strip().lower(), + aliases=channel.aliases.all(), + locks="cmd:all();%s" % channel.locks, + help_category="Channel names", + obj=channel, + is_channel=True) # format the help entry key = channel.key cmd.__doc__ = cmd.__doc__.format(channelkey=key, @@ -221,7 +224,7 @@ class ChannelHandler(object): channeldesc=channel.attributes.get("desc", default="").strip()) self.cached_channel_cmds[channel] = cmd self.cached_cmdsets = {} - add_channel = add # legacy alias + add_channel = add # legacy alias def remove(self, channel): """ @@ -269,8 +272,8 @@ class ChannelHandler(object): # create a new cmdset holding all viable channels chan_cmdset = None chan_cmds = [channelcmd for channel, channelcmd in self.cached_channel_cmds.iteritems() - if channel.subscriptions.has(source_object) and - channelcmd.access(source_object, 'send')] + if channel.subscriptions.has(source_object) and + channelcmd.access(source_object, 'send')] if chan_cmds: chan_cmdset = cmdset.CmdSet() chan_cmdset.key = 'ChannelCmdSet' @@ -281,5 +284,6 @@ class ChannelHandler(object): self.cached_cmdsets[source_object] = chan_cmdset return chan_cmdset + CHANNEL_HANDLER = ChannelHandler() -CHANNELHANDLER = CHANNEL_HANDLER # legacy +CHANNELHANDLER = CHANNEL_HANDLER # legacy diff --git a/evennia/comms/comms.py b/evennia/comms/comms.py index f83f12810..e40de664d 100644 --- a/evennia/comms/comms.py +++ b/evennia/comms/comms.py @@ -357,7 +357,6 @@ class DefaultChannel(with_metaclass(TypeclassBase, ChannelDB)): # hooks def channel_prefix(self, msg=None, emit=False, **kwargs): - """ Hook method. How the channel should prefix itself for users. diff --git a/evennia/comms/managers.py b/evennia/comms/managers.py index b412127ff..14a746fa7 100644 --- a/evennia/comms/managers.py +++ b/evennia/comms/managers.py @@ -74,7 +74,7 @@ def identify_object(inp): if clsname == "AccountDB": return inp, "account" elif clsname == "ObjectDB": - return inp ,"object" + return inp, "object" elif clsname == "ChannelDB": return inp, "channel" if isinstance(inp, basestring): @@ -202,10 +202,10 @@ class MsgManager(TypedObjectManager): # explicitly exclude channel recipients if typ == 'account': return list(self.filter(db_sender_accounts=obj, - db_receivers_channels__isnull=True).exclude(db_hide_from_accounts=obj)) + db_receivers_channels__isnull=True).exclude(db_hide_from_accounts=obj)) elif typ == 'object': return list(self.filter(db_sender_objects=obj, - db_receivers_channels__isnull=True).exclude(db_hide_from_objects=obj)) + db_receivers_channels__isnull=True).exclude(db_hide_from_objects=obj)) else: raise CommError else: @@ -332,6 +332,7 @@ class ChannelDBManager(TypedObjectManager): subscribed to the Channel. """ + def get_all_channels(self): """ Get all channels. diff --git a/evennia/comms/migrations/0004_auto_20150118_1631.py b/evennia/comms/migrations/0004_auto_20150118_1631.py index 2b525c9b2..2d4ae9915 100644 --- a/evennia/comms/migrations/0004_auto_20150118_1631.py +++ b/evennia/comms/migrations/0004_auto_20150118_1631.py @@ -3,12 +3,14 @@ from __future__ import unicode_literals from django.db import models, migrations + def convert_defaults(apps, schema_editor): ChannelDB = apps.get_model("comms", "ChannelDB") for channel in ChannelDB.objects.filter(db_typeclass_path="src.comms.comms.Channel"): channel.db_typeclass_path = "typeclasses.channels.Channel" channel.save() + class Migration(migrations.Migration): dependencies = [ @@ -16,5 +18,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(convert_defaults), + migrations.RunPython(convert_defaults), ] diff --git a/evennia/comms/migrations/0005_auto_20150223_1517.py b/evennia/comms/migrations/0005_auto_20150223_1517.py index 5fcdfcd66..a26a6f63e 100644 --- a/evennia/comms/migrations/0005_auto_20150223_1517.py +++ b/evennia/comms/migrations/0005_auto_20150223_1517.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from django.db import migrations + def convert_channelnames(apps, schema_editor): ChannelDB = apps.get_model("comms", "ChannelDB") for chan in ChannelDB.objects.filter(db_key="MUDinfo"): @@ -13,6 +14,7 @@ def convert_channelnames(apps, schema_editor): chan.db_key = "MudInfo" chan.save() + class Migration(migrations.Migration): dependencies = [ @@ -20,5 +22,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(convert_channelnames), + migrations.RunPython(convert_channelnames), ] diff --git a/evennia/comms/migrations/0013_auto_20170705_1726.py b/evennia/comms/migrations/0013_auto_20170705_1726.py index e5c2dacb3..527cd22a5 100644 --- a/evennia/comms/migrations/0013_auto_20170705_1726.py +++ b/evennia/comms/migrations/0013_auto_20170705_1726.py @@ -26,41 +26,41 @@ class Migration(migrations.Migration): db_cursor = connection.cursor() operations = [ - migrations.AddField( - model_name='channeldb', - name='db_account_subscriptions', - field=models.ManyToManyField(blank=True, db_index=True, related_name='account_subscription_set', to='accounts.AccountDB', verbose_name=b'account subscriptions'), - ), - migrations.AlterField( - model_name='channeldb', - name='db_object_subscriptions', - field=models.ManyToManyField(blank=True, db_index=True, related_name='object_subscription_set', to='objects.ObjectDB', verbose_name=b'object subscriptions'), - ), - migrations.AlterField( - model_name='msg', - name='db_receivers_scripts', - field=models.ManyToManyField(blank=True, help_text=b'script_receivers', related_name='receiver_script_set', to='scripts.ScriptDB'), - ), - migrations.AlterField( - model_name='msg', - name='db_sender_scripts', - field=models.ManyToManyField(blank=True, db_index=True, related_name='sender_script_set', to='scripts.ScriptDB', verbose_name=b'sender(script)'), - ), - migrations.AlterField( - model_name='channeldb', - name='db_object_subscriptions', - field=models.ManyToManyField(blank=True, db_index=True, related_name='object_subscription_set', to='objects.ObjectDB', verbose_name=b'object subscriptions'), - ), - migrations.AlterField( - model_name='msg', - name='db_receivers_scripts', - field=models.ManyToManyField(blank=True, help_text=b'script_receivers', related_name='receiver_script_set', to='scripts.ScriptDB'), - ), - migrations.AlterField( - model_name='msg', - name='db_sender_scripts', - field=models.ManyToManyField(blank=True, db_index=True, related_name='sender_script_set', to='scripts.ScriptDB', verbose_name=b'sender(script)'), - ), + migrations.AddField( + model_name='channeldb', + name='db_account_subscriptions', + field=models.ManyToManyField(blank=True, db_index=True, related_name='account_subscription_set', to='accounts.AccountDB', verbose_name=b'account subscriptions'), + ), + migrations.AlterField( + model_name='channeldb', + name='db_object_subscriptions', + field=models.ManyToManyField(blank=True, db_index=True, related_name='object_subscription_set', to='objects.ObjectDB', verbose_name=b'object subscriptions'), + ), + migrations.AlterField( + model_name='msg', + name='db_receivers_scripts', + field=models.ManyToManyField(blank=True, help_text=b'script_receivers', related_name='receiver_script_set', to='scripts.ScriptDB'), + ), + migrations.AlterField( + model_name='msg', + name='db_sender_scripts', + field=models.ManyToManyField(blank=True, db_index=True, related_name='sender_script_set', to='scripts.ScriptDB', verbose_name=b'sender(script)'), + ), + migrations.AlterField( + model_name='channeldb', + name='db_object_subscriptions', + field=models.ManyToManyField(blank=True, db_index=True, related_name='object_subscription_set', to='objects.ObjectDB', verbose_name=b'object subscriptions'), + ), + migrations.AlterField( + model_name='msg', + name='db_receivers_scripts', + field=models.ManyToManyField(blank=True, help_text=b'script_receivers', related_name='receiver_script_set', to='scripts.ScriptDB'), + ), + migrations.AlterField( + model_name='msg', + name='db_sender_scripts', + field=models.ManyToManyField(blank=True, db_index=True, related_name='sender_script_set', to='scripts.ScriptDB', verbose_name=b'sender(script)'), + ), ] if _table_exists(db_cursor, 'comms_msg_db_hide_from_players'): diff --git a/evennia/comms/models.py b/evennia/comms/models.py index fc2afb82d..3adb0c5c7 100644 --- a/evennia/comms/models.py +++ b/evennia/comms/models.py @@ -81,27 +81,27 @@ class Msg(SharedMemoryModel): # an IRC channel; normally there is only one, but if co-modification of # a message is allowed, there may be more than one "author" db_sender_accounts = models.ManyToManyField("accounts.AccountDB", related_name='sender_account_set', - blank=True, verbose_name='sender(account)', db_index=True) + blank=True, verbose_name='sender(account)', db_index=True) db_sender_objects = models.ManyToManyField("objects.ObjectDB", related_name='sender_object_set', blank=True, verbose_name='sender(object)', db_index=True) db_sender_scripts = models.ManyToManyField("scripts.ScriptDB", related_name='sender_script_set', blank=True, verbose_name='sender(script)', db_index=True) db_sender_external = models.CharField('external sender', max_length=255, null=True, blank=True, db_index=True, - help_text="identifier for external sender, for example a sender over an " - "IRC connection (i.e. someone who doesn't have an exixtence in-game).") + help_text="identifier for external sender, for example a sender over an " + "IRC connection (i.e. someone who doesn't have an exixtence in-game).") # The destination objects of this message. Stored as a # comma-separated string of object dbrefs. Can be defined along # with channels below. db_receivers_accounts = models.ManyToManyField('accounts.AccountDB', related_name='receiver_account_set', - blank=True, help_text="account receivers") + blank=True, help_text="account receivers") db_receivers_objects = models.ManyToManyField('objects.ObjectDB', related_name='receiver_object_set', blank=True, help_text="object receivers") db_receivers_scripts = models.ManyToManyField('scripts.ScriptDB', related_name='receiver_script_set', blank=True, help_text="script_receivers") db_receivers_channels = models.ManyToManyField("ChannelDB", related_name='channel_set', - blank=True, help_text="channel recievers") + blank=True, help_text="channel recievers") # header could be used for meta-info about the message if your system needs # it, or as a separate store for the mail subject line maybe. @@ -122,7 +122,7 @@ class Msg(SharedMemoryModel): db_hide_from_channels = models.ManyToManyField("ChannelDB", related_name='hide_from_channels_set', blank=True) db_tags = models.ManyToManyField(Tag, blank=True, - help_text='tags on this message. Tags are simple string markers to identify, group and alias messages.') + help_text='tags on this message. Tags are simple string markers to identify, group and alias messages.') # Database manager objects = managers.MsgManager() @@ -156,10 +156,10 @@ class Msg(SharedMemoryModel): #@property def __senders_get(self): "Getter. Allows for value = self.sender" - return list(self.db_sender_accounts.all()) + \ - list(self.db_sender_objects.all()) + \ - list(self.db_sender_scripts.all()) + \ - self.extra_senders + return list(self.db_sender_accounts.all()) + \ + list(self.db_sender_objects.all()) + \ + list(self.db_sender_scripts.all()) + \ + self.extra_senders #@sender.setter def __senders_set(self, senders): @@ -225,9 +225,9 @@ class Msg(SharedMemoryModel): Returns four lists of receivers: accounts, objects, scripts and channels. """ return list(self.db_receivers_accounts.all()) + \ - list(self.db_receivers_objects.all()) + \ - list(self.db_receivers_scripts.all()) + \ - list(self.db_receivers_channels.all()) + list(self.db_receivers_objects.all()) + \ + list(self.db_receivers_scripts.all()) + \ + list(self.db_receivers_channels.all()) #@receivers.setter def __receivers_set(self, receivers): @@ -250,7 +250,6 @@ class Msg(SharedMemoryModel): elif clsname == "ChannelDB": self.db_receivers_channels.add(receiver) - #@receivers.deleter def __receivers_del(self): "Deleter. Clears all receivers" @@ -370,6 +369,7 @@ class Msg(SharedMemoryModel): # #------------------------------------------------------------ + class TempMsg(object): """ This is a non-persistent object for sending temporary messages @@ -377,6 +377,7 @@ class TempMsg(object): doesn't require sender to be given. """ + def __init__(self, senders=None, receivers=None, channels=None, message="", header="", type="", lockstring="", hide_from=None): """ Creates the temp message. @@ -471,6 +472,7 @@ class SubscriptionHandler(object): channel and hides away which type of entity is subscribing (Account or Object) """ + def __init__(self, obj): """ Initialize the handler @@ -620,10 +622,10 @@ class ChannelDB(TypedObject): """ db_account_subscriptions = models.ManyToManyField("accounts.AccountDB", - related_name="account_subscription_set", blank=True, verbose_name='account subscriptions', db_index=True) + related_name="account_subscription_set", blank=True, verbose_name='account subscriptions', db_index=True) db_object_subscriptions = models.ManyToManyField("objects.ObjectDB", - related_name="object_subscription_set", blank=True, verbose_name='object subscriptions', db_index=True) + related_name="object_subscription_set", blank=True, verbose_name='object subscriptions', db_index=True) # Database manager objects = managers.ChannelDBManager() diff --git a/evennia/contrib/__init__.py b/evennia/contrib/__init__.py index ff710f52c..428b3afcd 100644 --- a/evennia/contrib/__init__.py +++ b/evennia/contrib/__init__.py @@ -14,7 +14,7 @@ See README.md for more info. # own code, so somthing needs to be done here. See issue #766. /Griatch #import evennia -#evennia._init() +# evennia._init() #import barter, dice, extended_room, menu_login, talking_npc #import chargen, email_login, gendersub, menusystem, slow_exit #import tutorial_world, tutorial_examples diff --git a/evennia/contrib/barter.py b/evennia/contrib/barter.py index 9932cc769..71ea33694 100644 --- a/evennia/contrib/barter.py +++ b/evennia/contrib/barter.py @@ -105,6 +105,7 @@ class TradeTimeout(DefaultScript): """ This times out the trade request, in case player B did not reply in time. """ + def at_script_creation(self): """ Called when script is first created @@ -136,6 +137,7 @@ class TradeHandler(object): Objects of this class handles the ongoing trade, notably storing the current offers from each side and wether both have accepted or not. """ + def __init__(self, part_a, part_b): """ Initializes the trade. This is called when part A tries to @@ -391,6 +393,7 @@ class CmdTradeBase(Command): Base command for Trade commands to inherit from. Implements the custom parsing. """ + def parse(self): """ Parse the relevant parts and make it easily diff --git a/evennia/contrib/chargen.py b/evennia/contrib/chargen.py index 679400387..19689e42b 100644 --- a/evennia/contrib/chargen.py +++ b/evennia/contrib/chargen.py @@ -102,7 +102,7 @@ class CmdOOCLook(default_cmds.CmdLook): else: charlist = "You have no Characters." string = \ -""" You, %s, are an |wOOC ghost|n without form. The world is hidden + """ You, %s, are an |wOOC ghost|n without form. The world is hidden from you and besides chatting on channels your options are limited. You need to have a Character in order to interact with the world. @@ -179,6 +179,7 @@ class OOCCmdSetCharGen(default_cmds.AccountCmdSet): """ Extends the default OOC cmdset. """ + def at_cmdset_creation(self): """Install everything from the default set, then overload""" self.add(CmdOOCLook()) diff --git a/evennia/contrib/clothing.py b/evennia/contrib/clothing.py index 7fc85b7c8..4e1935c99 100644 --- a/evennia/contrib/clothing.py +++ b/evennia/contrib/clothing.py @@ -88,11 +88,11 @@ CLOTHING_TYPE_ORDER = ['hat', 'jewelry', 'top', 'undershirt', 'gloves', 'fullbod 'underpants', 'socks', 'shoes', 'accessory'] # The maximum number of each type of clothes that can be worn. Unlimited if untyped or not specified. CLOTHING_TYPE_LIMIT = { - 'hat': 1, - 'gloves': 1, - 'socks': 1, - 'shoes': 1 - } + 'hat': 1, + 'gloves': 1, + 'socks': 1, + 'shoes': 1 +} # The maximum number of clothing items that can be worn, or None for unlimited. CLOTHING_OVERALL_LIMIT = 20 # What types of clothes will automatically cover what other types of clothes when worn. @@ -100,11 +100,11 @@ CLOTHING_OVERALL_LIMIT = 20 # on that auto-covers it - for example, it's perfectly possible to have your underpants # showing if you put them on after your pants! CLOTHING_TYPE_AUTOCOVER = { - 'top': ['undershirt'], - 'bottom': ['underpants'], - 'fullbody': ['undershirt', 'underpants'], - 'shoes': ['socks'] - } + 'top': ['undershirt'], + 'bottom': ['underpants'], + 'fullbody': ['undershirt', 'underpants'], + 'shoes': ['socks'] +} # Types of clothes that can't be used to cover other clothes. CLOTHING_TYPE_CANT_COVER_WITH = ['jewelry'] @@ -295,6 +295,7 @@ class ClothedCharacter(DefaultCharacter): just copy the return_appearance hook defined below to your own game's character typeclass. """ + def return_appearance(self, looker): """ This formats a description. It is the hook a 'look' command diff --git a/evennia/contrib/color_markups.py b/evennia/contrib/color_markups.py index 66991e679..4d3e7a887 100644 --- a/evennia/contrib/color_markups.py +++ b/evennia/contrib/color_markups.py @@ -166,14 +166,14 @@ CURLY_COLOR_XTERM256_EXTRA_GFG = [r'\{=([a-z])'] # |=a - greyscale foregrou CURLY_COLOR_XTERM256_EXTRA_GBG = [r'\{\[=([a-z])'] # |[=a - greyscale background CURLY_COLOR_ANSI_XTERM256_BRIGHT_BG_EXTRA_MAP = [ - (r'{[r', r'{[500'), - (r'{[g', r'{[050'), - (r'{[y', r'{[550'), - (r'{[b', r'{[005'), - (r'{[m', r'{[505'), - (r'{[c', r'{[055'), - (r'{[w', r'{[555'), # white background - (r'{[x', r'{[222'), # dark grey background + (r'{[r', r'{[500'), + (r'{[g', r'{[050'), + (r'{[y', r'{[550'), + (r'{[b', r'{[005'), + (r'{[m', r'{[505'), + (r'{[c', r'{[055'), + (r'{[w', r'{[555'), # white background + (r'{[x', r'{[222'), # dark grey background ] @@ -191,34 +191,34 @@ CURLY_COLOR_ANSI_XTERM256_BRIGHT_BG_EXTRA_MAP = [ ############################################################# MUX_COLOR_ANSI_EXTRA_MAP = [ - (r'%cn', _ANSI_NORMAL), # reset - (r'%ch', _ANSI_HILITE), # highlight - (r'%r', _ANSI_RETURN), # line break - (r'%R', _ANSI_RETURN), # - (r'%t', _ANSI_TAB), # tab - (r'%T', _ANSI_TAB), # - (r'%b', _ANSI_SPACE), # space - (r'%B', _ANSI_SPACE), - (r'%cf', _ANSI_BLINK), # annoying and not supported by all clients - (r'%ci', _ANSI_INVERSE), # invert + (r'%cn', _ANSI_NORMAL), # reset + (r'%ch', _ANSI_HILITE), # highlight + (r'%r', _ANSI_RETURN), # line break + (r'%R', _ANSI_RETURN), # + (r'%t', _ANSI_TAB), # tab + (r'%T', _ANSI_TAB), # + (r'%b', _ANSI_SPACE), # space + (r'%B', _ANSI_SPACE), + (r'%cf', _ANSI_BLINK), # annoying and not supported by all clients + (r'%ci', _ANSI_INVERSE), # invert - (r'%cr', _ANSI_RED), - (r'%cg', _ANSI_GREEN), - (r'%cy', _ANSI_YELLOW), - (r'%cb', _ANSI_BLUE), - (r'%cm', _ANSI_MAGENTA), - (r'%cc', _ANSI_CYAN), - (r'%cw', _ANSI_WHITE), - (r'%cx', _ANSI_BLACK), + (r'%cr', _ANSI_RED), + (r'%cg', _ANSI_GREEN), + (r'%cy', _ANSI_YELLOW), + (r'%cb', _ANSI_BLUE), + (r'%cm', _ANSI_MAGENTA), + (r'%cc', _ANSI_CYAN), + (r'%cw', _ANSI_WHITE), + (r'%cx', _ANSI_BLACK), - (r'%cR', _ANSI_BACK_RED), - (r'%cG', _ANSI_BACK_GREEN), - (r'%cY', _ANSI_BACK_YELLOW), - (r'%cB', _ANSI_BACK_BLUE), - (r'%cM', _ANSI_BACK_MAGENTA), - (r'%cC', _ANSI_BACK_CYAN), - (r'%cW', _ANSI_BACK_WHITE), - (r'%cX', _ANSI_BACK_BLACK) + (r'%cR', _ANSI_BACK_RED), + (r'%cG', _ANSI_BACK_GREEN), + (r'%cY', _ANSI_BACK_YELLOW), + (r'%cB', _ANSI_BACK_BLUE), + (r'%cM', _ANSI_BACK_MAGENTA), + (r'%cC', _ANSI_BACK_CYAN), + (r'%cW', _ANSI_BACK_WHITE), + (r'%cX', _ANSI_BACK_BLACK) ] MUX_COLOR_XTERM256_EXTRA_FG = [r'%c([0-5])([0-5])([0-5])'] # %c123 - foreground colour @@ -227,12 +227,12 @@ MUX_COLOR_XTERM256_EXTRA_GFG = [r'%c=([a-z])'] # %c=a - greyscale foregroun MUX_COLOR_XTERM256_EXTRA_GBG = [r'%c\[=([a-z])'] # %c[=a - greyscale background MUX_COLOR_ANSI_XTERM256_BRIGHT_BG_EXTRA_MAP = [ - (r'%ch%cR', r'%c[500'), - (r'%ch%cG', r'%c[050'), - (r'%ch%cY', r'%c[550'), - (r'%ch%cB', r'%c[005'), - (r'%ch%cM', r'%c[505'), - (r'%ch%cC', r'%c[055'), - (r'%ch%cW', r'%c[555'), # white background - (r'%ch%cX', r'%c[222'), # dark grey background + (r'%ch%cR', r'%c[500'), + (r'%ch%cG', r'%c[050'), + (r'%ch%cY', r'%c[550'), + (r'%ch%cB', r'%c[005'), + (r'%ch%cM', r'%c[505'), + (r'%ch%cC', r'%c[055'), + (r'%ch%cW', r'%c[555'), # white background + (r'%ch%cX', r'%c[222'), # dark grey background ] diff --git a/evennia/contrib/custom_gametime.py b/evennia/contrib/custom_gametime.py index 199e6b6f8..42a53d138 100644 --- a/evennia/contrib/custom_gametime.py +++ b/evennia/contrib/custom_gametime.py @@ -45,15 +45,15 @@ TIMEFACTOR = settings.TIME_FACTOR # Each unit must be consistent and expressed in seconds. UNITS = getattr(settings, "TIME_UNITS", { # default custom calendar - "sec": 1, - "min": 60, - "hr": 60 * 60, - "hour": 60 * 60, - "day": 60 * 60 * 24, - "week": 60 * 60 * 24 * 7, - "month": 60 * 60 * 24 * 7 * 4, - "yr": 60 * 60 * 24 * 7 * 4 * 12, - "year": 60 * 60 * 24 * 7 * 4 * 12, }) + "sec": 1, + "min": 60, + "hr": 60 * 60, + "hour": 60 * 60, + "day": 60 * 60 * 24, + "week": 60 * 60 * 24 * 7, + "month": 60 * 60 * 24 * 7 * 4, + "yr": 60 * 60 * 24 * 7 * 4 * 12, + "year": 60 * 60 * 24 * 7 * 4 * 12, }) def time_to_tuple(seconds, *divisors): @@ -111,8 +111,8 @@ def gametime_to_realtime(format=False, **kwargs): name = name[:-1] if name not in UNITS: - raise ValueError("the unit {} isn't defined as a valid " \ - "game time unit".format(name)) + raise ValueError("the unit {} isn't defined as a valid " + "game time unit".format(name)) rtime += value * UNITS[name] rtime /= TIMEFACTOR if format: @@ -121,7 +121,7 @@ def gametime_to_realtime(format=False, **kwargs): def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0, weeks=0, - months=0, yrs=0, format=False): + months=0, yrs=0, format=False): """ This method calculates how much in-game time a real-world time interval would correspond to. This is usually a lot less @@ -140,7 +140,7 @@ def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0, weeks=0, """ gtime = TIMEFACTOR * (secs + mins * 60 + hrs * 3600 + days * 86400 + - weeks * 604800 + months * 2628000 + yrs * 31536000) + weeks * 604800 + months * 2628000 + yrs * 31536000) if format: units = sorted(set(UNITS.values()), reverse=True) # Remove seconds from the tuple @@ -149,6 +149,7 @@ def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0, weeks=0, return time_to_tuple(gtime, *units) return gtime + def custom_gametime(absolute=False): """ Return the custom game time as a tuple of units, as defined in settings. @@ -168,6 +169,7 @@ def custom_gametime(absolute=False): del units[-1] return time_to_tuple(current, *units) + def real_seconds_until(**kwargs): """ Return the real seconds until game time. @@ -228,6 +230,7 @@ def real_seconds_until(**kwargs): return (projected - current) / TIMEFACTOR + def schedule(callback, repeat=False, **kwargs): """ Call the callback when the game time is up. @@ -256,14 +259,16 @@ def schedule(callback, repeat=False, **kwargs): """ seconds = real_seconds_until(**kwargs) script = create_script("evennia.contrib.custom_gametime.GametimeScript", - key="GametimeScript", desc="A timegame-sensitive script", - interval=seconds, start_delay=True, - repeats=-1 if repeat else 1) + key="GametimeScript", desc="A timegame-sensitive script", + interval=seconds, start_delay=True, + repeats=-1 if repeat else 1) script.db.callback = callback script.db.gametime = kwargs return script # Scripts dealing in gametime (use `schedule` to create it) + + class GametimeScript(DefaultScript): """Gametime-sensitive script.""" diff --git a/evennia/contrib/dice.py b/evennia/contrib/dice.py index e6c900d2a..374acc01f 100644 --- a/evennia/contrib/dice.py +++ b/evennia/contrib/dice.py @@ -118,6 +118,7 @@ def roll_dice(dicenum, dicetype, modifier=None, conditional=None, return_tuple=F else: return result + RE_PARTS = re.compile(r"(d|\+|-|/|\*|<|>|<=|>=|!=|==)") RE_MOD = re.compile(r"(\+|-|/|\*)") RE_COND = re.compile(r"(<|>|<=|>=|!=|==)") @@ -255,6 +256,7 @@ class DiceCmdSet(CmdSet): a small cmdset for testing purposes. Add with @py self.cmdset.add("contrib.dice.DiceCmdSet") """ + def at_cmdset_creation(self): """Called when set is created""" self.add(CmdDice()) diff --git a/evennia/contrib/egi_client/client.py b/evennia/contrib/egi_client/client.py index 39c684f8e..6bb49db2e 100644 --- a/evennia/contrib/egi_client/client.py +++ b/evennia/contrib/egi_client/client.py @@ -24,6 +24,7 @@ class EvenniaGameIndexClient(object): Evennia Game Index. Since EGI is in the early goings, this isn't incredibly configurable as far as what is being sent. """ + def __init__(self, on_bad_request=None): """ :param on_bad_request: Optional callable to trigger when a bad request @@ -131,6 +132,7 @@ class SimpleResponseReceiver(protocol.Protocol): """ Used for pulling the response body out of an HTTP response. """ + def __init__(self, status_code, d): self.status_code = status_code self.buf = '' diff --git a/evennia/contrib/extended_room.py b/evennia/contrib/extended_room.py index 244f2b5e4..962578fa1 100644 --- a/evennia/contrib/extended_room.py +++ b/evennia/contrib/extended_room.py @@ -108,6 +108,7 @@ class ExtendedRoom(DefaultRoom): time. It also allows for "details", together with a slightly modified look command. """ + def at_object_creation(self): """Called when room is first created only.""" self.db.spring_desc = "" @@ -281,6 +282,7 @@ class CmdExtendedLook(default_cmds.CmdLook): Observes your location, details at your location or objects in your vicinity. """ + def func(self): """ Handle the looking - add fallback to details. diff --git a/evennia/contrib/gendersub.py b/evennia/contrib/gendersub.py index 0160728fb..205f9a991 100644 --- a/evennia/contrib/gendersub.py +++ b/evennia/contrib/gendersub.py @@ -38,22 +38,23 @@ _GENDER_PRONOUN_MAP = {"male": {"s": "he", "p": "his", "a": "his"}, "female": {"s": "she", - "o": "her", - "p": "her", - "a": "hers"}, + "o": "her", + "p": "her", + "a": "hers"}, "neutral": {"s": "it", - "o": "it", - "p": "its", - "a": "its"}, + "o": "it", + "p": "its", + "a": "its"}, "ambiguous": {"s": "they", "o": "them", "p": "their", "a": "theirs"} - } + } _RE_GENDER_PRONOUN = re.compile(r'(? [= ]", - "@call/add = [parameters]", - "@call/edit = [callback number]", - "@call/del = [callback number]", - "@call/tasks [object name [= ]]", + "@call [= ]", + "@call/add = [parameters]", + "@call/edit = [callback number]", + "@call/del = [callback number]", + "@call/tasks [object name [= ]]", ] BASIC_SWITCHES = [ @@ -39,7 +39,7 @@ BASIC_SWITCHES = [ ] VALIDATOR_USAGES = [ - "@call/accept [object name = [callback number]]", + "@call/accept [object name = [callback number]]", ] VALIDATOR_SWITCHES = [ @@ -73,6 +73,7 @@ them and when. You can then accept a specific callback: Use the /del switch to remove callbacks that should not be connected. """ + class CmdCallback(COMMAND_DEFAULT_CLASS): """ @@ -141,8 +142,8 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): self.is_validator = validator self.autovalid = autovalid if self.handler is None: - caller.msg("The event handler is not running, can't " \ - "access the event system.") + caller.msg("The event handler is not running, can't " + "access the event system.") return # Before the equal sign, there is an object name or nothing @@ -170,8 +171,8 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): elif switch in ["tasks", "task"]: self.list_tasks() else: - caller.msg("Mutually exclusive or invalid switches were " \ - "used, cannot proceed.") + caller.msg("Mutually exclusive or invalid switches were " + "used, cannot proceed.") def list_callbacks(self): """Display the list of callbacks connected to the object.""" @@ -186,7 +187,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): created = callbacks.get(callback_name) if created is None: self.msg("No callback {} has been set on {}.".format(callback_name, - obj)) + obj)) return if parameters: @@ -197,7 +198,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): callback = callbacks[callback_name][number] except (ValueError, AssertionError, IndexError): self.msg("The callback {} {} cannot be found in {}.".format( - callback_name, parameters, obj)) + callback_name, parameters, obj)) return # Display the callback's details @@ -241,8 +242,8 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): if updated_on: updated_on = "{} ago".format(time_format( - (now - updated_on).total_seconds(), - 4).capitalize()) + (now - updated_on).total_seconds(), + 4).capitalize()) else: updated_on = "|gUnknown|n" parameters = callback.get("parameters", "") @@ -256,7 +257,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): else: names = list(set(list(types.keys()) + list(callbacks.keys()))) table = EvTable("Callback name", "Number", "Description", - valign="t", width=78) + valign="t", width=78) table.reformat_column(0, width=20) table.reformat_column(1, width=10, align="r") table.reformat_column(2, width=48) @@ -277,9 +278,9 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): types = self.handler.get_events(obj) # Check that the callback exists - if not callback_name.startswith("chain_") and not callback_name in types: - self.msg("The callback name {} can't be found in {} of " \ - "typeclass {}.".format(callback_name, obj, type(obj))) + if not callback_name.startswith("chain_") and callback_name not in types: + self.msg("The callback name {} can't be found in {} of " + "typeclass {}.".format(callback_name, obj, type(obj))) return definition = types.get(callback_name, (None, "Chained event.")) @@ -288,7 +289,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): # Open the editor callback = self.handler.add_callback(obj, callback_name, "", - self.caller, False, parameters=self.parameters) + self.caller, False, parameters=self.parameters) # Lock this callback right away self.handler.db.locked.append((obj, callback_name, callback["number"])) @@ -296,8 +297,8 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): # Open the editor for this callback self.caller.db._callback = callback EvEditor(self.caller, loadfunc=_ev_load, savefunc=_ev_save, - quitfunc=_ev_quit, key="Callback {} of {}".format( - callback_name, obj), persistent=True, codefunc=_ev_save) + quitfunc=_ev_quit, key="Callback {} of {}".format( + callback_name, obj), persistent=True, codefunc=_ev_save) def edit_callback(self): """Edit a callback.""" @@ -313,9 +314,9 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): return # Check that the callback exists - if not callback_name in callbacks: + if callback_name not in callbacks: self.msg("The callback name {} can't be found in {}.".format( - callback_name, obj)) + callback_name, obj)) return # If there's only one callback, just edit it @@ -335,7 +336,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): callback = callbacks[callback_name][number] except (ValueError, AssertionError, IndexError): self.msg("The callback {} {} cannot be found in {}.".format( - callback_name, parameters, obj)) + callback_name, parameters, obj)) return # If caller can't edit without validation, forbid editing @@ -361,7 +362,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): self.caller.db._callback = callback EvEditor(self.caller, loadfunc=_ev_load, savefunc=_ev_save, quitfunc=_ev_quit, key="Callback {} of {}".format( - callback_name, obj), persistent=True, codefunc=_ev_save) + callback_name, obj), persistent=True, codefunc=_ev_save) def del_callback(self): """Delete a callback.""" @@ -377,9 +378,9 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): return # Check that the callback exists - if not callback_name in callbacks: + if callback_name not in callbacks: self.msg("The callback name {} can't be found in {}.".format( - callback_name, obj)) + callback_name, obj)) return # If there's only one callback, just delete it @@ -388,8 +389,8 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): callback = callbacks[callback_name][0] else: if not parameters: - self.msg("Which callback do you wish to delete? Specify " \ - "a number.") + self.msg("Which callback do you wish to delete? Specify " + "a number.") self.list_callbacks() return @@ -400,7 +401,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): callback = callbacks[callback_name][number] except (ValueError, AssertionError, IndexError): self.msg("The callback {} {} cannot be found in {}.".format( - callback_name, parameters, obj)) + callback_name, parameters, obj)) return # If caller can't edit without validation, forbid deleting @@ -417,7 +418,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): # Delete the callback self.handler.del_callback(obj, callback_name, number) self.msg("The callback {}[{}] of {} was deleted.".format( - callback_name, number + 1, obj)) + callback_name, number + 1, obj)) def accept_callback(self): """Accept a callback.""" @@ -428,7 +429,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): # If no object, display the list of callbacks to be checked if obj is None: table = EvTable("ID", "Type", "Object", "Name", "Updated by", - "On", width=78) + "On", width=78) table.reformat_column(0, align="r") now = datetime.now() for obj, name, number in self.handler.db.to_valid: @@ -450,8 +451,8 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): if updated_on: updated_on = "{} ago".format(time_format( - (now - updated_on).total_seconds(), - 4).capitalize()) + (now - updated_on).total_seconds(), + 4).capitalize()) else: updated_on = "|gUnknown|n" @@ -469,9 +470,9 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): return # Check that the callback exists - if not callback_name in callbacks: + if callback_name not in callbacks: self.msg("The callback name {} can't be found in {}.".format( - callback_name, obj)) + callback_name, obj)) return if not parameters: @@ -486,7 +487,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): callback = callbacks[callback_name][number] except (ValueError, AssertionError, IndexError): self.msg("The callback {} {} cannot be found in {}.".format( - callback_name, parameters, obj)) + callback_name, parameters, obj)) return # Accept the callback @@ -495,7 +496,7 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): else: self.handler.accept_callback(obj, callback_name, number) self.msg("The callback {} {} of {} has been accepted.".format( - callback_name, parameters, obj)) + callback_name, parameters, obj)) def list_tasks(self): """List the active tasks.""" @@ -520,40 +521,44 @@ class CmdCallback(COMMAND_DEFAULT_CLASS): self.msg(unicode(table)) # Private functions to handle editing + + def _ev_load(caller): return caller.db._callback and caller.db._callback.get("code", "") or "" + def _ev_save(caller, buf): """Save and add the callback.""" lock = "perm({}) or perm(events_without_validation)".format( - WITHOUT_VALIDATION) + WITHOUT_VALIDATION) autovalid = caller.locks.check_lockstring(caller, lock) callback = caller.db._callback handler = get_event_handler() - if not handler or not callback or not all(key in callback for key in \ - ("obj", "name", "number", "valid")): + if not handler or not callback or not all(key in callback for key in + ("obj", "name", "number", "valid")): caller.msg("Couldn't save this callback.") return False if (callback["obj"], callback["name"], callback["number"]) in handler.db.locked: handler.db.locked.remove((callback["obj"], callback["name"], - callback["number"])) + callback["number"])) handler.edit_callback(callback["obj"], callback["name"], callback["number"], buf, - caller, valid=autovalid) + caller, valid=autovalid) return True + def _ev_quit(caller): callback = caller.db._callback handler = get_event_handler() - if not handler or not callback or not all(key in callback for key in \ - ("obj", "name", "number", "valid")): + if not handler or not callback or not all(key in callback for key in + ("obj", "name", "number", "valid")): caller.msg("Couldn't save this callback.") return False if (callback["obj"], callback["name"], callback["number"]) in handler.db.locked: handler.db.locked.remove((callback["obj"], callback["name"], - callback["number"])) + callback["number"])) del caller.db._callback caller.msg("Exited the code editor.") diff --git a/evennia/contrib/ingame_python/eventfuncs.py b/evennia/contrib/ingame_python/eventfuncs.py index 12ec39f82..daa918534 100644 --- a/evennia/contrib/ingame_python/eventfuncs.py +++ b/evennia/contrib/ingame_python/eventfuncs.py @@ -8,6 +8,7 @@ Eventfuncs are just Python functions that can be used inside of calllbacks. from evennia import ObjectDB, ScriptDB from evennia.contrib.ingame_python.utils import InterruptEvent + def deny(): """ Deny, that is stop, the callback here. @@ -22,6 +23,7 @@ def deny(): """ raise InterruptEvent + def get(**kwargs): """ Return an object with the given search option or None if None is found. @@ -53,6 +55,7 @@ def get(**kwargs): return object + def call_event(obj, event_name, seconds=0): """ Call the specified event in X seconds. diff --git a/evennia/contrib/ingame_python/scripts.py b/evennia/contrib/ingame_python/scripts.py index c0885c600..8ffdf172a 100644 --- a/evennia/contrib/ingame_python/scripts.py +++ b/evennia/contrib/ingame_python/scripts.py @@ -21,6 +21,7 @@ from evennia.contrib.ingame_python.utils import get_next_wait, EVENTS, Interrupt # Constants RE_LINE_ERROR = re.compile(r'^ File "\", line (\d+)') + class EventHandler(DefaultScript): """ @@ -94,7 +95,7 @@ class EventHandler(DefaultScript): self.ndb.channel = ChannelDB.objects.get(db_key="everror") except ChannelDB.DoesNotExist: self.ndb.channel = create_channel("everror", desc="Event errors", - locks="control:false();listen:perm(Builders);send:false()") + locks="control:false();listen:perm(Builders);send:false()") def get_events(self, obj): """ @@ -131,7 +132,7 @@ class EventHandler(DefaultScript): for key, etype in all_events.get(typeclass_name, {}).items(): if key in invalid: continue - if etype[0] is None: # Invalidate + if etype[0] is None: # Invalidate invalid.append(key) continue if key not in events: @@ -200,7 +201,7 @@ class EventHandler(DefaultScript): return callbacks def add_callback(self, obj, callback_name, code, author=None, valid=False, - parameters=""): + parameters=""): """ Add the specified callback. @@ -228,11 +229,11 @@ class EventHandler(DefaultScript): # Add the callback in the list callbacks.append({ - "created_on": datetime.now(), - "author": author, - "valid": valid, - "code": code, - "parameters": parameters, + "created_on": datetime.now(), + "author": author, + "valid": valid, + "code": code, + "parameters": parameters, }) # If not valid, set it in 'to_valid' @@ -241,7 +242,7 @@ class EventHandler(DefaultScript): # Call the custom_add if needed custom_add = self.get_events(obj).get( - callback_name, [None, None, None, None])[3] + callback_name, [None, None, None, None])[3] if custom_add: custom_add(obj, callback_name, len(callbacks) - 1, parameters) @@ -253,7 +254,7 @@ class EventHandler(DefaultScript): return definition def edit_callback(self, obj, callback_name, number, code, author=None, - valid=False): + valid=False): """ Edit the specified callback. @@ -288,10 +289,10 @@ class EventHandler(DefaultScript): # Edit the callback callbacks[number].update({ - "updated_on": datetime.now(), - "updated_by": author, - "valid": valid, - "code": code, + "updated_on": datetime.now(), + "updated_by": author, + "valid": valid, + "code": code, }) # If not valid, set it in 'to_valid' @@ -334,7 +335,7 @@ class EventHandler(DefaultScript): return else: logger.log_info("Deleting callback {} {} of {}:\n{}".format( - callback_name, number, obj, code)) + callback_name, number, obj, code)) del callbacks[number] # Change IDs of callbacks to be validated @@ -349,7 +350,7 @@ class EventHandler(DefaultScript): elif t_number > number: # Change the ID for this callback self.db.to_valid.insert(i, (t_obj, t_callback_name, - t_number - 1)) + t_number - 1)) del self.db.to_valid[i + 1] i += 1 @@ -414,13 +415,13 @@ class EventHandler(DefaultScript): # Errors should not pass silently allowed = ("number", "parameters", "locals") if any(k for k in kwargs if k not in allowed): - raise TypeError("Unknown keyword arguments were specified " \ - "to call callbacks: {}".format(kwargs)) + raise TypeError("Unknown keyword arguments were specified " + "to call callbacks: {}".format(kwargs)) event = self.get_events(obj).get(callback_name) if locals is None and not event: - logger.log_err("The callback {} for the object {} (typeclass " \ - "{}) can't be found".format(callback_name, obj, type(obj))) + logger.log_err("The callback {} for the object {} (typeclass " + "{}) can't be found".format(callback_name, obj, type(obj))) return False # Prepare the locals if necessary @@ -430,9 +431,9 @@ class EventHandler(DefaultScript): try: locals[variable] = args[i] except IndexError: - logger.log_trace("callback {} of {} ({}): need variable " \ - "{} in position {}".format(callback_name, obj, - type(obj), variable, i)) + logger.log_trace("callback {} of {} ({}): need variable " + "{} in position {}".format(callback_name, obj, + type(obj), variable, i)) return False else: locals = {key: value for key, value in locals.items()} @@ -482,9 +483,9 @@ class EventHandler(DefaultScript): number = callback["number"] obj = callback["obj"] oid = obj.id - logger.log_err("An error occurred during the callback {} of " \ - "{} (#{}), number {}\n{}".format(callback_name, obj, - oid, number + 1, "\n".join(trace))) + logger.log_err("An error occurred during the callback {} of " + "{} (#{}), number {}\n{}".format(callback_name, obj, + oid, number + 1, "\n".join(trace))) # Create the error message line = "|runknown|n" @@ -505,8 +506,8 @@ class EventHandler(DefaultScript): exc = raw(trace[-1].strip("\n").splitlines()[-1]) err_msg = "Error in {} of {} (#{})[{}], line {}:" \ - " {}\n{}".format(callback_name, obj, - oid, number + 1, lineno, line, exc) + " {}\n{}".format(callback_name, obj, + oid, number + 1, lineno, line, exc) # Inform the last updater if connected updater = callback.get("updated_by") @@ -517,8 +518,8 @@ class EventHandler(DefaultScript): updater.msg(err_msg) else: err_msg = "Error in {} of {} (#{})[{}], line {}:" \ - " {}\n {}".format(callback_name, obj, - oid, number + 1, lineno, line, exc) + " {}\n {}".format(callback_name, obj, + oid, number + 1, lineno, line, exc) self.ndb.channel.msg(err_msg) def add_event(self, typeclass, name, variables, help_text, custom_call, custom_add): @@ -655,8 +656,8 @@ def complete_task(task_id): return if task_id not in script.db.tasks: - logger.log_err("The task #{} was scheduled, but it cannot be " \ - "found".format(task_id)) + logger.log_err("The task #{} was scheduled, but it cannot be " + "found".format(task_id)) return delta, obj, callback_name, locals = script.db.tasks.pop(task_id) diff --git a/evennia/contrib/ingame_python/tests.py b/evennia/contrib/ingame_python/tests.py index 10eed40a4..496c93944 100644 --- a/evennia/contrib/ingame_python/tests.py +++ b/evennia/contrib/ingame_python/tests.py @@ -21,6 +21,7 @@ settings.EVENTS_CALENDAR = "standard" # Constants OLD_EVENTS = {} + class TestEventHandler(EvenniaTest): """ @@ -31,7 +32,7 @@ class TestEventHandler(EvenniaTest): """Create the event handler.""" super(TestEventHandler, self).setUp() self.handler = create_script( - "evennia.contrib.ingame_python.scripts.EventHandler") + "evennia.contrib.ingame_python.scripts.EventHandler") # Copy old events if necessary if OLD_EVENTS: @@ -64,7 +65,7 @@ class TestEventHandler(EvenniaTest): """Add a callback while needing validation.""" author = self.char1 self.handler.add_callback(self.room1, "dummy", - "character.db.strength = 40", author=author, valid=False) + "character.db.strength = 40", author=author, valid=False) callback = self.handler.get_callbacks(self.room1).get("dummy") callback = callback[0] self.assertIsNotNone(callback) @@ -78,18 +79,18 @@ class TestEventHandler(EvenniaTest): self.char1.db.strength = 10 locals = {"character": self.char1} self.assertTrue(self.handler.call( - self.room1, "dummy", locals=locals)) + self.room1, "dummy", locals=locals)) self.assertEqual(self.char1.db.strength, 10) def test_edit(self): """Test editing a callback.""" author = self.char1 self.handler.add_callback(self.room1, "dummy", - "character.db.strength = 60", author=author, valid=True) + "character.db.strength = 60", author=author, valid=True) # Edit it right away self.handler.edit_callback(self.room1, "dummy", 0, - "character.db.strength = 65", author=self.char2, valid=True) + "character.db.strength = 65", author=self.char2, valid=True) # Check that the callback was written callback = self.handler.get_callbacks(self.room1).get("dummy") @@ -103,35 +104,35 @@ class TestEventHandler(EvenniaTest): self.char1.db.strength = 10 locals = {"character": self.char1} self.assertTrue(self.handler.call( - self.room1, "dummy", locals=locals)) + self.room1, "dummy", locals=locals)) self.assertEqual(self.char1.db.strength, 65) def test_edit_validation(self): """Edit a callback when validation isn't automatic.""" author = self.char1 self.handler.add_callback(self.room1, "dummy", - "character.db.strength = 70", author=author, valid=True) + "character.db.strength = 70", author=author, valid=True) # Edit it right away self.handler.edit_callback(self.room1, "dummy", 0, - "character.db.strength = 80", author=self.char2, valid=False) + "character.db.strength = 80", author=self.char2, valid=False) # Run this dummy callback (shouldn't do anything) self.char1.db.strength = 10 locals = {"character": self.char1} self.assertTrue(self.handler.call( - self.room1, "dummy", locals=locals)) + self.room1, "dummy", locals=locals)) self.assertEqual(self.char1.db.strength, 10) def test_del(self): """Try to delete a callback.""" # Add 3 callbacks self.handler.add_callback(self.room1, "dummy", - "character.db.strength = 5", author=self.char1, valid=True) + "character.db.strength = 5", author=self.char1, valid=True) self.handler.add_callback(self.room1, "dummy", - "character.db.strength = 8", author=self.char2, valid=False) + "character.db.strength = 8", author=self.char2, valid=False) self.handler.add_callback(self.room1, "dummy", - "character.db.strength = 9", author=self.char1, valid=True) + "character.db.strength = 9", author=self.char1, valid=True) # Note that the second callback isn't valid self.assertIn((self.room1, "dummy", 1), self.handler.db.to_valid) @@ -160,16 +161,16 @@ class TestEventHandler(EvenniaTest): self.char1.db.strength = 10 locals = {"character": self.char1} self.assertTrue(self.handler.call( - self.room1, "dummy", locals=locals)) + self.room1, "dummy", locals=locals)) self.assertEqual(self.char1.db.strength, 9) def test_accept(self): """Accept an callback.""" # Add 2 callbacks self.handler.add_callback(self.room1, "dummy", - "character.db.strength = 5", author=self.char1, valid=True) + "character.db.strength = 5", author=self.char1, valid=True) self.handler.add_callback(self.room1, "dummy", - "character.db.strength = 8", author=self.char2, valid=False) + "character.db.strength = 8", author=self.char2, valid=False) # Note that the second callback isn't valid self.assertIn((self.room1, "dummy", 1), self.handler.db.to_valid) @@ -185,7 +186,7 @@ class TestEventHandler(EvenniaTest): self.char1.db.strength = 10 locals = {"character": self.char1} self.assertTrue(self.handler.call( - self.room1, "dummy", locals=locals)) + self.room1, "dummy", locals=locals)) self.assertEqual(self.char1.db.strength, 8) def test_call(self): @@ -201,14 +202,14 @@ class TestEventHandler(EvenniaTest): character.db.health = 0 """.strip("\n")) self.handler.add_callback(self.room1, "dummy", code, - author=self.char1, valid=True) + author=self.char1, valid=True) # Call the dummy callback self.assertTrue(self.handler.call( - self.room1, "dummy", locals={"character": self.char1})) + self.room1, "dummy", locals={"character": self.char1})) self.assertEqual(self.char1.db.health, 50) self.assertTrue(self.handler.call( - self.room1, "dummy", locals={"character": self.char2})) + self.room1, "dummy", locals={"character": self.char2})) self.assertEqual(self.char2.db.health, 0) def test_handler(self): @@ -217,7 +218,7 @@ class TestEventHandler(EvenniaTest): # Add an callback callback = self.room1.callbacks.add("dummy", "pass", author=self.char1, - valid=True) + valid=True) self.assertEqual(callback.obj, self.room1) self.assertEqual(callback.name, "dummy") self.assertEqual(callback.code, "pass") @@ -227,13 +228,13 @@ class TestEventHandler(EvenniaTest): # Edit this very callback new = self.room1.callbacks.edit("dummy", 0, "character.db.say = True", - author=self.char1, valid=True) + author=self.char1, valid=True) self.assertIn([new], self.room1.callbacks.all().values()) self.assertNotIn([callback], self.room1.callbacks.all().values()) # Try to call this callback self.assertTrue(self.room1.callbacks.call("dummy", - locals={"character": self.char2})) + locals={"character": self.char2})) self.assertTrue(self.char2.db.say) # Delete the callback @@ -249,7 +250,7 @@ class TestCmdCallback(CommandTest): """Create the callback handler.""" super(TestCmdCallback, self).setUp() self.handler = create_script( - "evennia.contrib.ingame_python.scripts.EventHandler") + "evennia.contrib.ingame_python.scripts.EventHandler") # Copy old events if necessary if OLD_EVENTS: @@ -287,7 +288,7 @@ class TestCmdCallback(CommandTest): # Add some callback self.handler.add_callback(self.exit, "traverse", "pass", - author=self.char1, valid=True) + author=self.char1, valid=True) # Try to obtain more details on a specific callback on exit table = self.call(CmdCallback(), "out = traverse") @@ -355,18 +356,18 @@ class TestCmdCallback(CommandTest): def test_del(self): """Add and remove an callback.""" self.handler.add_callback(self.exit, "traverse", "pass", - author=self.char1, valid=True) + author=self.char1, valid=True) # Try to delete the callback # char2 shouldn't be allowed to do so (that's not HIS callback) self.call(CmdCallback(), "/del out = traverse 1", caller=self.char2) self.assertTrue(len(self.handler.get_callbacks(self.exit).get( - "traverse", [])) == 1) + "traverse", [])) == 1) # Now, char1 should be allowed to delete it self.call(CmdCallback(), "/del out = traverse 1") self.assertTrue(len(self.handler.get_callbacks(self.exit).get( - "traverse", [])) == 0) + "traverse", [])) == 0) def test_lock(self): """Test the lock of multiple editing.""" @@ -414,7 +415,7 @@ class TestDefaultCallbacks(CommandTest): """Create the callback handler.""" super(TestDefaultCallbacks, self).setUp() self.handler = create_script( - "evennia.contrib.ingame_python.scripts.EventHandler") + "evennia.contrib.ingame_python.scripts.EventHandler") # Copy old events if necessary if OLD_EVENTS: @@ -450,7 +451,7 @@ class TestDefaultCallbacks(CommandTest): # Try the can_traverse callback self.handler.add_callback(self.exit, "can_traverse", code, - author=self.char1, valid=True) + author=self.char1, valid=True) # Have char1 move through the exit self.call(ExitCommand(), "", "You can leave.", obj=self.exit) @@ -458,13 +459,13 @@ class TestDefaultCallbacks(CommandTest): # Have char2 move through this exit self.call(ExitCommand(), "", "You cannot leave.", obj=self.exit, - caller=self.char2) + caller=self.char2) self.assertIs(self.char2.location, self.room1) # Try the traverse callback self.handler.del_callback(self.exit, "can_traverse", 0) self.handler.add_callback(self.exit, "traverse", "character.msg('Fine!')", - author=self.char1, valid=True) + author=self.char1, valid=True) # Have char2 move through the exit self.call(ExitCommand(), "", obj=self.exit, caller=self.char2) @@ -478,15 +479,15 @@ class TestDefaultCallbacks(CommandTest): # Test msg_arrive and msg_leave code = 'message = "{character} goes out."' self.handler.add_callback(self.exit, "msg_leave", code, - author=self.char1, valid=True) + author=self.char1, valid=True) # Have char1 move through the exit old_msg = self.char2.msg try: self.char2.msg = Mock() self.call(ExitCommand(), "", obj=self.exit) - stored_msg = [args[0] if args and args[0] else kwargs.get("text",utils.to_str(kwargs, force_string=True)) - for name, args, kwargs in self.char2.msg.mock_calls] + stored_msg = [args[0] if args and args[0] else kwargs.get("text", utils.to_str(kwargs, force_string=True)) + for name, args, kwargs in self.char2.msg.mock_calls] # Get the first element of a tuple if msg received a tuple instead of a string stored_msg = [smsg[0] if isinstance(smsg, tuple) else smsg for smsg in stored_msg] returned_msg = ansi.parse_ansi("\n".join(stored_msg), strip_ansi=True) @@ -496,18 +497,18 @@ class TestDefaultCallbacks(CommandTest): # Create a return exit back = create_object("evennia.objects.objects.DefaultExit", - key="in", location=self.room2, destination=self.room1) + key="in", location=self.room2, destination=self.room1) code = 'message = "{character} goes in."' self.handler.add_callback(self.exit, "msg_arrive", code, - author=self.char1, valid=True) + author=self.char1, valid=True) # Have char1 move through the exit old_msg = self.char2.msg try: self.char2.msg = Mock() self.call(ExitCommand(), "", obj=back) - stored_msg = [args[0] if args and args[0] else kwargs.get("text",utils.to_str(kwargs, force_string=True)) - for name, args, kwargs in self.char2.msg.mock_calls] + stored_msg = [args[0] if args and args[0] else kwargs.get("text", utils.to_str(kwargs, force_string=True)) + for name, args, kwargs in self.char2.msg.mock_calls] # Get the first element of a tuple if msg received a tuple instead of a string stored_msg = [smsg[0] if isinstance(smsg, tuple) else smsg for smsg in stored_msg] returned_msg = ansi.parse_ansi("\n".join(stored_msg), strip_ansi=True) diff --git a/evennia/contrib/ingame_python/typeclasses.py b/evennia/contrib/ingame_python/typeclasses.py index 5bce96bb2..e69983793 100644 --- a/evennia/contrib/ingame_python/typeclasses.py +++ b/evennia/contrib/ingame_python/typeclasses.py @@ -159,6 +159,7 @@ Variables you can use in this event: character: the character connected to this event. """ + @register_events class EventCharacter(DefaultCharacter): @@ -208,12 +209,12 @@ class EventCharacter(DefaultCharacter): exits = [o for o in location.contents if o.location is location and o.destination is destination] mapping = mapping or {} mapping.update({ - "character": self, + "character": self, }) if exits: exits[0].callbacks.call("msg_leave", self, exits[0], - location, destination, string, mapping) + location, destination, string, mapping) string = exits[0].callbacks.get_variable("message") mapping = exits[0].callbacks.get_variable("mapping") @@ -261,14 +262,14 @@ class EventCharacter(DefaultCharacter): exits = [] mapping = mapping or {} mapping.update({ - "character": self, + "character": self, }) if origin: exits = [o for o in destination.contents if o.location is destination and o.destination is origin] if exits: exits[0].callbacks.call("msg_arrive", self, exits[0], - origin, destination, string, mapping) + origin, destination, string, mapping) string = exits[0].callbacks.get_variable("message") mapping = exits[0].callbacks.get_variable("mapping") @@ -299,7 +300,7 @@ class EventCharacter(DefaultCharacter): Room = DefaultRoom if isinstance(origin, Room) and isinstance(destination, Room): can = self.callbacks.call("can_move", self, - origin, destination) + origin, destination) if can: can = origin.callbacks.call("can_move", self, origin) if can: @@ -489,6 +490,7 @@ Variables you can use in this event: destination: the character's location after moving. """ + @register_events class EventExit(DefaultExit): @@ -520,7 +522,7 @@ class EventExit(DefaultExit): is_character = inherits_from(traversing_object, DefaultCharacter) if is_character: allow = self.callbacks.call("can_traverse", traversing_object, - self, self.location) + self, self.location) if not allow: return @@ -529,7 +531,7 @@ class EventExit(DefaultExit): # After traversing if is_character: self.callbacks.call("traverse", traversing_object, - self, self.location, self.destination) + self, self.location, self.destination) # Object help @@ -573,6 +575,7 @@ Variables you can use in this event: object: the object connected to this event. """ + @register_events class EventObject(DefaultObject): @@ -621,6 +624,7 @@ class EventObject(DefaultObject): super(EventObject, self).at_drop(dropper) self.callbacks.call("drop", dropper, self) + # Room help ROOM_CAN_DELETE = """ Can the room be deleted? @@ -742,6 +746,7 @@ Variables you can use in this event: room: the room connected to this event. """ + @register_events class EventRoom(DefaultRoom): @@ -792,7 +797,7 @@ class EventRoom(DefaultRoom): """ allow = self.callbacks.call("can_say", speaker, self, message, - parameters=message) + parameters=message) if not allow: return @@ -802,7 +807,7 @@ class EventRoom(DefaultRoom): for present in [o for o in self.contents if isinstance( o, DefaultCharacter) and o is not speaker]: allow = present.callbacks.call("can_say", speaker, present, - message, parameters=message) + message, parameters=message) if not allow: return @@ -811,10 +816,10 @@ class EventRoom(DefaultRoom): # We force the next event to be called after the message # This will have to change when the Evennia API adds new hooks delay(0, self.callbacks.call, "say", speaker, self, message, - parameters=message) + parameters=message) for present in [o for o in self.contents if isinstance( o, DefaultCharacter) and o is not speaker]: delay(0, present.callbacks.call, "say", speaker, present, message, - parameters=message) + parameters=message) return message diff --git a/evennia/contrib/ingame_python/utils.py b/evennia/contrib/ingame_python/utils.py index 63f2966c9..71a7c9d4c 100644 --- a/evennia/contrib/ingame_python/utils.py +++ b/evennia/contrib/ingame_python/utils.py @@ -20,6 +20,7 @@ from evennia.contrib.custom_gametime import real_seconds_until as custom_rsu # Temporary storage for events waiting for the script to be started EVENTS = [] + def get_event_handler(): """Return the event handler or None.""" try: @@ -30,6 +31,7 @@ def get_event_handler(): return script + def register_events(path_or_typeclass): """ Register the events in this typeclass. @@ -84,6 +86,8 @@ def register_events(path_or_typeclass): return typeclass # Custom callbacks for specific event types + + def get_next_wait(format): """ Get the length of time in seconds before format. @@ -104,8 +108,8 @@ def get_next_wait(format): """ calendar = getattr(settings, "EVENTS_CALENDAR", None) if calendar is None: - logger.log_err("A time-related event has been set whereas " \ - "the gametime calendar has not been set in the settings.") + logger.log_err("A time-related event has been set whereas " + "the gametime calendar has not been set in the settings.") return elif calendar == "standard": rsu = standard_rsu @@ -131,8 +135,8 @@ def get_next_wait(format): break if not piece.isdigit(): - logger.log_trace("The time specified '{}' in {} isn't " \ - "a valid number".format(piece, format)) + logger.log_trace("The time specified '{}' in {} isn't " + "a valid number".format(piece, format)) return # Convert the piece to int @@ -154,6 +158,7 @@ def get_next_wait(format): usual = gametime_to_realtime(**kwargs) return until, usual, details + def time_event(obj, event_name, number, parameters): """ Create a time-related event. @@ -173,6 +178,7 @@ def time_event(obj, event_name, number, parameters): script.db.number = number script.ndb.usual = usual + def keyword_event(callbacks, parameters): """ Custom call for events with keywords (like push, or pull, or turn...). @@ -201,6 +207,7 @@ def keyword_event(callbacks, parameters): return to_call + def phrase_event(callbacks, parameters): """ Custom call for events with keywords in sentences (like say or whisper). @@ -236,6 +243,7 @@ def phrase_event(callbacks, parameters): return to_call + class InterruptEvent(RuntimeError): """ diff --git a/evennia/contrib/mail.py b/evennia/contrib/mail.py index 10d63b915..0f82fad47 100644 --- a/evennia/contrib/mail.py +++ b/evennia/contrib/mail.py @@ -22,6 +22,7 @@ _HEAD_CHAR = "|015-|n" _SUB_HEAD_CHAR = "-" _WIDTH = 78 + class CmdMail(default_cmds.MuxCommand): """ Commands that allow either IC or OOC communications @@ -253,4 +254,3 @@ class CmdMail(default_cmds.MuxCommand): self.caller.msg(_HEAD_CHAR * _WIDTH) else: self.caller.msg("There are no messages in your inbox.") - diff --git a/evennia/contrib/mapbuilder.py b/evennia/contrib/mapbuilder.py index 205222398..2b156eb46 100644 --- a/evennia/contrib/mapbuilder.py +++ b/evennia/contrib/mapbuilder.py @@ -173,6 +173,7 @@ def example1_build_temple(x, y, **kwargs): # This is generally mandatory. return room + # Include your trigger characters and build functions in a legend dict. EXAMPLE1_LEGEND = {("♣", "♠"): example1_build_forest, ("∩", "n"): example1_build_mountains, @@ -225,17 +226,17 @@ def example2_build_verticle_exit(x, y, **kwargs): if kwargs["iteration"] == 0: return - north_room = kwargs["room_dict"][(x, y-1)] - south_room = kwargs["room_dict"][(x, y+1)] + north_room = kwargs["room_dict"][(x, y - 1)] + south_room = kwargs["room_dict"][(x, y + 1)] # create exits in the rooms create_object(exits.Exit, key="south", - aliases=["s"], location=north_room, - destination=south_room) + aliases=["s"], location=north_room, + destination=south_room) create_object(exits.Exit, key="north", - aliases=["n"], location=south_room, - destination=north_room) + aliases=["n"], location=south_room, + destination=north_room) kwargs["caller"].msg("Connected: " + north_room.key + " & " + south_room.key) @@ -247,20 +248,21 @@ def example2_build_horizontal_exit(x, y, **kwargs): if kwargs["iteration"] == 0: return - west_room = kwargs["room_dict"][(x-1, y)] - east_room = kwargs["room_dict"][(x+1, y)] + west_room = kwargs["room_dict"][(x - 1, y)] + east_room = kwargs["room_dict"][(x + 1, y)] create_object(exits.Exit, key="east", - aliases=["e"], location=west_room, - destination=east_room) + aliases=["e"], location=west_room, + destination=east_room) create_object(exits.Exit, key="west", - aliases=["w"], location=east_room, - destination=west_room) + aliases=["w"], location=east_room, + destination=west_room) kwargs["caller"].msg("Connected: " + west_room.key + " & " + east_room.key) + # Include your trigger characters and build functions in a legend dict. EXAMPLE2_LEGEND = {("♣", "♠"): example2_build_forest, ("|"): example2_build_verticle_exit, @@ -339,37 +341,38 @@ def build_map(caller, game_map, legend, iterations=1, build_exits=True): y = loc_key[1] # north - if (x, y-1) in room_dict: - if room_dict[(x, y-1)]: + if (x, y - 1) in room_dict: + if room_dict[(x, y - 1)]: create_object(exits.Exit, key="north", aliases=["n"], location=location, - destination=room_dict[(x, y-1)]) + destination=room_dict[(x, y - 1)]) # east - if (x+1, y) in room_dict: - if room_dict[(x+1, y)]: + if (x + 1, y) in room_dict: + if room_dict[(x + 1, y)]: create_object(exits.Exit, key="east", aliases=["e"], location=location, - destination=room_dict[(x+1, y)]) + destination=room_dict[(x + 1, y)]) # south - if (x, y+1) in room_dict: - if room_dict[(x, y+1)]: + if (x, y + 1) in room_dict: + if room_dict[(x, y + 1)]: create_object(exits.Exit, key="south", aliases=["s"], location=location, - destination=room_dict[(x, y+1)]) + destination=room_dict[(x, y + 1)]) # west - if (x-1, y) in room_dict: - if room_dict[(x-1, y)]: + if (x - 1, y) in room_dict: + if room_dict[(x - 1, y)]: create_object(exits.Exit, key="west", aliases=["w"], location=location, - destination=room_dict[(x-1, y)]) + destination=room_dict[(x - 1, y)]) caller.msg("Map Created.") # access command + class CmdMapBuilder(COMMAND_DEFAULT_CLASS): """ Build a map from a 2D ASCII map. @@ -478,4 +481,3 @@ class CmdMapBuilder(COMMAND_DEFAULT_CLASS): # Pass map and legend to the build function. build_map(caller, game_map, legend, iterations, build_exits) - diff --git a/evennia/contrib/menu_login.py b/evennia/contrib/menu_login.py index d7dd455c1..44dbd5982 100644 --- a/evennia/contrib/menu_login.py +++ b/evennia/contrib/menu_login.py @@ -276,7 +276,7 @@ def create_password(caller, string_input): permissions = settings.PERMISSION_ACCOUNT_DEFAULT typeclass = settings.BASE_CHARACTER_TYPECLASS new_account = unloggedin._create_account(caller, accountname, - password, permissions) + password, permissions) if new_account: if settings.MULTISESSION_MODE < 2: default_home = ObjectDB.objects.get_id( diff --git a/evennia/contrib/multidescer.py b/evennia/contrib/multidescer.py index 4445304df..32b2c00d5 100644 --- a/evennia/contrib/multidescer.py +++ b/evennia/contrib/multidescer.py @@ -97,6 +97,7 @@ def _update_store(caller, key=None, desc=None, delete=False, swapkey=None): # eveditor save/load/quit functions + def _save_editor(caller, buffer): "Called when the editor saves its contents" key = caller.db._multidesc_editkey @@ -104,6 +105,7 @@ def _save_editor(caller, buffer): caller.msg("Saved description to key '%s'." % key) return True + def _load_editor(caller): "Called when the editor loads contents" key = caller.db._multidesc_editkey @@ -112,6 +114,7 @@ def _load_editor(caller): return caller.db.multidesc[match[0]][1] return "" + def _quit_editor(caller): "Called when the editor quits" del caller.db._multidesc_editkey @@ -161,13 +164,13 @@ class CmdMultiDesc(default_cmds.MuxCommand): # list all stored descriptions, either in full or cropped. # Note that we list starting from 1, not from 0. _update_store(caller) - do_crop = not "full" in switches + do_crop = "full" not in switches if do_crop: outtext = ["|w%s:|n %s" % (key, crop(desc)) - for key, desc in caller.db.multidesc] + for key, desc in caller.db.multidesc] else: outtext = ["\n|w%s:|n|n\n%s\n%s" % (key, "-" * (len(key) + 1), desc) - for key, desc in caller.db.multidesc] + for key, desc in caller.db.multidesc] caller.msg("|wStored descs:|n\n" + "\n".join(outtext)) return @@ -249,6 +252,6 @@ class CmdMultiDesc(default_cmds.MuxCommand): else: caller.msg("|wCurrent desc:|n\n%s" % caller.db.desc) - except DescValidateError, err: + except DescValidateError as err: # This is triggered by _key_to_index caller.msg(err) diff --git a/evennia/contrib/random_string_generator.py b/evennia/contrib/random_string_generator.py index cdf38d683..dcce163b7 100644 --- a/evennia/contrib/random_string_generator.py +++ b/evennia/contrib/random_string_generator.py @@ -57,6 +57,7 @@ import time from evennia import DefaultScript, ScriptDB from evennia.utils.create import create_script + class RejectedRegex(RuntimeError): """The provided regular expression has been rejected. @@ -331,7 +332,7 @@ class RandomStringGenerator(object): generated = script.db.generated.get(self.name, []) if element not in generated: raise ValueError("the string {} isn't stored as generated by the generator {}".format( - element, self.name)) + element, self.name)) generated.remove(element) diff --git a/evennia/contrib/rplanguage.py b/evennia/contrib/rplanguage.py index 2a342262e..215971964 100644 --- a/evennia/contrib/rplanguage.py +++ b/evennia/contrib/rplanguage.py @@ -148,13 +148,13 @@ class LanguageHandler(DefaultScript): don't know the language well enough). """ + def at_script_creation(self): "Called when script is first started" self.key = "language_handler" self.persistent = True self.db.language_storage = {} - def add(self, key="default", phonemes=_PHONEMES, grammar=_GRAMMAR, word_length_variance=0, noun_prefix="", noun_postfix="", vowels=_VOWELS, manual_translations=None, @@ -240,8 +240,8 @@ class LanguageHandler(DefaultScript): word = word.strip() lword = len(word) new_word = "" - wlen = max(0, lword + sum(randint(-1,1) for i - in range(word_length_variance))) + wlen = max(0, lword + sum(randint(-1, 1) for i + in range(word_length_variance))) if wlen not in grammar: # always create a translation, use random length structure = choice(grammar[choice(list(grammar))]) @@ -257,7 +257,7 @@ class LanguageHandler(DefaultScript): translation.update(dict((key.lower(), value.lower()) for key, value in manual_translations.items())) # store data - storage = {"translation" : translation, + storage = {"translation": translation, "grammar": grammar, "grammar2phonemes": dict(grammar2phonemes), "word_length_variance": word_length_variance, @@ -296,8 +296,8 @@ class LanguageHandler(DefaultScript): else: # make up translation on the fly. Length can # vary from un-translated word. - wlen = max(0, lword + sum(randint(-1,1) for i - in range(self.language["word_length_variance"]))) + wlen = max(0, lword + sum(randint(-1, 1) for i + in range(self.language["word_length_variance"]))) grammar = self.language["grammar"] if wlen not in grammar: # this word has no direct translation! @@ -347,6 +347,8 @@ class LanguageHandler(DefaultScript): # Language access functions _LANGUAGE_HANDLER = None + + def obfuscate_language(text, level=0.0, language="default"): """ Main access method for the language parser. @@ -412,7 +414,6 @@ def available_languages(): return list(_LANGUAGE_HANDLER.attributes.get("language_storage", {})) - #------------------------------------------------------------ # # Whisper obscuration @@ -427,6 +428,7 @@ def available_languages(): # #------------------------------------------------------------ + _RE_WHISPER_OBSCURE = [ re.compile(r"^$", _RE_FLAGS), # This is a Test! #0 full whisper re.compile(r"[ae]", _RE_FLAGS), # This -s - Test! #1 add uy @@ -434,7 +436,7 @@ _RE_WHISPER_OBSCURE = [ re.compile(r"[aeiouy]", _RE_FLAGS), # Th-s -s - T-st! #3 add all consonants re.compile(r"[aeiouybdhjlmnpqrv]", _RE_FLAGS), # T--s -s - T-st! #4 add hard consonants re.compile(r"[a-eg-rt-z]", _RE_FLAGS), # T--s -s - T-s-! #5 add all capitals - re.compile(r"[A-EG-RT-Za-eg-rt-z]", _RE_FLAGS), # ---s -s - --s-! #6 add f + re.compile(r"[A-EG-RT-Za-eg-rt-z]", _RE_FLAGS), # ---s -s - --s-! #6 add f re.compile(r"[A-EG-RT-Za-rt-z]", _RE_FLAGS), # ---s -s - --s-! #7 add s re.compile(r"[A-EG-RT-Za-z]", _RE_FLAGS), # ---- -- - ----! #8 add capital F re.compile(r"[A-RT-Za-z]", _RE_FLAGS), # ---- -- - ----! #9 add capital S @@ -460,4 +462,3 @@ def obfuscate_whisper(whisper, level=0.0): level = min(max(0.0, level), 1.0) olevel = int(13.0 * level) return _RE_WHISPER_OBSCURE[olevel].sub('...' if olevel == 13.0 else '-', whisper) - diff --git a/evennia/contrib/rpsystem.py b/evennia/contrib/rpsystem.py index 93ecb3cf6..340383e50 100644 --- a/evennia/contrib/rpsystem.py +++ b/evennia/contrib/rpsystem.py @@ -127,10 +127,10 @@ _NUM_SEP = "-" # Texts _EMOTE_NOMATCH_ERROR = \ -"""|RNo match for |r{ref}|R.|n""" + """|RNo match for |r{ref}|R.|n""" _EMOTE_MULTIMATCH_ERROR = \ -"""|RMultiple possibilities for {ref}: + """|RMultiple possibilities for {ref}: |r{reflist}|n""" _RE_FLAGS = re.MULTILINE + re.IGNORECASE + re.UNICODE @@ -142,7 +142,7 @@ _RE_PREFIX = re.compile(r"^%s" % _PREFIX, re.UNICODE) # marker. So entering "/tall man" will return groups ("", "tall") # and "/2-tall man" will return groups ("2", "tall"). _RE_OBJ_REF_START = re.compile(r"%s(?:([0-9]+)%s)*(\w+)" % - (_PREFIX, _NUM_SEP), _RE_FLAGS) + (_PREFIX, _NUM_SEP), _RE_FLAGS) _RE_LEFT_BRACKETS = re.compile(r"\{+", _RE_FLAGS) _RE_RIGHT_BRACKETS = re.compile(r"\}+", _RE_FLAGS) @@ -168,6 +168,7 @@ _RE_LANGUAGE = re.compile(r"(?:\((\w+)\))*(\".+?\")") # 2) for every person seeing the emote, parse this # intermediary form into the one valid for that char. + class EmoteError(Exception): pass @@ -240,6 +241,7 @@ def ordered_permutation_regex(sentence): regex = r"|".join(sorted(set(solution), key=len, reverse=True)) return regex + def regex_tuple_from_key_alias(obj): """ This will build a regex tuple for any object, not just from those @@ -358,13 +360,13 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False): """ # Load all candidate regex tuples [(regex, obj, sdesc/recog),...] candidate_regexes = \ - ([(_RE_SELF_REF, sender, sender.sdesc.get())] if hasattr(sender, "sdesc") else [])+ \ - ([sender.recog.get_regex_tuple(obj) for obj in candidates] if hasattr(sender, "recog") else []) + \ - [obj.sdesc.get_regex_tuple() - for obj in candidates if hasattr(obj, "sdesc")] + \ - [regex_tuple_from_key_alias(obj) # handle objects without sdescs - for obj in candidates if not (hasattr(obj, "recog") and - hasattr(obj, "sdesc"))] + ([(_RE_SELF_REF, sender, sender.sdesc.get())] if hasattr(sender, "sdesc") else []) + \ + ([sender.recog.get_regex_tuple(obj) for obj in candidates] if hasattr(sender, "recog") else []) + \ + [obj.sdesc.get_regex_tuple() + for obj in candidates if hasattr(obj, "sdesc")] + \ + [regex_tuple_from_key_alias(obj) # handle objects without sdescs + for obj in candidates if not (hasattr(obj, "recog") and + hasattr(obj, "sdesc"))] # filter out non-found data candidate_regexes = [tup for tup in candidate_regexes if tup] @@ -387,7 +389,7 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False): # start index forward for all candidates. # first see if there is a number given (e.g. 1-tall) - num_identifier, _ = marker_match.groups("") # return "" if no match, rather than None + num_identifier, _ = marker_match.groups("") # return "" if no match, rather than None istart0 = marker_match.start() istart = istart0 @@ -418,7 +420,7 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False): else: # multi-match. # was a numerical identifier given to help us separate the multi-match? - inum = min(max(0, int(num_identifier) - 1), nmatches-1) if num_identifier else None + inum = min(max(0, int(num_identifier) - 1), nmatches - 1) if num_identifier else None if inum is not None: # A valid inum is given. Use this to separate data. obj = bestmatches[inum][0] @@ -438,10 +440,10 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False): mapping[key] = obj else: refname = marker_match.group() - reflist = ["%s%s%s (%s%s)" % (inum+1, _NUM_SEP, - _RE_PREFIX.sub("", refname), text, - " (%s)" % sender.key if sender == ob else "") - for inum, (ob, text) in enumerate(obj)] + reflist = ["%s%s%s (%s%s)" % (inum + 1, _NUM_SEP, + _RE_PREFIX.sub("", refname), text, + " (%s)" % sender.key if sender == ob else "") + for inum, (ob, text) in enumerate(obj)] errors.append(_EMOTE_MULTIMATCH_ERROR.format( ref=marker_match.group(), reflist="\n ".join(reflist))) if search_mode: @@ -532,8 +534,8 @@ def send_emote(sender, receivers, emote, anonymous_add="first"): receiver_sdesc_mapping = dict((ref, process_recog(recog_get(obj), obj)) for ref, obj in obj_mapping.items()) except AttributeError: receiver_sdesc_mapping = dict((ref, process_sdesc(obj.sdesc.get(), obj) - if hasattr(obj, "sdesc") else process_sdesc(obj.key, obj)) - for ref, obj in obj_mapping.items()) + if hasattr(obj, "sdesc") else process_sdesc(obj.key, obj)) + for ref, obj in obj_mapping.items()) # make sure receiver always sees their real name rkey = "#%i" % receiver.id if rkey in receiver_sdesc_mapping: @@ -546,6 +548,7 @@ def send_emote(sender, receivers, emote, anonymous_add="first"): # Handlers for sdesc and recog #------------------------------------------------------------ + class SdescHandler(object): """ This Handler wraps all operations with sdescs. We @@ -559,6 +562,7 @@ class SdescHandler(object): _regex - an empty dictionary """ + def __init__(self, obj): """ Initialize the handler @@ -600,10 +604,10 @@ class SdescHandler(object): """ # strip emote components from sdesc sdesc = _RE_REF.sub(r"\1", - _RE_REF_LANG.sub(r"\1", - _RE_SELF_REF.sub(r"", - _RE_LANGUAGE.sub(r"", - _RE_OBJ_REF_START.sub(r"", sdesc))))) + _RE_REF_LANG.sub(r"\1", + _RE_SELF_REF.sub(r"", + _RE_LANGUAGE.sub(r"", + _RE_OBJ_REF_START.sub(r"", sdesc))))) # make an sdesc clean of ANSI codes cleaned_sdesc = ansi.strip_ansi(sdesc) @@ -656,6 +660,7 @@ class RecogHandler(object): _recog_obj2regex """ + def __init__(self, obj): """ Initialize the handler @@ -679,9 +684,9 @@ class RecogHandler(object): obj2regex = self.obj.attributes.get("_recog_obj2regex", default={}) obj2recog = self.obj.attributes.get("_recog_obj2recog", default={}) self.obj2regex = dict((obj, re.compile(regex, _RE_FLAGS)) - for obj, regex in obj2regex.items() if obj) + for obj, regex in obj2regex.items() if obj) self.obj2recog = dict((obj, recog) - for obj, recog in obj2recog.items() if obj) + for obj, recog in obj2recog.items() if obj) def add(self, obj, recog, max_length=60): """ @@ -705,10 +710,10 @@ class RecogHandler(object): """ # strip emote components from recog recog = _RE_REF.sub(r"\1", - _RE_REF_LANG.sub(r"\1", - _RE_SELF_REF.sub(r"", - _RE_LANGUAGE.sub(r"", - _RE_OBJ_REF_START.sub(r"", recog))))) + _RE_REF_LANG.sub(r"\1", + _RE_SELF_REF.sub(r"", + _RE_LANGUAGE.sub(r"", + _RE_OBJ_REF_START.sub(r"", recog))))) # make an recog clean of ANSI codes cleaned_recog = ansi.strip_ansi(recog) @@ -751,7 +756,7 @@ class RecogHandler(object): # to avoid revealing masked characters. If lock # does not exist, pass automatically. return self.obj2recog.get(obj, obj.sdesc.get() - if hasattr(obj, "sdesc") else obj.key) + if hasattr(obj, "sdesc") else obj.key) else: # recog_mask log not passed, disable recog return obj.sdesc.get() if hasattr(obj, "sdesc") else obj.key @@ -785,6 +790,7 @@ class RecogHandler(object): class RPCommand(Command): "simple parent" + def parse(self): "strip extra whitespace" self.args = self.args.strip() @@ -828,7 +834,7 @@ class CmdEmote(RPCommand): # replaces the main emote send_emote(self.caller, targets, emote, anonymous_add='first') -class CmdSay(RPCommand): # replaces standard say +class CmdSay(RPCommand): # replaces standard say """ speak as your character @@ -843,7 +849,6 @@ class CmdSay(RPCommand): # replaces standard say locks = "cmd:all()" def func(self): - "Run the say command" caller = self.caller @@ -860,7 +865,7 @@ class CmdSay(RPCommand): # replaces standard say send_emote(self.caller, targets, speech, anonymous_add=None) -class CmdSdesc(RPCommand): # set/look at own sdesc +class CmdSdesc(RPCommand): # set/look at own sdesc """ Assign yourself a short description (sdesc). @@ -884,13 +889,13 @@ class CmdSdesc(RPCommand): # set/look at own sdesc sdesc = _RE_CHAREND.sub("", self.args) try: sdesc = caller.sdesc.add(sdesc) - except SdescError, err: + except SdescError as err: caller.msg(err) return caller.msg("%s's sdesc was set to '%s'." % (caller.key, sdesc)) -class CmdPose(RPCommand): # set current pose and default pose +class CmdPose(RPCommand): # set current pose and default pose """ Set a static pose @@ -976,7 +981,7 @@ class CmdPose(RPCommand): # set current pose and default pose # set the pose. We do one-time ref->sdesc mapping here. parsed, mapping = parse_sdescs_and_recogs(caller, caller.location.contents, pose) mapping = dict((ref, obj.sdesc.get() if hasattr(obj, "sdesc") else obj.key) - for ref, obj in mapping.iteritems()) + for ref, obj in mapping.iteritems()) pose = parsed.format(**mapping) if len(target_name) + len(pose) > 60: @@ -988,7 +993,7 @@ class CmdPose(RPCommand): # set current pose and default pose caller.msg("Pose will read '%s %s'." % (target_name, pose)) -class CmdRecog(RPCommand): # assign personal alias to object in room +class CmdRecog(RPCommand): # assign personal alias to object in room """ Recognize another person in the same room. @@ -1034,11 +1039,11 @@ class CmdRecog(RPCommand): # assign personal alias to object in room if nmatches == 0: caller.msg(_EMOTE_NOMATCH_ERROR.format(ref=sdesc)) elif nmatches > 1: - reflist = ["%s%s%s (%s%s)" % (inum+1, _NUM_SEP, - _RE_PREFIX.sub("", sdesc), caller.recog.get(obj), - " (%s)" % caller.key if caller == obj else "") - for inum, obj in enumerate(matches)] - caller.msg(_EMOTE_MULTIMATCH_ERROR.format(ref=sdesc,reflist="\n ".join(reflist))) + reflist = ["%s%s%s (%s%s)" % (inum + 1, _NUM_SEP, + _RE_PREFIX.sub("", sdesc), caller.recog.get(obj), + " (%s)" % caller.key if caller == obj else "") + for inum, obj in enumerate(matches)] + caller.msg(_EMOTE_MULTIMATCH_ERROR.format(ref=sdesc, reflist="\n ".join(reflist))) else: obj = matches[0] if not obj.access(self.obj, "enable_recog", default=True): @@ -1053,7 +1058,7 @@ class CmdRecog(RPCommand): # assign personal alias to object in room sdesc = obj.sdesc.get() if hasattr(obj, "sdesc") else obj.key try: alias = caller.recog.add(obj, alias) - except RecogError, err: + except RecogError as err: caller.msg(err) return caller.msg("%s will now remember |w%s|n as |w%s|n." % (caller.key, sdesc, alias)) @@ -1095,7 +1100,7 @@ class CmdMask(RPCommand): caller.sdesc.add(sdesc) caller.msg("You wear a mask as '%s'." % sdesc) else: - #unmask + # unmask old_sdesc = caller.db.unmasked_sdesc if not old_sdesc: caller.msg("You are not wearing a mask.") @@ -1110,6 +1115,7 @@ class RPSystemCmdSet(CmdSet): """ Mix-in for adding rp-commands to default cmdset. """ + def at_cmdset_creation(self): self.add(CmdEmote()) self.add(CmdSay()) @@ -1231,7 +1237,7 @@ class ContribRPObject(DefaultObject): searchdata = self.nicks.nickreplace(searchdata, categories=("object", "account"), include_account=True) if(global_search or (is_string and searchdata.startswith("#") and - len(searchdata) > 1 and searchdata[1:].isdigit())): + len(searchdata) > 1 and searchdata[1:].isdigit())): # only allow exact matching if searching the entire database # or unique #dbrefs exact = True @@ -1258,16 +1264,17 @@ class ContribRPObject(DefaultObject): # the sdesc-related substitution is_builder = self.locks.check_lockstring(self, "perm(Builder)") use_dbref = is_builder if use_dbref is None else use_dbref - search_obj = lambda string: ObjectDB.objects.object_search(string, - attribute_name=attribute_name, - typeclass=typeclass, - candidates=candidates, - exact=exact, - use_dbref=use_dbref) + + def search_obj(string): return ObjectDB.objects.object_search(string, + attribute_name=attribute_name, + typeclass=typeclass, + candidates=candidates, + exact=exact, + use_dbref=use_dbref) if candidates: candidates = parse_sdescs_and_recogs(self, candidates, - _PREFIX + searchdata, search_mode=True) + _PREFIX + searchdata, search_mode=True) results = [] for candidate in candidates: # we search by candidate keys here; this allows full error @@ -1288,8 +1295,8 @@ class ContribRPObject(DefaultObject): if quiet: return results - return _AT_SEARCH_RESULT(results, self, query=searchdata, - nofound_string=nofound_string, multimatch_string=multimatch_string) + return _AT_SEARCH_RESULT(results, self, query=searchdata, + nofound_string=nofound_string, multimatch_string=multimatch_string) def get_display_name(self, looker, **kwargs): """ @@ -1336,7 +1343,7 @@ class ContribRPObject(DefaultObject): return "" # get and identify all objects visible = (con for con in self.contents if con != looker and - con.access(looker, "view")) + con.access(looker, "view")) exits, users, things = [], [], [] for con in visible: key = con.get_display_name(looker, pose=True) @@ -1488,4 +1495,4 @@ class ContribRPCharacter(DefaultCharacter, ContribRPObject): return "%s|w%s|n" % ("|W(%s)" % language if language else "", text) #from evennia.contrib import rplanguage - #return "|w%s|n" % rplanguage.obfuscate_language(text, level=1.0) + # return "|w%s|n" % rplanguage.obfuscate_language(text, level=1.0) diff --git a/evennia/contrib/simpledoor.py b/evennia/contrib/simpledoor.py index 630e1ba64..d43d211fa 100644 --- a/evennia/contrib/simpledoor.py +++ b/evennia/contrib/simpledoor.py @@ -40,6 +40,7 @@ class SimpleDoor(DefaultExit): sides using `exitname.setlock("traverse:false())` """ + def at_object_creation(self): """ Called the very first time the door is created. @@ -115,7 +116,7 @@ class CmdOpen(default_cmds.CmdOpen): self.caller.msg("Note: A door-type exit was created - ignored eventual custom return-exit type.") self.return_exit_already_created = True back_exit = self.create_exit(exit_name, destination, location, - exit_aliases=exit_aliases, typeclass=typeclass) + exit_aliases=exit_aliases, typeclass=typeclass) new_exit.db.return_exit = back_exit back_exit.db.return_exit = new_exit return new_exit @@ -159,10 +160,9 @@ class CmdOpenCloseDoor(default_cmds.MuxCommand): else: door.setlock("traverse:true()") self.caller.msg("You open %s." % door.key) - else: # close + else: # close if not door.locks.check(self.caller, "traverse"): self.caller.msg("%s is already closed." % door.key) else: door.setlock("traverse:false()") self.caller.msg("You close %s." % door.key) - diff --git a/evennia/contrib/slow_exit.py b/evennia/contrib/slow_exit.py index f8216c4d9..1994a87b6 100644 --- a/evennia/contrib/slow_exit.py +++ b/evennia/contrib/slow_exit.py @@ -42,10 +42,12 @@ MOVE_DELAY = {"stroll": 6, "run": 2, "sprint": 1} + class SlowExit(DefaultExit): """ This overloads the way moving happens. """ + def at_traverse(self, traversing_object, target_location): """ Implements the actual traversal, using utils.delay to delay the move_to. @@ -87,6 +89,7 @@ SPEED_DESCS = {"stroll": "strolling", "run": "running", "sprint": "sprinting"} + class CmdSetSpeed(Command): """ set your movement speed diff --git a/evennia/contrib/talking_npc.py b/evennia/contrib/talking_npc.py index 108537447..3d7ace2fc 100644 --- a/evennia/contrib/talking_npc.py +++ b/evennia/contrib/talking_npc.py @@ -48,6 +48,7 @@ def info1(caller): return text, options + def info2(caller): text = "'My name is not really important ... I'm just an NPC after all.'" @@ -67,7 +68,6 @@ def info3(caller): {"desc": "Wait, why don't you tell me your name first?", "goto": "info2"}) - return text, options @@ -82,6 +82,7 @@ def END(caller): # The talk command (sits on the NPC) # + class CmdTalk(default_cmds.MuxCommand): """ Talks to an npc @@ -112,6 +113,7 @@ class CmdTalk(default_cmds.MuxCommand): class TalkingCmdSet(CmdSet): "Stores the talk command." key = "talkingcmdset" + def at_cmdset_creation(self): "populates the cmdset" self.add(CmdTalk()) @@ -122,6 +124,7 @@ class TalkingNPC(DefaultObject): This implements a simple Object using the talk command and using the conversation defined above. """ + def at_object_creation(self): "This is called when object is first created." self.db.desc = "This is a talkative NPC." diff --git a/evennia/contrib/tests.py b/evennia/contrib/tests.py index c15a1f295..2342c4d17 100644 --- a/evennia/contrib/tests.py +++ b/evennia/contrib/tests.py @@ -64,6 +64,7 @@ class TestLanguage(EvenniaTest): # Testing of emoting / sdesc / recog system + from evennia import create_object from evennia.contrib import rpsystem @@ -176,6 +177,7 @@ from evennia.contrib import extended_room from evennia import gametime from evennia.objects.objects import DefaultRoom + class ForceUTCDatetime(datetime.datetime): """Force UTC datetime.""" @@ -185,6 +187,7 @@ class ForceUTCDatetime(datetime.datetime): """Force fromtimestamp to run with naive datetimes.""" return datetime.datetime.utcfromtimestamp(timestamp) + @patch('evennia.contrib.extended_room.datetime.datetime', ForceUTCDatetime) class TestExtendedRoom(CommandTest): room_typeclass = extended_room.ExtendedRoom @@ -208,7 +211,7 @@ class TestExtendedRoom(CommandTest): old_desc = DefaultRoom.return_appearance(self.room1, self.char1) # the new appearance should be the old one, but with the desc switched self.assertEqual(old_desc.replace(self.OLD_DESC, self.SPRING_DESC), - self.room1.return_appearance(self.char1)) + self.room1.return_appearance(self.char1)) self.assertEqual("spring", self.room1.ndb.last_season) self.assertEqual("evening", self.room1.ndb.last_timeslot) @@ -223,7 +226,7 @@ class TestExtendedRoom(CommandTest): def test_cmdextendeddesc(self): self.call(extended_room.CmdExtendedDesc(), "", "Details on Room", cmdstring="detail") self.call(extended_room.CmdExtendedDesc(), "thingie = newdetail with spaces", - "Set Detail thingie to 'newdetail with spaces'.", cmdstring="detail") + "Set Detail thingie to 'newdetail with spaces'.", cmdstring="detail") self.call(extended_room.CmdExtendedDesc(), "thingie", "Detail 'thingie' on Room:\n", cmdstring="detail") self.call(extended_room.CmdExtendedDesc(), "/del thingie", "Detail thingie deleted, if it existed.", cmdstring="detail") self.call(extended_room.CmdExtendedDesc(), "thingie", "Detail 'thingie' not found.", cmdstring="detail") @@ -237,6 +240,7 @@ class TestExtendedRoom(CommandTest): from evennia.contrib import barter + class TestBarter(CommandTest): def setUp(self): @@ -295,7 +299,7 @@ class TestBarter(CommandTest): self.call(barter.CmdTrade(), "Char decline : Nope!", "You say, \"Nope!\"", caller=self.char2) self.call(barter.CmdTrade(), "Char2 : Hey wanna trade?", "You say, \"Hey wanna trade?\"", caller=self.char1) self.call(barter.CmdTrade(), "Char accept : Sure!", "You say, \"Sure!\"", caller=self.char2) - self.call(barter.CmdOffer(), "TradeItem3", "Your trade action: You offer TradeItem3",caller=self.char2) + self.call(barter.CmdOffer(), "TradeItem3", "Your trade action: You offer TradeItem3", caller=self.char2) self.call(barter.CmdOffer(), "TradeItem1 : Here's my offer.", "You say, \"Here's my offer.\"\n [You offer TradeItem1]") self.call(barter.CmdAccept(), "", "Your trade action: You accept the offer. Char2 must now also accept") self.call(barter.CmdDecline(), "", "Your trade action: You change your mind, declining the current offer.") @@ -319,9 +323,11 @@ class TestBarter(CommandTest): # Test wilderness + from evennia.contrib import wilderness from evennia import DefaultCharacter + class TestWilderness(EvenniaTest): def setUp(self): @@ -372,8 +378,8 @@ class TestWilderness(EvenniaTest): # be visible / traversable exits = [i for i in self.char1.location.contents if i.destination and ( - i.access(self.char1, "view") or - i.access(self.char1, "traverse"))] + i.access(self.char1, "view") or + i.access(self.char1, "traverse"))] self.assertEquals(len(exits), 3) exitsok = ["north", "northeast", "east"] @@ -385,8 +391,8 @@ class TestWilderness(EvenniaTest): wilderness.enter_wilderness(self.char1, coordinates=(1, 1)) exits = [i for i in self.char1.location.contents if i.destination and ( - i.access(self.char1, "view") or - i.access(self.char1, "traverse"))] + i.access(self.char1, "view") or + i.access(self.char1, "traverse"))] self.assertEquals(len(exits), 8) exitsok = ["north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest"] @@ -438,9 +444,11 @@ class TestWilderness(EvenniaTest): new_loc = wilderness.get_new_coordinates(loc, direction) self.assertEquals(new_loc, correct_loc, direction) + # Testing chargen contrib from evennia.contrib import chargen + class TestChargen(CommandTest): def test_ooclook(self): @@ -451,13 +459,15 @@ class TestChargen(CommandTest): self.call(chargen.CmdOOCCharacterCreate(), "testchar", "The character testchar was successfully created!", caller=self.account) self.call(chargen.CmdOOCCharacterCreate(), "testchar", "Character testchar already exists.", caller=self.account) self.assertTrue(self.account.db._character_dbrefs) - self.call(chargen.CmdOOCLook(), "", "You, TestAccount, are an OOC ghost without form.",caller=self.account) + self.call(chargen.CmdOOCLook(), "", "You, TestAccount, are an OOC ghost without form.", caller=self.account) self.call(chargen.CmdOOCLook(), "testchar", "testchar(", caller=self.account) + # Testing clothing contrib from evennia.contrib import clothing from evennia.objects.objects import DefaultRoom + class TestClothingCmd(CommandTest): def test_clothingcommands(self): @@ -501,6 +511,7 @@ class TestClothingCmd(CommandTest): # Test inventory command. self.call(clothing.CmdInventory(), "", "You are not carrying or wearing anything.", caller=wearer) + class TestClothingFunc(EvenniaTest): def test_clothingfunctions(self): @@ -537,42 +548,50 @@ class TestClothingFunc(EvenniaTest): test_pants.wear(wearer, True) self.assertEqual(clothing.get_worn_clothes(wearer), [test_hat, test_pants]) - self.assertEqual(clothing.clothing_type_count(clothes_list), {'hat':1, 'top':1, 'bottom':1}) + self.assertEqual(clothing.clothing_type_count(clothes_list), {'hat': 1, 'top': 1, 'bottom': 1}) self.assertEqual(clothing.single_type_count(clothes_list, 'hat'), 1) - # Testing custom_gametime from evennia.contrib import custom_gametime + def _testcallback(): pass + class TestCustomGameTime(EvenniaTest): def setUp(self): super(TestCustomGameTime, self).setUp() - gametime.gametime = Mock(return_value=2975000898.46) # does not seem to work + gametime.gametime = Mock(return_value=2975000898.46) # does not seem to work + def tearDown(self): if hasattr(self, "timescript"): self.timescript.stop() + def test_time_to_tuple(self): - self.assertEqual(custom_gametime.time_to_tuple(10000, 34,2,4,6,1), (294, 2, 0, 0, 0, 0)) - self.assertEqual(custom_gametime.time_to_tuple(10000, 3,3,4), (3333, 0, 0, 1)) - self.assertEqual(custom_gametime.time_to_tuple(100000, 239,24,3), (418, 4, 0, 2)) + self.assertEqual(custom_gametime.time_to_tuple(10000, 34, 2, 4, 6, 1), (294, 2, 0, 0, 0, 0)) + self.assertEqual(custom_gametime.time_to_tuple(10000, 3, 3, 4), (3333, 0, 0, 1)) + self.assertEqual(custom_gametime.time_to_tuple(100000, 239, 24, 3), (418, 4, 0, 2)) + def test_gametime_to_realtime(self): self.assertEqual(custom_gametime.gametime_to_realtime(days=2, mins=4), 86520.0) - self.assertEqual(custom_gametime.gametime_to_realtime(format=True, days=2), (0,0,0,1,0,0,0)) + self.assertEqual(custom_gametime.gametime_to_realtime(format=True, days=2), (0, 0, 0, 1, 0, 0, 0)) + def test_realtime_to_gametime(self): self.assertEqual(custom_gametime.realtime_to_gametime(days=2, mins=34), 349680.0) self.assertEqual(custom_gametime.realtime_to_gametime(days=2, mins=34, format=True), (0, 0, 0, 4, 1, 8, 0)) self.assertEqual(custom_gametime.realtime_to_gametime(format=True, days=2, mins=4), (0, 0, 0, 4, 0, 8, 0)) + def test_custom_gametime(self): self.assertEqual(custom_gametime.custom_gametime(), (102, 5, 2, 6, 21, 8, 18)) self.assertEqual(custom_gametime.custom_gametime(absolute=True), (102, 5, 2, 6, 21, 8, 18)) + def test_real_seconds_until(self): self.assertEqual(custom_gametime.real_seconds_until(year=2300, month=11, day=6), 31911667199.77) + def test_schedule(self): self.timescript = custom_gametime.schedule(_testcallback, repeat=True, min=5, sec=0) self.assertEqual(self.timescript.interval, 1700.7699999809265) @@ -585,9 +604,10 @@ class TestDice(CommandTest): def test_roll_dice(self, mocked_randint): # we must import dice here for the mocked randint to apply correctly. from evennia.contrib import dice - self.assertEqual(dice.roll_dice(6, 6, modifier=('+', 4)), mocked_randint()*6 + 4) + self.assertEqual(dice.roll_dice(6, 6, modifier=('+', 4)), mocked_randint() * 6 + 4) self.assertEqual(dice.roll_dice(6, 6, conditional=('<', 35)), True) self.assertEqual(dice.roll_dice(6, 6, conditional=('>', 33)), False) + def test_cmddice(self, mocked_randint): from evennia.contrib import dice self.call(dice.CmdDice(), "3d6 + 4", "You roll 3d6 + 4.| Roll(s): 5, 5 and 5. Total result is 19.") @@ -596,29 +616,37 @@ class TestDice(CommandTest): # Test email-login + from evennia.contrib import email_login + class TestEmailLogin(CommandTest): def test_connect(self): self.call(email_login.CmdUnconnectedConnect(), "mytest@test.com test", "The email 'mytest@test.com' does not match any accounts.") self.call(email_login.CmdUnconnectedCreate(), '"mytest" mytest@test.com test11111', "A new account 'mytest' was created. Welcome!") self.call(email_login.CmdUnconnectedConnect(), "mytest@test.com test11111", "", caller=self.account.sessions.get()[0]) + def test_quit(self): self.call(email_login.CmdUnconnectedQuit(), "", "", caller=self.account.sessions.get()[0]) + def test_unconnectedlook(self): self.call(email_login.CmdUnconnectedLook(), "", "==========") + def test_unconnectedhelp(self): self.call(email_login.CmdUnconnectedHelp(), "", "You are not yet logged into the game.") # test gendersub contrib + from evennia.contrib import gendersub + class TestGenderSub(CommandTest): def test_setgender(self): self.call(gendersub.SetGender(), "male", "Your gender was set to male.") self.call(gendersub.SetGender(), "ambiguous", "Your gender was set to ambiguous.") self.call(gendersub.SetGender(), "Foo", "Usage: @gender") + def test_gendercharacter(self): char = create_object(gendersub.GenderCharacter, key="Gendered", location=self.room1) txt = "Test |p gender" @@ -626,8 +654,10 @@ class TestGenderSub(CommandTest): # test mail contrib + from evennia.contrib import mail + class TestMail(CommandTest): def test_mail(self): self.call(mail.CmdMail(), "2", "'2' is not a valid mail id.", caller=self.account) @@ -635,7 +665,7 @@ class TestMail(CommandTest): self.call(mail.CmdMail(), "Char=Message 1", "You have received a new @mail from Char|You sent your message.", caller=self.char1) self.call(mail.CmdMail(), "Char=Message 2", "You sent your message.", caller=self.char2) self.call(mail.CmdMail(), "TestAccount2=Message 2", - "You have received a new @mail from TestAccount2(account 2)|You sent your message.", caller=self.account2) + "You have received a new @mail from TestAccount2(account 2)|You sent your message.", caller=self.account2) self.call(mail.CmdMail(), "TestAccount=Message 1", "You sent your message.", caller=self.account2) self.call(mail.CmdMail(), "TestAccount=Message 2", "You sent your message.", caller=self.account2) self.call(mail.CmdMail(), "", "| ID: From: Subject:", caller=self.account) @@ -646,21 +676,23 @@ class TestMail(CommandTest): # test map builder contrib + from evennia.contrib import mapbuilder + class TestMapBuilder(CommandTest): def test_cmdmapbuilder(self): self.call(mapbuilder.CmdMapBuilder(), - "evennia.contrib.mapbuilder.EXAMPLE1_MAP evennia.contrib.mapbuilder.EXAMPLE1_LEGEND", -"""Creating Map...|≈≈≈≈≈ + "evennia.contrib.mapbuilder.EXAMPLE1_MAP evennia.contrib.mapbuilder.EXAMPLE1_LEGEND", + """Creating Map...|≈≈≈≈≈ ≈♣n♣≈ ≈∩▲∩≈ ≈♠n♠≈ ≈≈≈≈≈ |Creating Landmass...|""") self.call(mapbuilder.CmdMapBuilder(), - "evennia.contrib.mapbuilder.EXAMPLE2_MAP evennia.contrib.mapbuilder.EXAMPLE2_LEGEND", -"""Creating Map...|≈ ≈ ≈ ≈ ≈ + "evennia.contrib.mapbuilder.EXAMPLE2_MAP evennia.contrib.mapbuilder.EXAMPLE2_LEGEND", + """Creating Map...|≈ ≈ ≈ ≈ ≈ ≈ ♣♣♣ ≈ ≈ ♣ ♣ ♣ ≈ @@ -674,6 +706,7 @@ class TestMapBuilder(CommandTest): from evennia.contrib import menu_login + class TestMenuLogin(CommandTest): def test_cmdunloggedlook(self): self.call(menu_login.CmdUnloggedinLook(), "", "======") @@ -683,28 +716,31 @@ class TestMenuLogin(CommandTest): from evennia.contrib import multidescer + class TestMultidescer(CommandTest): def test_cmdmultidesc(self): - self.call(multidescer.CmdMultiDesc(),"/list", "Stored descs:\ncaller:") - self.call(multidescer.CmdMultiDesc(),"test = Desc 1", "Stored description 'test': \"Desc 1\"") - self.call(multidescer.CmdMultiDesc(),"test2 = Desc 2", "Stored description 'test2': \"Desc 2\"") - self.call(multidescer.CmdMultiDesc(),"/swap test-test2", "Swapped descs 'test' and 'test2'.") - self.call(multidescer.CmdMultiDesc(),"test3 = Desc 3init", "Stored description 'test3': \"Desc 3init\"") - self.call(multidescer.CmdMultiDesc(),"/list", "Stored descs:\ntest3: Desc 3init\ntest: Desc 1\ntest2: Desc 2\ncaller:") - self.call(multidescer.CmdMultiDesc(),"test3 = Desc 3", "Stored description 'test3': \"Desc 3\"") - self.call(multidescer.CmdMultiDesc(),"/set test1 + test2 + + test3", "test1 Desc 2 Desc 3\n\n" - "The above was set as the current description.") + self.call(multidescer.CmdMultiDesc(), "/list", "Stored descs:\ncaller:") + self.call(multidescer.CmdMultiDesc(), "test = Desc 1", "Stored description 'test': \"Desc 1\"") + self.call(multidescer.CmdMultiDesc(), "test2 = Desc 2", "Stored description 'test2': \"Desc 2\"") + self.call(multidescer.CmdMultiDesc(), "/swap test-test2", "Swapped descs 'test' and 'test2'.") + self.call(multidescer.CmdMultiDesc(), "test3 = Desc 3init", "Stored description 'test3': \"Desc 3init\"") + self.call(multidescer.CmdMultiDesc(), "/list", "Stored descs:\ntest3: Desc 3init\ntest: Desc 1\ntest2: Desc 2\ncaller:") + self.call(multidescer.CmdMultiDesc(), "test3 = Desc 3", "Stored description 'test3': \"Desc 3\"") + self.call(multidescer.CmdMultiDesc(), "/set test1 + test2 + + test3", "test1 Desc 2 Desc 3\n\n" + "The above was set as the current description.") self.assertEqual(self.char1.db.desc, "test1 Desc 2 Desc 3") # test simpledoor contrib + from evennia.contrib import simpledoor + class TestSimpleDoor(CommandTest): def test_cmdopen(self): self.call(simpledoor.CmdOpen(), "newdoor;door:contrib.simpledoor.SimpleDoor,backdoor;door = Room2", - "Created new Exit 'newdoor' from Room to Room2 (aliases: door).|Note: A doortype exit was " - "created ignored eventual custom returnexit type.|Created new Exit 'newdoor' from Room2 to Room (aliases: door).") + "Created new Exit 'newdoor' from Room to Room2 (aliases: door).|Note: A doortype exit was " + "created ignored eventual custom returnexit type.|Created new Exit 'newdoor' from Room2 to Room (aliases: door).") self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "You close newdoor.", cmdstring="close") self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "newdoor is already closed.", cmdstring="close") self.call(simpledoor.CmdOpenCloseDoor(), "newdoor", "You open newdoor.", cmdstring="open") @@ -712,8 +748,10 @@ class TestSimpleDoor(CommandTest): # test slow_exit contrib + from evennia.contrib import slow_exit -slow_exit.MOVE_DELAY = {"stroll":0, "walk": 0, "run": 0, "sprint": 0} +slow_exit.MOVE_DELAY = {"stroll": 0, "walk": 0, "run": 0, "sprint": 0} + class TestSlowExit(CommandTest): def test_exit(self): @@ -724,12 +762,14 @@ class TestSlowExit(CommandTest): # test talking npc contrib + from evennia.contrib import talking_npc + class TestTalkingNPC(CommandTest): def test_talkingnpc(self): npc = create_object(talking_npc.TalkingNPC, key="npctalker", location=self.room1) - self.call(talking_npc.CmdTalk(), "","(You walk up and talk to Char.)|") + self.call(talking_npc.CmdTalk(), "", "(You walk up and talk to Char.)|") npc.delete() @@ -739,6 +779,7 @@ class TestTalkingNPC(CommandTest): from evennia.contrib.tutorial_world import mob + class TestTutorialWorldMob(EvenniaTest): def test_mob(self): mobobj = create_object(mob.Mob, key="mob") @@ -748,28 +789,34 @@ class TestTutorialWorldMob(EvenniaTest): mobobj.set_dead() self.assertEqual(mobobj.db.is_dead, True) mobobj._set_ticker(0, "foo", stop=True) - #TODO should be expanded with further tests of the modes and damage etc. + # TODO should be expanded with further tests of the modes and damage etc. # test tutorial_world/objects + from evennia.contrib.tutorial_world import objects as tutobjects + class TestTutorialWorldObjects(CommandTest): def test_tutorialobj(self): obj1 = create_object(tutobjects.TutorialObject, key="tutobj") obj1.reset() self.assertEqual(obj1.location, obj1.home) + def test_readable(self): readable = create_object(tutobjects.Readable, key="book", location=self.room1) readable.db.readable_text = "Text to read" - self.call(tutobjects.CmdRead(), "book","You read book:\n Text to read", obj=readable) + self.call(tutobjects.CmdRead(), "book", "You read book:\n Text to read", obj=readable) + def test_climbable(self): climbable = create_object(tutobjects.Climbable, key="tree", location=self.room1) self.call(tutobjects.CmdClimb(), "tree", "You climb tree. Having looked around, you climb down again.", obj=climbable) self.assertEqual(self.char1.tags.get("tutorial_climbed_tree", category="tutorial_world"), "tutorial_climbed_tree") + def test_obelisk(self): obelisk = create_object(tutobjects.Obelisk, key="obelisk", location=self.room1) self.assertEqual(obelisk.return_appearance(self.char1).startswith("|cobelisk("), True) + def test_lightsource(self): light = create_object(tutobjects.LightSource, key="torch", location=self.room1) self.call(tutobjects.CmdLight(), "", "You light torch.", obj=light) @@ -777,39 +824,44 @@ class TestTutorialWorldObjects(CommandTest): if hasattr(light, "deferred"): light.deferred.cancel() self.assertFalse(light.pk) + def test_crumblingwall(self): wall = create_object(tutobjects.CrumblingWall, key="wall", location=self.room1) self.assertFalse(wall.db.button_exposed) self.assertFalse(wall.db.exit_open) - wall.db.root_pos = {"yellow":0, "green":0,"red":0,"blue":0} + wall.db.root_pos = {"yellow": 0, "green": 0, "red": 0, "blue": 0} self.call(tutobjects.CmdShiftRoot(), "blue root right", - "You shove the root adorned with small blue flowers to the right.", obj=wall) + "You shove the root adorned with small blue flowers to the right.", obj=wall) self.call(tutobjects.CmdShiftRoot(), "red root left", - "You shift the reddish root to the left.", obj=wall) + "You shift the reddish root to the left.", obj=wall) self.call(tutobjects.CmdShiftRoot(), "yellow root down", - "You shove the root adorned with small yellow flowers downwards.", obj=wall) + "You shove the root adorned with small yellow flowers downwards.", obj=wall) self.call(tutobjects.CmdShiftRoot(), "green root up", - "You shift the weedy green root upwards.|Holding aside the root you think you notice something behind it ...", obj=wall) + "You shift the weedy green root upwards.|Holding aside the root you think you notice something behind it ...", obj=wall) self.call(tutobjects.CmdPressButton(), "", - "You move your fingers over the suspicious depression, then gives it a decisive push. First", obj=wall) + "You move your fingers over the suspicious depression, then gives it a decisive push. First", obj=wall) self.assertTrue(wall.db.button_exposed) self.assertTrue(wall.db.exit_open) wall.reset() if hasattr(wall, "deferred"): wall.deferred.cancel() wall.delete() + def test_weapon(self): weapon = create_object(tutobjects.Weapon, key="sword", location=self.char1) self.call(tutobjects.CmdAttack(), "Char", "You stab with sword.", obj=weapon, cmdstring="stab") self.call(tutobjects.CmdAttack(), "Char", "You slash with sword.", obj=weapon, cmdstring="slash") + def test_weaponrack(self): rack = create_object(tutobjects.WeaponRack, key="rack", location=self.room1) rack.db.available_weapons = ["sword"] self.call(tutobjects.CmdGetWeapon(), "", "You find Rusty sword.", obj=rack) + # test tutorial_world/ from evennia.contrib.tutorial_world import rooms as tutrooms + class TestTutorialWorldRooms(CommandTest): def test_cmdtutorial(self): room = create_object(tutrooms.TutorialRoom, key="tutroom") @@ -820,14 +872,17 @@ class TestTutorialWorldRooms(CommandTest): self.call(tutrooms.CmdTutorialLook(), "detail", "A detail", obj=room) self.call(tutrooms.CmdTutorialLook(), "foo", "A detail", obj=room) room.delete() + def test_weatherroom(self): room = create_object(tutrooms.WeatherRoom, key="weatherroom") room.update_weather() tutrooms.TICKER_HANDLER.remove(interval=room.db.interval, callback=room.update_weather, idstring="tutorial") room.delete() + def test_introroom(self): room = create_object(tutrooms.IntroRoom, key="introroom") room.at_object_receive(self.char1, self.room1) + def test_bridgeroom(self): room = create_object(tutrooms.BridgeRoom, key="bridgeroom") room.update_weather() @@ -837,19 +892,24 @@ class TestTutorialWorldRooms(CommandTest): room.at_object_leave(self.char1, self.room1) tutrooms.TICKER_HANDLER.remove(interval=room.db.interval, callback=room.update_weather, idstring="tutorial") room.delete() + def test_darkroom(self): room = create_object(tutrooms.DarkRoom, key="darkroom") self.char1.move_to(room) self.call(tutrooms.CmdDarkHelp(), "", "Can't help you until") + def test_teleportroom(self): create_object(tutrooms.TeleportRoom, key="teleportroom") + def test_outroroom(self): create_object(tutrooms.OutroRoom, key="outroroom") + # test turnbattle from evennia.contrib import turnbattle from evennia.objects.objects import DefaultRoom + class TestTurnBattleCmd(CommandTest): # Test combat commands @@ -860,6 +920,7 @@ class TestTurnBattleCmd(CommandTest): self.call(turnbattle.CmdDisengage(), "", "You can only do that in combat. (see: help fight)") self.call(turnbattle.CmdRest(), "", "Char rests to recover HP.") + class TestTurnBattleFunc(EvenniaTest): # Test combat functions @@ -944,6 +1005,7 @@ class TestTurnBattleFunc(EvenniaTest): from evennia.contrib.unixcommand import UnixCommand + class CmdDummy(UnixCommand): """A dummy UnixCommand.""" @@ -991,6 +1053,7 @@ class TestUnixCommand(CommandTest): import re from evennia.contrib import color_markups + class TestColorMarkup(EvenniaTest): """ Note: Normally this would be tested by importing the ansi parser and run @@ -999,6 +1062,7 @@ class TestColorMarkup(EvenniaTest): many other modules it appears that trying to overload settings to test it causes issues with unrelated tests. """ + def test_curly_markup(self): ansi_map = color_markups.CURLY_COLOR_ANSI_EXTRA_MAP self.assertIsNotNone(re.match(re.escape(ansi_map[7][0]), '{r')) @@ -1047,10 +1111,12 @@ class TestColorMarkup(EvenniaTest): self.assertEqual(bright_map[0][1], '%c[500') self.assertEqual(bright_map[-1][1], '%c[222') + from evennia.contrib import random_string_generator SIMPLE_GENERATOR = random_string_generator.RandomStringGenerator("simple", "[01]{2}") + class TestRandomStringGenerator(EvenniaTest): def test_generate(self): diff --git a/evennia/contrib/tutorial_examples/bodyfunctions.py b/evennia/contrib/tutorial_examples/bodyfunctions.py index ea478cbb4..406e8da64 100644 --- a/evennia/contrib/tutorial_examples/bodyfunctions.py +++ b/evennia/contrib/tutorial_examples/bodyfunctions.py @@ -12,6 +12,7 @@ make sure to put it on yourself or you won't see any messages! import random from evennia import DefaultScript + class BodyFunctions(DefaultScript): """ This class defines the script itself @@ -21,7 +22,7 @@ class BodyFunctions(DefaultScript): self.key = "bodyfunction" self.desc = "Adds various timed events to a character." self.interval = 20 # seconds - #self.repeats = 5 # repeat only a certain number of times + # self.repeats = 5 # repeat only a certain number of times self.start_delay = True # wait self.interval until first call #self.persistent = True diff --git a/evennia/contrib/tutorial_examples/cmdset_red_button.py b/evennia/contrib/tutorial_examples/cmdset_red_button.py index fce903195..47c0df3ee 100644 --- a/evennia/contrib/tutorial_examples/cmdset_red_button.py +++ b/evennia/contrib/tutorial_examples/cmdset_red_button.py @@ -119,7 +119,7 @@ class CmdSmashGlass(Command): string += " you should just try to open the lid instead?" self.caller.msg(string) self.caller.location.msg_contents("%s tries to smash the glass of the button." % - (self.caller.name), exclude=self.caller) + (self.caller.name), exclude=self.caller) class CmdOpenLid(Command): @@ -146,7 +146,7 @@ class CmdOpenLid(Command): string += "the lid will soon close again." self.caller.msg(string) self.caller.location.msg_contents("%s opens the lid of the button." % - (self.caller.name), exclude=self.caller) + (self.caller.name), exclude=self.caller) # add the relevant cmdsets to button self.obj.cmdset.add(LidClosedCmdSet) # call object method @@ -213,7 +213,7 @@ class CmdBlindLook(Command): string += "Until it wears off, all you can do is feel around blindly." self.caller.msg(string) self.caller.location.msg_contents("%s stumbles around, blinded." % - (self.caller.name), exclude=self.caller) + (self.caller.name), exclude=self.caller) class CmdBlindHelp(Command): diff --git a/evennia/contrib/tutorial_examples/example_batch_code.py b/evennia/contrib/tutorial_examples/example_batch_code.py index a38be7b12..bd7bab46b 100644 --- a/evennia/contrib/tutorial_examples/example_batch_code.py +++ b/evennia/contrib/tutorial_examples/example_batch_code.py @@ -39,7 +39,7 @@ # -#HEADER +# HEADER # everything in this block will be appended to the beginning of # all other #CODE blocks when they are executed. @@ -51,7 +51,7 @@ from evennia import DefaultObject limbo = search_object('Limbo')[0] -#CODE +# CODE # This is the first code block. Within each block, Python # code works as normal. Note how we make use if imports and @@ -61,12 +61,12 @@ limbo = search_object('Limbo')[0] # create a red button in limbo red_button = create_object(red_button.RedButton, key="Red button", - location=limbo, aliases=["button"]) + location=limbo, aliases=["button"]) # we take a look at what we created caller.msg("A %s was created." % red_button.key) -#CODE +# CODE # this code block has 'table' and 'chair' set as deletable # objects. This means that when the batchcode processor runs in diff --git a/evennia/contrib/tutorial_examples/red_button.py b/evennia/contrib/tutorial_examples/red_button.py index 3b2402c9a..00f7b58f5 100644 --- a/evennia/contrib/tutorial_examples/red_button.py +++ b/evennia/contrib/tutorial_examples/red_button.py @@ -33,6 +33,7 @@ class RedButton(DefaultObject): desc_lamp_broken - description when lamp is broken """ + def at_object_creation(self): """ This function is called when object is created. Use this diff --git a/evennia/contrib/tutorial_examples/red_button_scripts.py b/evennia/contrib/tutorial_examples/red_button_scripts.py index 43be358b2..d3c2001cf 100644 --- a/evennia/contrib/tutorial_examples/red_button_scripts.py +++ b/evennia/contrib/tutorial_examples/red_button_scripts.py @@ -26,12 +26,14 @@ from evennia.contrib.tutorial_examples import cmdset_red_button as cmdsetexample # a bright light. The last one also has a timer component that allows it # to remove itself after a while (and the player recovers their eyesight). + class ClosedLidState(DefaultScript): """ This manages the cmdset for the "closed" button state. What this means is that while this script is valid, we add the RedButtonClosed cmdset to it (with commands like open, nudge lid etc) """ + def at_script_creation(self): "Called when script first created." self.desc = "Script that manages the closed-state cmdsets for red button." @@ -44,7 +46,7 @@ class ClosedLidState(DefaultScript): checked so we don't need to worry about adding the script to an open lid. """ - #All we do is add the cmdset for the closed state. + # All we do is add the cmdset for the closed state. self.obj.cmdset.add(cmdsetexamples.LidClosedCmdSet) def is_valid(self): @@ -67,6 +69,7 @@ class OpenLidState(DefaultScript): This manages the cmdset for the "open" button state. This will add the RedButtonOpen """ + def at_script_creation(self): "Called when script first created." self.desc = "Script that manages the opened-state cmdsets for red button." @@ -105,6 +108,7 @@ class BlindedState(DefaultScript): restored. It's up to the function starting the script to actually set it on the right account object. """ + def at_script_creation(self): """ We set up the script here. @@ -139,7 +143,7 @@ class BlindedState(DefaultScript): % self.obj.name, exclude=self.obj) self.obj.cmdset.delete() # this will clear the latest added cmdset, - # (which is the blinded one). + # (which is the blinded one). # @@ -158,6 +162,7 @@ class CloseLidEvent(DefaultScript): script that should be started/created when the lid is opened. """ + def at_script_creation(self): """ Called when script object is first created. Sets things up. @@ -172,7 +177,7 @@ class CloseLidEvent(DefaultScript): self.start_delay = True # we want to pospone the launch. self.repeats = 1 # we only close the lid once self.persistent = True # even if the server crashes in those 20 seconds, - # the lid will still close once the game restarts. + # the lid will still close once the game restarts. def is_valid(self): """ @@ -194,10 +199,12 @@ class CloseLidEvent(DefaultScript): """ self.obj.close_lid() + class BlinkButtonEvent(DefaultScript): """ This timed script lets the button flash at regular intervals. """ + def at_script_creation(self): """ Sets things up. We want the button's lamp to blink at @@ -206,9 +213,9 @@ class BlinkButtonEvent(DefaultScript): """ self.key = "blink_button" self.desc = "Blinks red buttons" - self.interval = 35 #seconds - self.start_delay = False #blink right away - self.persistent = True #keep blinking also after server reboot + self.interval = 35 # seconds + self.start_delay = False # blink right away + self.persistent = True # keep blinking also after server reboot def is_valid(self): """ @@ -223,6 +230,7 @@ class BlinkButtonEvent(DefaultScript): """ self.obj.blink() + class DeactivateButtonEvent(DefaultScript): """ This deactivates the button for a short while (it won't blink, won't @@ -231,13 +239,14 @@ class DeactivateButtonEvent(DefaultScript): in the AddBlindedCmdSet script since that script is defined on the *account* whereas this one must be defined on the *button*. """ + def at_script_creation(self): """ Sets things up. """ self.key = "deactivate_button" self.desc = "Deactivate red button temporarily" - self.interval = 21 #seconds + self.interval = 21 # seconds self.start_delay = True # wait with the first repeat for self.interval seconds. self.persistent = True self.repeats = 1 # only do this once diff --git a/evennia/contrib/tutorial_world/__init__.py b/evennia/contrib/tutorial_world/__init__.py index 9b6839c5d..87f7c7f4c 100644 --- a/evennia/contrib/tutorial_world/__init__.py +++ b/evennia/contrib/tutorial_world/__init__.py @@ -5,4 +5,3 @@ This package holds the demo game of Evennia. from __future__ import absolute_import from . import mob, objects, rooms - diff --git a/evennia/contrib/tutorial_world/mob.py b/evennia/contrib/tutorial_world/mob.py index 22bcbddb3..8a27c7cf5 100644 --- a/evennia/contrib/tutorial_world/mob.py +++ b/evennia/contrib/tutorial_world/mob.py @@ -13,6 +13,7 @@ from evennia import Command, CmdSet from evennia import logger from evennia.contrib.tutorial_world import objects as tut_objects + class CmdMobOnOff(Command): """ Activates/deactivates Mob @@ -51,9 +52,11 @@ class MobCmdSet(CmdSet): """ Holds the admin command controlling the mob """ + def at_cmdset_creation(self): self.add(CmdMobOnOff()) + class Mob(tut_objects.TutorialObject): """ This is a state-machine AI mobile. It has several states which are @@ -91,6 +94,7 @@ class Mob(tut_objects.TutorialObject): happen to roam into a room with no exits. """ + def at_init(self): """ When initialized from cache (after a server reboot), set up @@ -122,7 +126,7 @@ class Mob(tut_objects.TutorialObject): self.db.patrolling_pace = 6 self.db.aggressive_pace = 2 self.db.hunting_pace = 1 - self.db.death_pace = 100 # stay dead for 100 seconds + self.db.death_pace = 100 # stay dead for 100 seconds # we store the call to the tickerhandler # so we can easily deactivate the last @@ -179,19 +183,19 @@ class Mob(tut_objects.TutorialObject): we need to remember this across reloads. """ - idstring = "tutorial_mob" # this doesn't change + idstring = "tutorial_mob" # this doesn't change last_interval = self.db.last_ticker_interval last_hook_key = self.db.last_hook_key if last_interval and last_hook_key: # we have a previous subscription, kill this first. TICKER_HANDLER.remove(interval=last_interval, - callback=getattr(self, last_hook_key), idstring=idstring) + callback=getattr(self, last_hook_key), idstring=idstring) self.db.last_ticker_interval = interval self.db.last_hook_key = hook_key if not stop: # set the new ticker TICKER_HANDLER.add(interval=interval, - callback=getattr(self, hook_key), idstring=idstring) + callback=getattr(self, hook_key), idstring=idstring) def _find_target(self, location): """ @@ -206,7 +210,7 @@ class Mob(tut_objects.TutorialObject): """ targets = [obj for obj in location.contents_get(exclude=self) - if obj.has_account and not obj.is_superuser] + if obj.has_account and not obj.is_superuser] return targets[0] if targets else None def set_alive(self, *args, **kwargs): @@ -379,7 +383,6 @@ class Mob(tut_objects.TutorialObject): else: logger.log_err("Mob: mob.db.send_defeated_to not found: %s" % self.db.send_defeated_to) - # response methods - called by other objects def at_hit(self, weapon, attacker, damage): diff --git a/evennia/contrib/tutorial_world/objects.py b/evennia/contrib/tutorial_world/objects.py index 943bf3a91..3859de7d2 100644 --- a/evennia/contrib/tutorial_world/objects.py +++ b/evennia/contrib/tutorial_world/objects.py @@ -106,6 +106,7 @@ class CmdSetReadable(CmdSet): """ A CmdSet for readables. """ + def at_cmdset_creation(self): """ Called when the cmdset is created. @@ -117,6 +118,7 @@ class Readable(TutorialObject): """ This simple object defines some attributes and """ + def at_object_creation(self): """ Called when object is created. We make sure to set the needed @@ -176,6 +178,7 @@ class CmdClimb(Command): class CmdSetClimbable(CmdSet): """Climbing cmdset""" + def at_cmdset_creation(self): """populate set""" self.add(CmdClimb()) @@ -303,6 +306,7 @@ class LightSource(TutorialObject): When burned out, the object will be deleted. """ + def at_init(self): """ If this is called with the Attribute is_giving_light already @@ -589,6 +593,7 @@ class CrumblingWall(TutorialObject, DefaultExit): whenever the button is pushed (this hides it as an exit until it actually is) """ + def at_init(self): """ Called when object is recalled from cache. @@ -838,6 +843,7 @@ class CmdAttack(Command): class CmdSetWeapon(CmdSet): """Holds the attack command.""" + def at_cmdset_creation(self): """called at first object creation.""" self.add(CmdAttack()) @@ -854,6 +860,7 @@ class Weapon(TutorialObject): type of attack) (0-10) """ + def at_object_creation(self): """Called at first creation of the object""" super(Weapon, self).at_object_creation() @@ -987,7 +994,7 @@ WEAPON_PROTOTYPES = { "hit": 0.85, "parry": 0.7, "damage": 11} - } +} class CmdGetWeapon(Command): @@ -1037,6 +1044,7 @@ class WeaponRack(TutorialObject): grab another one. """ + def at_object_creation(self): """ called at creation diff --git a/evennia/contrib/tutorial_world/rooms.py b/evennia/contrib/tutorial_world/rooms.py index 61f41c163..c16baccff 100644 --- a/evennia/contrib/tutorial_world/rooms.py +++ b/evennia/contrib/tutorial_world/rooms.py @@ -215,6 +215,7 @@ class TutorialRoom(DefaultRoom): This is the base room type for all rooms in the tutorial world. It defines a cmdset on itself for reading tutorial info about the location. """ + def at_object_creation(self): """Called when room is first created""" self.db.tutorial_info = "This is a tutorial room. It allows you to use the 'tutorial' command." @@ -278,17 +279,17 @@ class TutorialRoom(DefaultRoom): # These are rainy weather strings WEATHER_STRINGS = ( - "The rain coming down from the iron-grey sky intensifies.", - "A gust of wind throws the rain right in your face. Despite your cloak you shiver.", - "The rainfall eases a bit and the sky momentarily brightens.", - "For a moment it looks like the rain is slowing, then it begins anew with renewed force.", - "The rain pummels you with large, heavy drops. You hear the rumble of thunder in the distance.", - "The wind is picking up, howling around you, throwing water droplets in your face. It's cold.", - "Bright fingers of lightning flash over the sky, moments later followed by a deafening rumble.", - "It rains so hard you can hardly see your hand in front of you. You'll soon be drenched to the bone.", - "Lightning strikes in several thundering bolts, striking the trees in the forest to your west.", - "You hear the distant howl of what sounds like some sort of dog or wolf.", - "Large clouds rush across the sky, throwing their load of rain over the world.") + "The rain coming down from the iron-grey sky intensifies.", + "A gust of wind throws the rain right in your face. Despite your cloak you shiver.", + "The rainfall eases a bit and the sky momentarily brightens.", + "For a moment it looks like the rain is slowing, then it begins anew with renewed force.", + "The rain pummels you with large, heavy drops. You hear the rumble of thunder in the distance.", + "The wind is picking up, howling around you, throwing water droplets in your face. It's cold.", + "Bright fingers of lightning flash over the sky, moments later followed by a deafening rumble.", + "It rains so hard you can hardly see your hand in front of you. You'll soon be drenched to the bone.", + "Lightning strikes in several thundering bolts, striking the trees in the forest to your west.", + "You hear the distant howl of what sounds like some sort of dog or wolf.", + "Large clouds rush across the sky, throwing their load of rain over the world.") class WeatherRoom(TutorialRoom): @@ -300,6 +301,7 @@ class WeatherRoom(TutorialRoom): inherit from this. """ + def at_object_creation(self): """ Called when object is first created. @@ -355,6 +357,7 @@ class IntroRoom(TutorialRoom): properties to customize: char_health - integer > 0 (default 20) """ + def at_object_creation(self): """ Called when the room is first created. @@ -377,7 +380,7 @@ class IntroRoom(TutorialRoom): character.db.health_max = health if character.is_superuser: - string = "-"*78 + SUPERUSER_WARNING + "-"*78 + string = "-" * 78 + SUPERUSER_WARNING + "-" * 78 character.msg("|r%s|n" % string.format(name=character.key, quell="|w@quell|r")) @@ -589,16 +592,16 @@ class BridgeCmdSet(CmdSet): BRIDGE_WEATHER = ( - "The rain intensifies, making the planks of the bridge even more slippery.", - "A gust of wind throws the rain right in your face.", - "The rainfall eases a bit and the sky momentarily brightens.", - "The bridge shakes under the thunder of a closeby thunder strike.", - "The rain pummels you with large, heavy drops. You hear the distinct howl of a large hound in the distance.", - "The wind is picking up, howling around you and causing the bridge to sway from side to side.", - "Some sort of large bird sweeps by overhead, giving off an eery screech. Soon it has disappeared in the gloom.", - "The bridge sways from side to side in the wind.", - "Below you a particularly large wave crashes into the rocks.", - "From the ruin you hear a distant, otherwordly howl. Or maybe it was just the wind.") + "The rain intensifies, making the planks of the bridge even more slippery.", + "A gust of wind throws the rain right in your face.", + "The rainfall eases a bit and the sky momentarily brightens.", + "The bridge shakes under the thunder of a closeby thunder strike.", + "The rain pummels you with large, heavy drops. You hear the distinct howl of a large hound in the distance.", + "The wind is picking up, howling around you and causing the bridge to sway from side to side.", + "Some sort of large bird sweeps by overhead, giving off an eery screech. Soon it has disappeared in the gloom.", + "The bridge sways from side to side in the wind.", + "Below you a particularly large wave crashes into the rocks.", + "From the ruin you hear a distant, otherwordly howl. Or maybe it was just the wind.") class BridgeRoom(WeatherRoom): @@ -625,6 +628,7 @@ class BridgeRoom(WeatherRoom): the CmdLookBridge command). """ + def at_object_creation(self): """Setups the room""" # this will start the weather room's ticker and tell @@ -827,6 +831,7 @@ class DarkRoom(TutorialRoom): may have been beaten up by the ghostly apparition at this point. """ + def at_object_creation(self): """ Called when object is first created. @@ -942,6 +947,7 @@ class TeleportRoom(TutorialRoom): failure_teleport_msg - message to echo while teleporting to failure """ + def at_object_creation(self): """Called at first creation""" super(TeleportRoom, self).at_object_creation() diff --git a/evennia/contrib/unixcommand.py b/evennia/contrib/unixcommand.py index 6a77b7277..8ba538c57 100644 --- a/evennia/contrib/unixcommand.py +++ b/evennia/contrib/unixcommand.py @@ -111,8 +111,8 @@ class UnixCommandParser(argparse.ArgumentParser): """ prog = prog or command.key super(UnixCommandParser, self).__init__( - prog=prog, description=description, - conflict_handler='resolve', add_help=False, **kwargs) + prog=prog, description=description, + conflict_handler='resolve', add_help=False, **kwargs) self.command = command self.post_help = epilog diff --git a/evennia/game_template/commands/default_cmdsets.py b/evennia/game_template/commands/default_cmdsets.py index 0a599177c..a2e5ffbf4 100644 --- a/evennia/game_template/commands/default_cmdsets.py +++ b/evennia/game_template/commands/default_cmdsets.py @@ -16,6 +16,7 @@ own cmdsets by inheriting from them or directly from `evennia.CmdSet`. from evennia import default_cmds + class CharacterCmdSet(default_cmds.CharacterCmdSet): """ The `CharacterCmdSet` contains general in-game commands like `look`, diff --git a/evennia/game_template/server/conf/at_search.py b/evennia/game_template/server/conf/at_search.py index a43a04060..09828944b 100644 --- a/evennia/game_template/server/conf/at_search.py +++ b/evennia/game_template/server/conf/at_search.py @@ -25,6 +25,7 @@ line to your settings file: """ + def at_search_result(matches, caller, query="", quiet=False, **kwargs): """ This is a generic hook for handling all processing of a search diff --git a/evennia/game_template/server/conf/cmdparser.py b/evennia/game_template/server/conf/cmdparser.py index b2d024185..831990a89 100644 --- a/evennia/game_template/server/conf/cmdparser.py +++ b/evennia/game_template/server/conf/cmdparser.py @@ -31,6 +31,7 @@ your settings file: """ + def cmdparser(raw_string, cmdset, caller, match_index=None): """ This function is called by the cmdhandler once it has diff --git a/evennia/game_template/server/conf/inlinefuncs.py b/evennia/game_template/server/conf/inlinefuncs.py index c4edd83da..119059767 100644 --- a/evennia/game_template/server/conf/inlinefuncs.py +++ b/evennia/game_template/server/conf/inlinefuncs.py @@ -45,7 +45,7 @@ and can be used to customize it to each session. """ -#def capitalize(text, *args, **kwargs): +# def capitalize(text, *args, **kwargs): # "Silly capitalize example. Used as {capitalize() ... {/capitalize" # session = kwargs.get("session") # return text.capitalize() diff --git a/evennia/game_template/server/conf/lockfuncs.py b/evennia/game_template/server/conf/lockfuncs.py index 2ca59c779..8dac0c703 100644 --- a/evennia/game_template/server/conf/lockfuncs.py +++ b/evennia/game_template/server/conf/lockfuncs.py @@ -20,7 +20,7 @@ lock functions from evennia.locks.lockfuncs. """ -#def myfalse(accessing_obj, accessed_obj, *args, **kwargs): +# def myfalse(accessing_obj, accessed_obj, *args, **kwargs): # """ # called in lockstring with myfalse(). # A simple logger that always returns false. Prints to stdout diff --git a/evennia/game_template/server/conf/mssp.py b/evennia/game_template/server/conf/mssp.py index e00aad631..b0b8e6a6b 100644 --- a/evennia/game_template/server/conf/mssp.py +++ b/evennia/game_template/server/conf/mssp.py @@ -13,105 +13,105 @@ affect uptime). """ -MSSPTable = { +MSSPTable = { # Required fieldss - "NAME": "Evennia", + "NAME": "Evennia", # Generic - "CRAWL DELAY": "-1", # limit how often crawler updates the listing. -1 for no limit + "CRAWL DELAY": "-1", # limit how often crawler updates the listing. -1 for no limit - "HOSTNAME": "", # current or new hostname - "PORT": ["4000"], # most important port should be last in list - "CODEBASE": "Evennia", - "CONTACT": "", # email for contacting the mud - "CREATED": "", # year MUD was created - "ICON": "", # url to icon 32x32 or larger; <32kb. - "IP": "", # current or new IP address - "LANGUAGE": "", # name of language used, e.g. English - "LOCATION": "", # full English name of server country - "MINIMUM AGE": "0", # set to 0 if not applicable - "WEBSITE": "www.evennia.com", + "HOSTNAME": "", # current or new hostname + "PORT": ["4000"], # most important port should be last in list + "CODEBASE": "Evennia", + "CONTACT": "", # email for contacting the mud + "CREATED": "", # year MUD was created + "ICON": "", # url to icon 32x32 or larger; <32kb. + "IP": "", # current or new IP address + "LANGUAGE": "", # name of language used, e.g. English + "LOCATION": "", # full English name of server country + "MINIMUM AGE": "0", # set to 0 if not applicable + "WEBSITE": "www.evennia.com", # Categorisation - "FAMILY": "Custom", # evennia goes under 'Custom' - "GENRE": "None", # Adult, Fantasy, Historical, Horror, Modern, None, or Science Fiction - "GAMEPLAY": "", # Adventure, Educational, Hack and Slash, None, - # Player versus Player, Player versus Environment, - # Roleplaying, Simulation, Social or Strategy - "STATUS": "Open Beta", # Alpha, Closed Beta, Open Beta, Live - "GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew - "SUBGENRE": "None", # LASG, Medieval Fantasy, World War II, Frankenstein, - # Cyberpunk, Dragonlance, etc. Or None if not available. + "FAMILY": "Custom", # evennia goes under 'Custom' + "GENRE": "None", # Adult, Fantasy, Historical, Horror, Modern, None, or Science Fiction + "GAMEPLAY": "", # Adventure, Educational, Hack and Slash, None, + # Player versus Player, Player versus Environment, + # Roleplaying, Simulation, Social or Strategy + "STATUS": "Open Beta", # Alpha, Closed Beta, Open Beta, Live + "GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew + "SUBGENRE": "None", # LASG, Medieval Fantasy, World War II, Frankenstein, + # Cyberpunk, Dragonlance, etc. Or None if not available. # World - "AREAS": "0", - "HELPFILES": "0", - "MOBILES": "0", - "OBJECTS": "0", - "ROOMS": "0", # use 0 if room-less - "CLASSES": "0", # use 0 if class-less - "LEVELS": "0", # use 0 if level-less - "RACES": "0", # use 0 if race-less - "SKILLS": "0", # use 0 if skill-less + "AREAS": "0", + "HELPFILES": "0", + "MOBILES": "0", + "OBJECTS": "0", + "ROOMS": "0", # use 0 if room-less + "CLASSES": "0", # use 0 if class-less + "LEVELS": "0", # use 0 if level-less + "RACES": "0", # use 0 if race-less + "SKILLS": "0", # use 0 if skill-less # Protocols set to 1 or 0) - "ANSI": "1", - "GMCP": "1", - "ATCP": "0", - "MCCP": "0", - "MCP": "0", - "MSDP": "0", - "MSP": "0", - "MXP": "0", - "PUEBLO": "0", - "SSL": "1", - "UTF-8": "1", - "VT100": "0", - "ZMP": "0", - "XTERM 256 COLORS": "0", + "ANSI": "1", + "GMCP": "1", + "ATCP": "0", + "MCCP": "0", + "MCP": "0", + "MSDP": "0", + "MSP": "0", + "MXP": "0", + "PUEBLO": "0", + "SSL": "1", + "UTF-8": "1", + "VT100": "0", + "ZMP": "0", + "XTERM 256 COLORS": "0", # Commercial set to 1 or 0) - "PAY TO PLAY": "0", - "PAY FOR PERKS": "0", + "PAY TO PLAY": "0", + "PAY FOR PERKS": "0", # Hiring set to 1 or 0) - "HIRING BUILDERS": "0", - "HIRING CODERS": "0", + "HIRING BUILDERS": "0", + "HIRING CODERS": "0", # Extended variables # World - "DBSIZE": "0", - "EXITS": "0", + "DBSIZE": "0", + "EXITS": "0", "EXTRA DESCRIPTIONS": "0", - "MUDPROGS": "0", - "MUDTRIGS": "0", - "RESETS": "0", + "MUDPROGS": "0", + "MUDTRIGS": "0", + "RESETS": "0", # Game (set to 1 or 0, or one of the given alternatives) - "ADULT MATERIAL": "0", - "MULTICLASSING": "0", - "NEWBIE FRIENDLY": "0", - "PLAYER CITIES": "0", - "PLAYER CLANS": "0", - "PLAYER CRAFTING": "0", - "PLAYER GUILDS": "0", - "EQUIPMENT SYSTEM": "None", # "None", "Level", "Skill", "Both" - "MULTIPLAYING": "None", # "None", "Restricted", "Full" - "PLAYERKILLING": "None", # "None", "Restricted", "Full" - "QUEST SYSTEM": "None", # "None", "Immortal Run", "Automated", "Integrated" - "ROLEPLAYING": "None", # "None", "Accepted", "Encouraged", "Enforced" - "TRAINING SYSTEM": "None", # "None", "Level", "Skill", "Both" - "WORLD ORIGINALITY": "None", # "All Stock", "Mostly Stock", "Mostly Original", "All Original" + "ADULT MATERIAL": "0", + "MULTICLASSING": "0", + "NEWBIE FRIENDLY": "0", + "PLAYER CITIES": "0", + "PLAYER CLANS": "0", + "PLAYER CRAFTING": "0", + "PLAYER GUILDS": "0", + "EQUIPMENT SYSTEM": "None", # "None", "Level", "Skill", "Both" + "MULTIPLAYING": "None", # "None", "Restricted", "Full" + "PLAYERKILLING": "None", # "None", "Restricted", "Full" + "QUEST SYSTEM": "None", # "None", "Immortal Run", "Automated", "Integrated" + "ROLEPLAYING": "None", # "None", "Accepted", "Encouraged", "Enforced" + "TRAINING SYSTEM": "None", # "None", "Level", "Skill", "Both" + "WORLD ORIGINALITY": "None", # "All Stock", "Mostly Stock", "Mostly Original", "All Original" - } +} diff --git a/evennia/game_template/server/conf/serversession.py b/evennia/game_template/server/conf/serversession.py index acaa6a9f2..a04f6db43 100644 --- a/evennia/game_template/server/conf/serversession.py +++ b/evennia/game_template/server/conf/serversession.py @@ -23,6 +23,7 @@ settings file: from evennia.server.serversession import ServerSession as BaseServerSession + class ServerSession(BaseServerSession): """ This class represents a player's session and is a template for diff --git a/evennia/game_template/server/conf/web_plugins.py b/evennia/game_template/server/conf/web_plugins.py index eccda6ddd..4050a8266 100644 --- a/evennia/game_template/server/conf/web_plugins.py +++ b/evennia/game_template/server/conf/web_plugins.py @@ -26,4 +26,3 @@ def at_webserver_root_creation(web_root): """ return web_root - diff --git a/evennia/game_template/typeclasses/accounts.py b/evennia/game_template/typeclasses/accounts.py index 9938da843..bbab3d4f2 100644 --- a/evennia/game_template/typeclasses/accounts.py +++ b/evennia/game_template/typeclasses/accounts.py @@ -24,6 +24,7 @@ several more options for customizing the Guest account system. from evennia import DefaultAccount, DefaultGuest + class Account(DefaultAccount): """ This class describes the actual OOC account (i.e. the user connecting diff --git a/evennia/game_template/typeclasses/channels.py b/evennia/game_template/typeclasses/channels.py index 7a04746fe..a1af6d763 100644 --- a/evennia/game_template/typeclasses/channels.py +++ b/evennia/game_template/typeclasses/channels.py @@ -14,6 +14,7 @@ to be modified. from evennia import DefaultChannel + class Channel(DefaultChannel): """ Working methods: diff --git a/evennia/game_template/typeclasses/characters.py b/evennia/game_template/typeclasses/characters.py index 11d747e1c..a6942609b 100644 --- a/evennia/game_template/typeclasses/characters.py +++ b/evennia/game_template/typeclasses/characters.py @@ -9,6 +9,7 @@ creation commands. """ from evennia import DefaultCharacter + class Character(DefaultCharacter): """ The Character defaults to reimplementing some of base Object's hook methods with the @@ -22,7 +23,7 @@ class Character(DefaultCharacter): at_post_unpuppet(account) - when Account disconnects from the Character, we store the current location in the pre_logout_location Attribute and move it to a None-location so the "unpuppeted" character - object does not need to stay on grid. Echoes "Account has disconnected" + object does not need to stay on grid. Echoes "Account has disconnected" to the room. at_pre_puppet - Just before Account re-connects, retrieves the character's pre_logout_location Attribute and move it back on the grid. diff --git a/evennia/game_template/typeclasses/exits.py b/evennia/game_template/typeclasses/exits.py index 53a942de1..6494641c1 100644 --- a/evennia/game_template/typeclasses/exits.py +++ b/evennia/game_template/typeclasses/exits.py @@ -8,6 +8,7 @@ for allowing Characters to traverse the exit to its destination. """ from evennia import DefaultExit + class Exit(DefaultExit): """ Exits are connectors between rooms. Exits are normal Objects except diff --git a/evennia/game_template/typeclasses/objects.py b/evennia/game_template/typeclasses/objects.py index 3523689fe..12468bbd5 100644 --- a/evennia/game_template/typeclasses/objects.py +++ b/evennia/game_template/typeclasses/objects.py @@ -12,6 +12,7 @@ inheritance. """ from evennia import DefaultObject + class Object(DefaultObject): """ This is the root typeclass object, implementing an in-game Evennia diff --git a/evennia/game_template/world/prototypes.py b/evennia/game_template/world/prototypes.py index 063510a25..d317bfa5b 100644 --- a/evennia/game_template/world/prototypes.py +++ b/evennia/game_template/world/prototypes.py @@ -38,7 +38,7 @@ See the `@spawn` command and `evennia.utils.spawner` for more info. #from random import randint # -#GOBLIN = { +# GOBLIN = { # "key": "goblin grunt", # "health": lambda: randint(20,30), # "resists": ["cold", "poison"], @@ -46,13 +46,13 @@ See the `@spawn` command and `evennia.utils.spawner` for more info. # "weaknesses": ["fire", "light"] # } # -#GOBLIN_WIZARD = { +# GOBLIN_WIZARD = { # "prototype": "GOBLIN", # "key": "goblin wizard", # "spells": ["fire ball", "lighting bolt"] # } # -#GOBLIN_ARCHER = { +# GOBLIN_ARCHER = { # "prototype": "GOBLIN", # "key": "goblin archer", # "attacks": ["short bow"] @@ -62,12 +62,12 @@ See the `@spawn` command and `evennia.utils.spawner` for more info. # (nor key) of its own, so it should normally only be # used as a mix-in, as in the example of the goblin # archwizard below. -#ARCHWIZARD_MIXIN = { +# ARCHWIZARD_MIXIN = { # "attacks": ["archwizard staff"], # "spells": ["greater fire ball", "greater lighting"] #} # -#GOBLIN_ARCHWIZARD = { +# GOBLIN_ARCHWIZARD = { # "key": "goblin archwizard", # "prototype" : ("GOBLIN_WIZARD", "ARCHWIZARD_MIXIN") #} diff --git a/evennia/help/admin.py b/evennia/help/admin.py index 91f974afa..9e31fc365 100644 --- a/evennia/help/admin.py +++ b/evennia/help/admin.py @@ -8,12 +8,12 @@ from django.contrib import admin from evennia.help.models import HelpEntry from evennia.typeclasses.admin import TagInline + class HelpTagInline(TagInline): model = HelpEntry.db_tags.through related_field = "helpentry" - class HelpEntryForm(forms.ModelForm): "Defines how to display the help entry" class Meta(object): @@ -22,8 +22,9 @@ class HelpEntryForm(forms.ModelForm): db_help_category = forms.CharField(label="Help category", initial='General', help_text="organizes help entries in lists") - db_lock_storage = forms.CharField(label="Locks", initial='view:all()',required=False, - widget=forms.TextInput(attrs={'size':'40'}),) + db_lock_storage = forms.CharField(label="Locks", initial='view:all()', required=False, + widget=forms.TextInput(attrs={'size': '40'}),) + class HelpEntryAdmin(admin.ModelAdmin): "Sets up the admin manaager for help entries" @@ -38,9 +39,9 @@ class HelpEntryAdmin(admin.ModelAdmin): form = HelpEntryForm fieldsets = ( - (None, {'fields':(('db_key', 'db_help_category'), + (None, {'fields': (('db_key', 'db_help_category'), 'db_entrytext', 'db_lock_storage'), - 'description':"Sets a Help entry. Set lock to view:all() unless you want to restrict it."}),) + 'description': "Sets a Help entry. Set lock to view:all() unless you want to restrict it."}),) admin.site.register(HelpEntry, HelpEntryAdmin) diff --git a/evennia/help/manager.py b/evennia/help/manager.py index f8ca23d39..1acb01be0 100644 --- a/evennia/help/manager.py +++ b/evennia/help/manager.py @@ -24,6 +24,7 @@ class HelpEntryManager(TypedObjectManager): search_help (equivalent to evennia.search_helpentry) """ + def find_topicmatch(self, topicstr, exact=False): """ Searches for matching topics or aliases based on player's diff --git a/evennia/help/models.py b/evennia/help/models.py index 2433963b8..4a81a9409 100644 --- a/evennia/help/models.py +++ b/evennia/help/models.py @@ -52,14 +52,14 @@ class HelpEntry(SharedMemoryModel): db_key = models.CharField('help key', max_length=255, unique=True, help_text='key to search for') # help category db_help_category = models.CharField("help category", max_length=255, default="General", - help_text='organizes help entries in lists') + help_text='organizes help entries in lists') # the actual help entry text, in any formatting. db_entrytext = models.TextField('help entry', blank=True, help_text='the main body of help text') # lock string storage db_lock_storage = models.TextField('locks', blank=True, help_text='normally view:all().') # tags are primarily used for permissions db_tags = models.ManyToManyField(Tag, blank=True, - help_text='tags on this object. Tags are simple string markers to identify, group and alias objects.') + help_text='tags on this object. Tags are simple string markers to identify, group and alias objects.') # (deprecated, only here to allow MUX helpfile load (don't use otherwise)). # TODO: remove this when not needed anymore. db_staff_only = models.BooleanField(default=False) diff --git a/evennia/locks/lockfuncs.py b/evennia/locks/lockfuncs.py index 48e0718ca..e30263adb 100644 --- a/evennia/locks/lockfuncs.py +++ b/evennia/locks/lockfuncs.py @@ -280,8 +280,8 @@ def pid(accessing_obj, accessed_obj, *args, **kwargs): # this is more efficient than multiple if ... elif statments CF_MAPPING = {'eq': lambda val1, val2: val1 == val2 or str(val1) == str(val2) or float(val1) == float(val2), - 'gt': lambda val1, val2: float(val1) > float(val2), - 'lt': lambda val1, val2: float(val1) < float(val2), + 'gt': lambda val1, val2: float(val1) > float(val2), + 'lt': lambda val1, val2: float(val1) < float(val2), 'ge': lambda val1, val2: float(val1) >= float(val2), 'le': lambda val1, val2: float(val1) <= float(val2), 'ne': lambda val1, val2: float(val1) != float(val2), @@ -346,8 +346,8 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs): # check attributes, if they exist if (hasattr(accessing_obj, 'attributes') and accessing_obj.attributes.has(attrname)): if value: - return (hasattr(accessing_obj, 'attributes') - and valcompare(accessing_obj.attributes.get(attrname), value, compare)) + return (hasattr(accessing_obj, 'attributes') and + valcompare(accessing_obj.attributes.get(attrname), value, compare)) # fails on False/None values return bool(accessing_obj.attributes.get(attrname)) return False @@ -366,6 +366,7 @@ def objattr(accessing_obj, accessed_obj, *args, **kwargs): """ return attr(accessed_obj, accessed_obj, *args, **kwargs) + def locattr(accessing_obj, accessed_obj, *args, **kwargs): """ Usage: @@ -386,6 +387,7 @@ def locattr(accessing_obj, accessed_obj, *args, **kwargs): return attr(accessing_obj.location, accessed_obj, *args, **kwargs) return False + def objlocattr(accessing_obj, accessed_obj, *args, **kwargs): """ Usage: @@ -464,6 +466,7 @@ def attr_ne(accessing_obj, accessed_obj, *args, **kwargs): """ return attr(accessing_obj, accessed_obj, *args, **{'compare': 'ne'}) + def tag(accessing_obj, accessed_obj, *args, **kwargs): """ Usage: @@ -481,6 +484,7 @@ def tag(accessing_obj, accessed_obj, *args, **kwargs): category = args[1] if len(args) > 1 else None return accessing_obj.tags.get(tagkey, category=category) + def objtag(accessing_obj, accessed_obj, *args, **kwargs): """ Usage: @@ -492,6 +496,7 @@ def objtag(accessing_obj, accessed_obj, *args, **kwargs): """ return accessed_obj.tags.get(*args) + def inside(accessing_obj, accessed_obj, *args, **kwargs): """ Usage: @@ -548,7 +553,7 @@ def holds(accessing_obj, accessed_obj, *args, **kwargs): if len(args) == 1: # command is holds(dbref/key) - check if given objname/dbref is held by accessing_ob return check_holds(args[0]) - elif len(args = 2): + elif len(args=2): # command is holds(attrname, value) check if any held object has the given attribute and value for obj in contents: if obj.attributes.get(args[0]) == args[1]: @@ -566,6 +571,7 @@ def superuser(*args, **kwargs): """ return False + def has_account(accessing_obj, accessed_obj, *args, **kwargs): """ Only returns true if accessing_obj has_account is true, that is, @@ -576,6 +582,7 @@ def has_account(accessing_obj, accessed_obj, *args, **kwargs): """ return hasattr(accessing_obj, "has_account") and accessing_obj.has_account + def serversetting(accessing_obj, accessed_obj, *args, **kwargs): """ Only returns true if the Evennia settings exists, alternatively has diff --git a/evennia/locks/lockhandler.py b/evennia/locks/lockhandler.py index 07240b671..30d17749a 100644 --- a/evennia/locks/lockhandler.py +++ b/evennia/locks/lockhandler.py @@ -121,6 +121,7 @@ WARNING_LOG = settings.LOCKWARNING_LOG_FILE # by errors in lock definitions. # + class LockException(Exception): """ Raised during an error in a lock. @@ -133,6 +134,8 @@ class LockException(Exception): # _LOCKFUNCS = {} + + def _cache_lockfuncs(): """ Updates the cache. @@ -146,6 +149,7 @@ def _cache_lockfuncs(): # pre-compiled regular expressions # + _RE_FUNCS = re.compile(r"\w+\([^)]*\)") _RE_SEPS = re.compile(r"(?<=[ )])AND(?=\s)|(?<=[ )])OR(?=\s)|(?<=[ )])NOT(?=\s)") _RE_OK = re.compile(r"%s|and|or|not") @@ -229,7 +233,7 @@ class LockHandler(object): if not callable(func): elist.append(_("Lock: lock-function '%s' is not available.") % funcstring) continue - args = list(arg.strip() for arg in rest.split(',') if arg and not '=' in arg) + args = list(arg.strip() for arg in rest.split(',') if arg and '=' not in arg) kwargs = dict([arg.split('=', 1) for arg in rest.split(',') if arg and '=' in arg]) lock_funcs.append((func, args, kwargs)) evalstring = evalstring.replace(funcstring, '%s') @@ -244,8 +248,8 @@ class LockHandler(object): continue if access_type in locks: duplicates += 1 - wlist.append(_("LockHandler on %(obj)s: access type '%(access_type)s' changed from '%(source)s' to '%(goal)s' " % \ - {"obj":self.obj, "access_type":access_type, "source":locks[access_type][2], "goal":raw_lockstring})) + wlist.append(_("LockHandler on %(obj)s: access type '%(access_type)s' changed from '%(source)s' to '%(goal)s' " % + {"obj": self.obj, "access_type": access_type, "source": locks[access_type][2], "goal": raw_lockstring})) locks[access_type] = (evalstring, tuple(lock_funcs), raw_lockstring) if wlist and WARNING_LOG: # a warning text was set, it's not an error, so only report @@ -300,7 +304,7 @@ class LockHandler(object): """ # sanity checks for lockdef in lockstring.split(';'): - if not ':' in lockstring: + if ':' not in lockstring: self._log_error(_("Lock: '%s' contains no colon (:).") % lockdef) return False access_type, rhs = [part.strip() for part in lockdef.split(':', 1)] @@ -391,7 +395,7 @@ class LockHandler(object): self._save_locks() return True return False - delete = remove # alias for historical reasons + delete = remove # alias for historical reasons def clear(self): """ @@ -449,9 +453,9 @@ class LockHandler(object): return True except AttributeError: # happens before session is initiated. - if not no_superuser_bypass and ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) - or (hasattr(accessing_obj, 'account') and hasattr(accessing_obj.account, 'is_superuser') and accessing_obj.account.is_superuser) - or (hasattr(accessing_obj, 'get_account') and (not accessing_obj.get_account() or accessing_obj.get_account().is_superuser))): + if not no_superuser_bypass and ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) or + (hasattr(accessing_obj, 'account') and hasattr(accessing_obj.account, 'is_superuser') and accessing_obj.account.is_superuser) or + (hasattr(accessing_obj, 'get_account') and (not accessing_obj.get_account() or accessing_obj.get_account().is_superuser))): return True # no superuser or bypass -> normal lock operation @@ -510,17 +514,17 @@ class LockHandler(object): if accessing_obj.locks.lock_bypass and not no_superuser_bypass: return True except AttributeError: - if no_superuser_bypass and ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) - or (hasattr(accessing_obj, 'account') and hasattr(accessing_obj.account, 'is_superuser') and accessing_obj.account.is_superuser) - or (hasattr(accessing_obj, 'get_account') and (not accessing_obj.get_account() or accessing_obj.get_account().is_superuser))): + if no_superuser_bypass and ((hasattr(accessing_obj, 'is_superuser') and accessing_obj.is_superuser) or + (hasattr(accessing_obj, 'account') and hasattr(accessing_obj.account, 'is_superuser') and accessing_obj.account.is_superuser) or + (hasattr(accessing_obj, 'get_account') and (not accessing_obj.get_account() or accessing_obj.get_account().is_superuser))): return True - if not ":" in lockstring: + if ":" not in lockstring: lockstring = "%s:%s" % ("_dummy", lockstring) locks = self._parse_lockstring(lockstring) if access_type: - if not access_type in locks: + if access_type not in locks: return default else: return self._eval_access_type( @@ -541,7 +545,7 @@ def _test(): obj1 = TestObj() obj2 = TestObj() - #obj1.lock_storage = "owner:dbref(#4);edit:dbref(#5) or perm(Admin);examine:perm(Builder);delete:perm(Admin);get:all()" + # obj1.lock_storage = "owner:dbref(#4);edit:dbref(#5) or perm(Admin);examine:perm(Builder);delete:perm(Admin);get:all()" #obj1.lock_storage = "cmd:all();admin:id(1);listen:all();send:all()" obj1.lock_storage = "listen:perm(Developer)" @@ -550,7 +554,7 @@ def _test(): obj2.permissions.add("Developer") obj2.id = 4 - #obj1.locks.add("edit:attr(test)") + # obj1.locks.add("edit:attr(test)") print("comparing obj2.permissions (%s) vs obj1.locks (%s)" % (obj2.permissions, obj1.locks)) print(obj1.locks.check(obj2, 'owner')) diff --git a/evennia/objects/admin.py b/evennia/objects/admin.py index 706ad331e..007cfe21c 100644 --- a/evennia/objects/admin.py +++ b/evennia/objects/admin.py @@ -97,18 +97,18 @@ class ObjectDBAdmin(admin.ModelAdmin): form = ObjectEditForm fieldsets = ( (None, { - 'fields': (('db_key', 'db_typeclass_path'), ('db_lock_storage', ), - ('db_location', 'db_home'), 'db_destination', 'db_cmdset_storage' - )}), - ) + 'fields': (('db_key', 'db_typeclass_path'), ('db_lock_storage', ), + ('db_location', 'db_home'), 'db_destination', 'db_cmdset_storage' + )}), + ) add_form = ObjectCreateForm add_fieldsets = ( (None, { - 'fields': (('db_key', 'db_typeclass_path'), - ('db_location', 'db_home'), 'db_destination', 'db_cmdset_storage' - )}), - ) + 'fields': (('db_key', 'db_typeclass_path'), + ('db_location', 'db_home'), 'db_destination', 'db_cmdset_storage' + )}), + ) def get_fieldsets(self, request, obj=None): """ diff --git a/evennia/objects/manager.py b/evennia/objects/manager.py index 01012c261..3d29768e5 100644 --- a/evennia/objects/manager.py +++ b/evennia/objects/manager.py @@ -261,7 +261,7 @@ class ObjectDBManager(TypedObjectManager): if exact: # exact match - do direct search return self.filter(cand_restriction & type_restriction & (Q(db_key__iexact=ostring) | - Q(db_tags__db_key__iexact=ostring) & Q(db_tags__db_tagtype__iexact="alias"))).distinct() + Q(db_tags__db_key__iexact=ostring) & Q(db_tags__db_tagtype__iexact="alias"))).distinct() elif candidates: # fuzzy with candidates search_candidates = self.filter(cand_restriction & type_restriction) diff --git a/evennia/objects/migrations/0004_auto_20150118_1622.py b/evennia/objects/migrations/0004_auto_20150118_1622.py index 3cc4d8bee..4626b63c9 100644 --- a/evennia/objects/migrations/0004_auto_20150118_1622.py +++ b/evennia/objects/migrations/0004_auto_20150118_1622.py @@ -3,12 +3,14 @@ from __future__ import unicode_literals from django.db import models, migrations + def convert_defaults(apps, schema_editor): ObjectDB = apps.get_model("objects", "ObjectDB") for obj in ObjectDB.objects.filter(db_typeclass_path="src.objects.objects.Object"): obj.db_typeclass_path = "typeclasses.objects.Object" obj.save() + class Migration(migrations.Migration): dependencies = [ @@ -16,5 +18,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(convert_defaults), + migrations.RunPython(convert_defaults), ] diff --git a/evennia/objects/migrations/0008_auto_20170705_1736.py b/evennia/objects/migrations/0008_auto_20170705_1736.py index 4e1ba6429..c2cd711af 100644 --- a/evennia/objects/migrations/0008_auto_20170705_1736.py +++ b/evennia/objects/migrations/0008_auto_20170705_1736.py @@ -21,6 +21,7 @@ def forwards(apps, schema_editor): object.db_account = account object.save(update_fields=['db_account']) + class Migration(migrations.Migration): dependencies = [ diff --git a/evennia/objects/models.py b/evennia/objects/models.py index e06b04c31..adac6e0a5 100644 --- a/evennia/objects/models.py +++ b/evennia/objects/models.py @@ -33,6 +33,7 @@ class ContentsHandler(object): for object-cmdsets). It is stored on the 'contents_cache' property of the ObjectDB. """ + def __init__(self, obj): """ Sets up the contents handler. @@ -171,12 +172,12 @@ class ObjectDB(TypedObject): # If this is a character object, the account is connected here. db_account = models.ForeignKey("accounts.AccountDB", null=True, verbose_name='account', on_delete=models.SET_NULL, - help_text='an Account connected to this object, if any.') + help_text='an Account connected to this object, if any.') # the session id associated with this account, if any db_sessid = models.CharField(null=True, max_length=32, validators=[validate_comma_separated_integer_list], - verbose_name="session id", - help_text="csv list of session ids of connected Account, if any.") + verbose_name="session id", + help_text="csv list of session ids of connected Account, if any.") # The location in the game world. Since this one is likely # to change often, we set this with the 'location' property # to transparently handle Typeclassing. diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 65d8087a7..bbc517ba5 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -39,6 +39,7 @@ class ObjectSessionHandler(object): Handles the get/setting of the sessid comma-separated integer field """ + def __init__(self, obj): """ Initializes the handler. @@ -1211,10 +1212,10 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)): mapping = {} mapping.update({ - "object": self, - "exit": exits[0] if exits else "somwhere", - "origin": location or "nowhere", - "destination": destination or "nowhere", + "object": self, + "exit": exits[0] if exits else "somwhere", + "origin": location or "nowhere", + "destination": destination or "nowhere", }) location.msg_contents(string, exclude=(self, ), mapping=mapping) @@ -1267,10 +1268,10 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)): mapping = {} mapping.update({ - "object": self, - "exit": exits[0] if exits else "somewhere", - "origin": origin or "nowhere", - "destination": destination or "nowhere", + "object": self, + "exit": exits[0] if exits else "somewhere", + "origin": origin or "nowhere", + "destination": destination or "nowhere", }) destination.msg_contents(string, exclude=(self, ), mapping=mapping) @@ -1630,20 +1631,20 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)): mapping = mapping or {} mapping.update({ - "object": self, - "location": self.location, - "speech": message, - "receiver": receiver - }) + "object": self, + "location": self.location, + "speech": message, + "receiver": receiver + }) if msg_self: self_mapping = {k: v.get_display_name(self) if hasattr( - v, "get_display_name") else str(v) for k, v in mapping.items()} + v, "get_display_name") else str(v) for k, v in mapping.items()} self.msg(msg_self.format(**self_mapping)) if receiver and msg_receiver: receiver_mapping = {k: v.get_display_name(receiver) if hasattr( - v, "get_display_name") else str(v) for k, v in mapping.items()} + v, "get_display_name") else str(v) for k, v in mapping.items()} receiver.msg(msg_receiver.format(**receiver_mapping)) if self.location and msg_location: @@ -1778,6 +1779,7 @@ class DefaultRoom(DefaultObject): This is the base room object. It's just like any Object except its location is always `None`. """ + def basetype_setup(self): """ Simple room setup setting locks to make sure the room diff --git a/evennia/scripts/admin.py b/evennia/scripts/admin.py index a5d8944a6..230fbf6e3 100644 --- a/evennia/scripts/admin.py +++ b/evennia/scripts/admin.py @@ -46,10 +46,10 @@ class ScriptDBAdmin(admin.ModelAdmin): fieldsets = ( (None, { - 'fields': (('db_key', 'db_typeclass_path'), 'db_interval', - 'db_repeats', 'db_start_delay', 'db_persistent', - 'db_obj')}), - ) + 'fields': (('db_key', 'db_typeclass_path'), 'db_interval', + 'db_repeats', 'db_start_delay', 'db_persistent', + 'db_obj')}), + ) inlines = [ScriptTagInline, ScriptAttributeInline] def save_model(self, request, obj, form, change): diff --git a/evennia/scripts/manager.py b/evennia/scripts/manager.py index c3753ed47..17a11d20b 100644 --- a/evennia/scripts/manager.py +++ b/evennia/scripts/manager.py @@ -34,6 +34,7 @@ class ScriptDBManager(TypedObjectManager): copy_script """ + def get_all_scripts_on_obj(self, obj, key=None): """ Find all Scripts related to a particular object. diff --git a/evennia/scripts/migrations/0002_auto_20150118_1625.py b/evennia/scripts/migrations/0002_auto_20150118_1625.py index 3d9c550c9..272c9b73d 100644 --- a/evennia/scripts/migrations/0002_auto_20150118_1625.py +++ b/evennia/scripts/migrations/0002_auto_20150118_1625.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from django.db import models, migrations + def convert_defaults(apps, schema_editor): ScriptDB = apps.get_model("scripts", "ScriptDB") for script in ScriptDB.objects.filter(db_typeclass_path="src.scripts.scripts.Script"): @@ -12,6 +13,7 @@ def convert_defaults(apps, schema_editor): script.db_typeclass_path = "evennia.utils.gametime.GameTime" script.save() + class Migration(migrations.Migration): dependencies = [ @@ -19,5 +21,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(convert_defaults), + migrations.RunPython(convert_defaults), ] diff --git a/evennia/scripts/migrations/0004_auto_20150306_1354.py b/evennia/scripts/migrations/0004_auto_20150306_1354.py index d9b420857..72bd29a4e 100644 --- a/evennia/scripts/migrations/0004_auto_20150306_1354.py +++ b/evennia/scripts/migrations/0004_auto_20150306_1354.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from django.db import models, migrations + def remove_manage_scripts(apps, schema_editor): ScriptDB = apps.get_model("scripts", "ScriptDB") for script in ScriptDB.objects.filter(db_typeclass_path__in=(u'evennia.scripts.scripts.CheckSessions', @@ -12,6 +13,7 @@ def remove_manage_scripts(apps, schema_editor): u'evennia.utils.gametime.GameTime')): script.delete() + class Migration(migrations.Migration): dependencies = [ diff --git a/evennia/scripts/migrations/0006_auto_20150310_2249.py b/evennia/scripts/migrations/0006_auto_20150310_2249.py index 1228fa8e8..d027d4900 100644 --- a/evennia/scripts/migrations/0006_auto_20150310_2249.py +++ b/evennia/scripts/migrations/0006_auto_20150310_2249.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from django.db import models, migrations + def remove_manage_scripts(apps, schema_editor): ScriptDB = apps.get_model("scripts", "ScriptDB") for script in ScriptDB.objects.filter(db_typeclass_path__in=(u'src.scripts.scripts.CheckSessions', @@ -12,6 +13,7 @@ def remove_manage_scripts(apps, schema_editor): u'src.utils.gametime.GameTime')): script.delete() + class Migration(migrations.Migration): dependencies = [ diff --git a/evennia/scripts/models.py b/evennia/scripts/models.py index a23eacb7f..d47034996 100644 --- a/evennia/scripts/models.py +++ b/evennia/scripts/models.py @@ -71,7 +71,6 @@ class ScriptDB(TypedObject): """ - # # ScriptDB Database Model setup # @@ -87,7 +86,7 @@ class ScriptDB(TypedObject): db_obj = models.ForeignKey("objects.ObjectDB", null=True, blank=True, verbose_name='scripted object', help_text='the object to store this script on, if not a global script.') db_account = models.ForeignKey("accounts.AccountDB", null=True, blank=True, verbose_name="scripted account", - help_text='the account to store this script on (should not be set if db_obj is set)') + help_text='the account to store this script on (should not be set if db_obj is set)') # how often to run Script (secs). -1 means there is no timer db_interval = models.IntegerField('interval', default=-1, help_text='how often to repeat script, in seconds. -1 means off.') diff --git a/evennia/scripts/monitorhandler.py b/evennia/scripts/monitorhandler.py index 1f9df1a4c..6c1c26870 100644 --- a/evennia/scripts/monitorhandler.py +++ b/evennia/scripts/monitorhandler.py @@ -23,11 +23,13 @@ _SA = object.__setattr__ _GA = object.__getattribute__ _DA = object.__delattr__ + class MonitorHandler(object): """ This is a resource singleton that allows for registering callbacks for when a field or Attribute is updated (saved). """ + def __init__(self): """ Initialize the handler. @@ -149,7 +151,6 @@ class MonitorHandler(object): else: self.monitors[obj][fieldname][idstring] = (callback, persistent, kwargs) - def remove(self, obj, fieldname, idstring=""): """ Remove a monitor. diff --git a/evennia/scripts/scripthandler.py b/evennia/scripts/scripthandler.py index 358a9853e..33ce30273 100644 --- a/evennia/scripts/scripthandler.py +++ b/evennia/scripts/scripthandler.py @@ -13,11 +13,13 @@ from evennia.utils import logger from django.utils.translation import ugettext as _ + class ScriptHandler(object): """ Implements the handler. This sits on each game object. """ + def __init__(self, obj): """ Set up internal state. @@ -49,8 +51,8 @@ class ScriptHandler(object): except Exception: next_repeat = "?" string += _("\n '%(key)s' (%(next_repeat)s/%(interval)s, %(repeats)s repeats): %(desc)s") % \ - {"key": script.key, "next_repeat": next_repeat, - "interval": interval, "repeats": repeats, "desc": script.desc} + {"key": script.key, "next_repeat": next_repeat, + "interval": interval, "repeats": repeats, "desc": script.desc} return string.strip() def add(self, scriptclass, key=None, autostart=True): @@ -73,7 +75,7 @@ class ScriptHandler(object): else: # the normal - adding to an Object script = create.create_script(scriptclass, key=key, obj=self.obj, - autostart=autostart) + autostart=autostart) if not script: logger.log_err("Script %s could not be created and/or started." % scriptclass) return False diff --git a/evennia/scripts/scripts.py b/evennia/scripts/scripts.py index 4fdef4ef6..db6e9652c 100644 --- a/evennia/scripts/scripts.py +++ b/evennia/scripts/scripts.py @@ -201,8 +201,8 @@ class DefaultScript(ScriptBase): """ cname = self.__class__.__name__ estring = _("Script %(key)s(#%(dbid)s) of type '%(cname)s': at_repeat() error '%(err)s'.") % \ - {"key": self.key, "dbid": self.dbid, "cname": cname, - "err": e.getErrorMessage()} + {"key": self.key, "dbid": self.dbid, "cname": cname, + "err": e.getErrorMessage()} try: self.db_obj.msg(estring) except Exception: @@ -596,6 +596,7 @@ class DoNothing(DefaultScript): """ A script that does nothing. Used as default fallback. """ + def at_script_creation(self): """ Setup the script @@ -608,6 +609,7 @@ class Store(DefaultScript): """ Simple storage script """ + def at_script_creation(self): """ Setup the script diff --git a/evennia/scripts/taskhandler.py b/evennia/scripts/taskhandler.py index aadc3d974..1849134d3 100644 --- a/evennia/scripts/taskhandler.py +++ b/evennia/scripts/taskhandler.py @@ -11,6 +11,7 @@ from evennia.utils.dbserialize import dbserialize, dbunserialize TASK_HANDLER = None + class TaskHandler(object): """ @@ -71,9 +72,9 @@ class TaskHandler(object): try: dbserialize(callback) except (TypeError, AttributeError): - raise ValueError("the specified callback {} cannot be pickled. " \ - "It must be a top-level function in a module or an " \ - "instance method.".format(callback)) + raise ValueError("the specified callback {} cannot be pickled. " + "It must be a top-level function in a module or an " + "instance method.".format(callback)) else: safe_callback = callback @@ -112,9 +113,9 @@ class TaskHandler(object): try: dbserialize(arg) except (TypeError, AttributeError): - logger.log_err("The positional argument {} cannot be " \ - "pickled and will not be present in the arguments " \ - "fed to the callback {}".format(arg, callback)) + logger.log_err("The positional argument {} cannot be " + "pickled and will not be present in the arguments " + "fed to the callback {}".format(arg, callback)) else: safe_args.append(arg) @@ -122,9 +123,9 @@ class TaskHandler(object): try: dbserialize(value) except (TypeError, AttributeError): - logger.log_err("The {} keyword argument {} cannot be " \ - "pickled and will not be present in the arguments " \ - "fed to the callback {}".format(key, value, callback)) + logger.log_err("The {} keyword argument {} cannot be " + "pickled and will not be present in the arguments " + "fed to the callback {}".format(key, value, callback)) else: safe_kwargs[key] = value @@ -185,4 +186,3 @@ class TaskHandler(object): # Create the soft singleton TASK_HANDLER = TaskHandler() - diff --git a/evennia/scripts/tests.py b/evennia/scripts/tests.py index 5d92d63e2..dbd36ae95 100644 --- a/evennia/scripts/tests.py +++ b/evennia/scripts/tests.py @@ -7,6 +7,7 @@ from evennia.scripts.scripts import DoNothing class TestScriptDB(TestCase): "Check the singleton/static ScriptDB object works correctly" + def setUp(self): self.scr = create_script(DoNothing) diff --git a/evennia/scripts/tickerhandler.py b/evennia/scripts/tickerhandler.py index 57a276f5c..36455329a 100644 --- a/evennia/scripts/tickerhandler.py +++ b/evennia/scripts/tickerhandler.py @@ -81,10 +81,11 @@ _SA = object.__setattr__ _ERROR_ADD_TICKER = \ -"""TickerHandler: Tried to add an invalid ticker: + """TickerHandler: Tried to add an invalid ticker: {storekey} Ticker was not added.""" + class Ticker(object): """ Represents a repeatedly running task that calls @@ -145,7 +146,6 @@ class Ticker(object): self._to_remove = [] self._to_add = [] - def __init__(self, interval): """ Set up the ticker @@ -338,7 +338,7 @@ class TickerHandler(object): outpath = "%s.%s" % (callback.__module__, callback.func_name) outcallfunc = callback else: - raise TypeError("%s is not a callable function or method." % callback) + raise TypeError("%s is not a callable function or method." % callback) return outobj, outpath, outcallfunc def _store_key(self, obj, path, interval, callfunc, idstring="", persistent=True): @@ -386,12 +386,12 @@ class TickerHandler(object): if self.ticker_storage: # get the current times so the tickers can be restarted with a delay later start_delays = dict((interval, ticker.task.next_call_time()) - for interval, ticker in self.ticker_pool.tickers.items()) + for interval, ticker in self.ticker_pool.tickers.items()) # remove any subscriptions that lost its object in the interim to_save = {store_key: (args, kwargs) for store_key, (args, kwargs) in self.ticker_storage.items() - if ((store_key[1] and ("_obj" in kwargs and kwargs["_obj"].pk) and - hasattr(kwargs["_obj"], store_key[1])) or # a valid method with existing obj + if ((store_key[1] and ("_obj" in kwargs and kwargs["_obj"].pk) and + hasattr(kwargs["_obj"], store_key[1])) or # a valid method with existing obj store_key[2])} # a path given # update the timers for the tickers @@ -491,12 +491,12 @@ class TickerHandler(object): """ if isinstance(callback, int): raise RuntimeError("TICKER_HANDLER.add has changed: " - "the interval is now the first argument, callback the second.") + "the interval is now the first argument, callback the second.") obj, path, callfunc = self._get_callback(callback) store_key = self._store_key(obj, path, interval, callfunc, idstring, persistent) kwargs["_obj"] = obj - kwargs["_callback"] = callfunc # either method-name or callable + kwargs["_callback"] = callfunc # either method-name or callable self.ticker_storage[store_key] = (args, kwargs) self.ticker_pool.add(store_key, *args, **kwargs) self.save() @@ -515,7 +515,7 @@ class TickerHandler(object): """ if isinstance(callback, int): raise RuntimeError("TICKER_HANDLER.remove has changed: " - "the interval is now the first argument, callback the second.") + "the interval is now the first argument, callback the second.") obj, path, callfunc = self._get_callback(callback) store_key = self._store_key(obj, path, interval, callfunc, idstring, persistent) @@ -539,8 +539,8 @@ class TickerHandler(object): self.ticker_pool.stop(interval) if interval: self.ticker_storage = dict((store_key, store_key) - for store_key in self.ticker_storage - if store_key[1] != interval) + for store_key in self.ticker_storage + if store_key[1] != interval) else: self.ticker_storage = {} self.save() @@ -562,13 +562,13 @@ class TickerHandler(object): if interval is None: # return dict of all, ordered by interval return dict((interval, ticker.subscriptions) - for interval, ticker in self.ticker_pool.tickers.iteritems()) + for interval, ticker in self.ticker_pool.tickers.iteritems()) else: # get individual interval ticker = self.ticker_pool.tickers.get(interval, None) if ticker: return {interval: ticker.subscriptions} - return None + return None def all_display(self): """ @@ -584,5 +584,6 @@ class TickerHandler(object): store_keys.append((kwargs.get("_obj", None), callfunc, path, interval, idstring, persistent)) return store_keys + # main tickerhandler TICKER_HANDLER = TickerHandler() diff --git a/evennia/server/admin.py b/evennia/server/admin.py index 070fb3511..75f0a39df 100644 --- a/evennia/server/admin.py +++ b/evennia/server/admin.py @@ -19,4 +19,6 @@ class ServerConfigAdmin(admin.ModelAdmin): save_as = True save_on_top = True list_select_related = True + + admin.site.register(ServerConfig, ServerConfigAdmin) diff --git a/evennia/server/amp.py b/evennia/server/amp.py index c4a523674..9694abd03 100644 --- a/evennia/server/amp.py +++ b/evennia/server/amp.py @@ -319,8 +319,12 @@ class FunctionCall(amp.Command): # Helper functions for pickling. -dumps = lambda data: to_str(pickle.dumps(to_str(data), pickle.HIGHEST_PROTOCOL)) -loads = lambda data: pickle.loads(to_str(data)) +def dumps(data): + return to_str(pickle.dumps(to_str(data), pickle.HIGHEST_PROTOCOL)) + + +def loads(data): + return pickle.loads(to_str(data)) # ------------------------------------------------------------- diff --git a/evennia/server/deprecations.py b/evennia/server/deprecations.py index 7425ef7c5..6cb260f26 100644 --- a/evennia/server/deprecations.py +++ b/evennia/server/deprecations.py @@ -5,6 +5,7 @@ checks for. These all print to the terminal. """ + def check_errors(settings): """ Check for deprecations that are critical errors and should stop @@ -52,10 +53,10 @@ def check_errors(settings): raise DeprecationWarning(deprstring % "CHANNEL_TYPECLASS_PATHS") if hasattr(settings, "SEARCH_MULTIMATCH_SEPARATOR"): raise DeprecationWarning( - "settings.SEARCH_MULTIMATCH_SEPARATOR was replaced by " - "SEARCH_MULTIMATCH_REGEX and SEARCH_MULTIMATCH_TEMPLATE. " - "Update your settings file (see evennia/settings_default.py " - "for more info).") + "settings.SEARCH_MULTIMATCH_SEPARATOR was replaced by " + "SEARCH_MULTIMATCH_REGEX and SEARCH_MULTIMATCH_TEMPLATE. " + "Update your settings file (see evennia/settings_default.py " + "for more info).") gametime_deprecation = ("The settings TIME_SEC_PER_MIN, TIME_MIN_PER_HOUR," "TIME_HOUR_PER_DAY, TIME_DAY_PER_WEEK, \n" @@ -65,8 +66,8 @@ def check_errors(settings): "and manipulate these time units, the tools from utils.gametime " "are now found in contrib/convert_gametime.py instead.") if any(hasattr(settings, value) for value in ("TIME_SEC_PER_MIN", "TIME_MIN_PER_HOUR", - "TIME_HOUR_PER_DAY", "TIME_DAY_PER_WEEK", "TIME_WEEK_PER_MONTH", - "TIME_MONTH_PER_YEAR")): + "TIME_HOUR_PER_DAY", "TIME_DAY_PER_WEEK", "TIME_WEEK_PER_MONTH", + "TIME_MONTH_PER_YEAR")): raise DeprecationWarning(gametime_deprecation) diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index 11165265a..10bbc84ba 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -24,7 +24,7 @@ import django # Signal processing SIG = signal.SIGINT -CTRL_C_EVENT = 0 # Windows SIGINT-like signal +CTRL_C_EVENT = 0 # Windows SIGINT-like signal # Set up the main python paths to Evennia EVENNIA_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -101,7 +101,7 @@ CREATED_NEW_GAMEDIR = \ """ ERROR_INPUT = \ -""" + """ Command {args} {kwargs} raised an error: '{traceback}'. @@ -419,6 +419,7 @@ def evennia_version(): pass return version + EVENNIA_VERSION = evennia_version() @@ -434,7 +435,7 @@ def check_main_evennia_dependencies(): error = False # Python - pversion = ".".join(str(num) for num in sys.version_info if type(num) == int) + pversion = ".".join(str(num) for num in sys.version_info if isinstance(num, int)) if LooseVersion(pversion) < LooseVersion(PYTHON_MIN): print(ERROR_PYTHON_VERSION.format(pversion=pversion, python_min=PYTHON_MIN)) error = True @@ -451,7 +452,7 @@ def check_main_evennia_dependencies(): error = True # Django try: - dversion = ".".join(str(num) for num in django.VERSION if type(num) == int) + dversion = ".".join(str(num) for num in django.VERSION if isinstance(num, int)) # only the main version (1.5, not 1.5.4.0) dversion_main = ".".join(dversion.split(".")[:2]) if LooseVersion(dversion) < LooseVersion(DJANGO_MIN): @@ -502,8 +503,8 @@ def create_secret_key(): import random import string secret_key = list((string.letters + - string.digits + string.punctuation).replace("\\", "")\ - .replace("'", '"').replace("{","_").replace("}","-")) + string.digits + string.punctuation).replace("\\", "") + .replace("'", '"').replace("{", "_").replace("}", "-")) random.shuffle(secret_key) secret_key = "".join(secret_key[:40]) return secret_key @@ -600,7 +601,7 @@ def check_database(): # Check so a database exists and is accessible from django.db import connection tables = connection.introspection.get_table_list(connection.cursor()) - if not tables or not isinstance(tables[0], basestring): # django 1.8+ + if not tables or not isinstance(tables[0], basestring): # django 1.8+ tables = [tableinfo.name for tableinfo in tables] if tables and u'accounts_accountdb' in tables: # database exists and seems set up. Initialize evennia. @@ -741,8 +742,8 @@ def kill(pidfile, killsignal=SIG, succmsg="", errmsg="", os.kill(int(pid), killsignal) except OSError: - print("Process %(pid)s cannot be stopped. "\ - "The PID file 'server/%(pidfile)s' seems stale. "\ + print("Process %(pid)s cannot be stopped. " + "The PID file 'server/%(pidfile)s' seems stale. " "Try removing it." % {'pid': pid, 'pidfile': pidfile}) return print("Evennia:", succmsg) @@ -782,6 +783,7 @@ def error_check_python_modules(): """ from django.conf import settings + def _imp(path, split=True): "helper method" mod, fromlist = path, "None" @@ -821,6 +823,7 @@ def error_check_python_modules(): _imp(settings.BASE_EXIT_TYPECLASS) _imp(settings.BASE_SCRIPT_TYPECLASS) + def init_game_directory(path, check_db=True): """ Try to analyze the given path to find settings.py - this defines @@ -888,10 +891,10 @@ def init_game_directory(path, check_db=True): # verify existence of log file dir (this can be missing e.g. # if the game dir itself was cloned since log files are in .gitignore) logdirs = [logfile.rsplit(os.path.sep, 1) - for logfile in (SERVER_LOGFILE, PORTAL_LOGFILE, HTTP_LOGFILE)] + for logfile in (SERVER_LOGFILE, PORTAL_LOGFILE, HTTP_LOGFILE)] if not all(os.path.isdir(pathtup[0]) for pathtup in logdirs): errstr = "\n ".join("%s (log file %s)" % (pathtup[0], pathtup[1]) for pathtup in logdirs - if not os.path.isdir(pathtup[0])) + if not os.path.isdir(pathtup[0])) print(ERROR_LOGDIR_MISSING.format(logfiles=errstr)) sys.exit() @@ -985,7 +988,7 @@ def list_settings(keys): table = evtable.EvTable() confs = [key for key in sorted(evsettings.__dict__) if key.isupper()] for i in range(0, len(confs), 4): - table.add_row(*confs[i:i+4]) + table.add_row(*confs[i:i + 4]) else: # a specific key table = evtable.EvTable(width=131) @@ -1165,8 +1168,8 @@ def server_operation(mode, service, interactive, profiler, logserver=False, doex elif mode == 'stop': if os.name == "nt": print ( - "(Obs: You can use a single Ctrl-C to skip " - "Windows' annoying 'Terminate batch job (Y/N)?' prompts.)") + "(Obs: You can use a single Ctrl-C to skip " + "Windows' annoying 'Terminate batch job (Y/N)?' prompts.)") # stop processes, avoiding reload if service == 'server': kill(SERVER_PIDFILE, SIG, @@ -1237,7 +1240,7 @@ def main(): help=("Which component to operate on: " "'server', 'portal' or 'all' (default if not set).")) parser.epilog = ( - "Common usage: evennia start|stop|reload. Django-admin database commands:" + "Common usage: evennia start|stop|reload. Django-admin database commands:" "evennia migration|flush|shell|dbshell (see the django documentation for more django-admin commands.)") args, unknown_args = parser.parse_known_args() @@ -1275,7 +1278,6 @@ def main(): print("Using settings file '%s' (%s)." % ( SETTINGSFILE, SETTINGS_DOTPATH)) - if args.initsettings: # create new settings file global GAMEDIR diff --git a/evennia/server/evennia_runner.py b/evennia/server/evennia_runner.py index 7fcb271a3..83e7bf409 100644 --- a/evennia/server/evennia_runner.py +++ b/evennia/server/evennia_runner.py @@ -19,7 +19,8 @@ import os import sys from argparse import ArgumentParser from subprocess import Popen -import Queue, thread +import Queue +import thread import evennia try: @@ -35,7 +36,7 @@ EVENNIA_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) EVENNIA_BIN = os.path.join(EVENNIA_ROOT, "bin") EVENNIA_LIB = os.path.dirname(evennia.__file__) -SERVER_PY_FILE = os.path.join(EVENNIA_LIB,'server', 'server.py') +SERVER_PY_FILE = os.path.join(EVENNIA_LIB, 'server', 'server.py') PORTAL_PY_FILE = os.path.join(EVENNIA_LIB, 'server', 'portal', 'portal.py') GAMEDIR = None @@ -79,6 +80,7 @@ PROCESS_DOEXIT = "Deferring to external runner." # Functions + def set_restart_mode(restart_file, flag="reload"): """ This sets a flag file for the restart mode. @@ -203,14 +205,14 @@ def start_services(server_argv, portal_argv, doexit=False): # restart only if process stopped cleanly if (message == "server_stopped" and int(rc) == 0 and - get_restart_mode(SERVER_RESTART) in ("True", "reload", "reset")): + get_restart_mode(SERVER_RESTART) in ("True", "reload", "reset")): print(PROCESS_RESTART.format(component="Server")) SERVER = thread.start_new_thread(server_waiter, (processes, )) continue # normally the portal is not reloaded since it's run as a daemon. if (message == "portal_stopped" and int(rc) == 0 and - get_restart_mode(PORTAL_RESTART) == "True"): + get_restart_mode(PORTAL_RESTART) == "True"): print(PROCESS_RESTART.format(component="Portal")) PORTAL = thread.start_new_thread(portal_waiter, (processes, )) continue @@ -296,8 +298,8 @@ def main(): pid = get_pid(SERVER_PIDFILE) if pid and not args.noserver: - print("\nEvennia Server is already running as process %(pid)s. Not restarted." % {'pid': pid}) - args.noserver = True + print("\nEvennia Server is already running as process %(pid)s. Not restarted." % {'pid': pid}) + args.noserver = True if args.noserver: server_argv = None else: @@ -350,5 +352,6 @@ def main(): # Start processes start_services(server_argv, portal_argv, doexit=args.doexit) + if __name__ == '__main__': main() diff --git a/evennia/server/inputfuncs.py b/evennia/server/inputfuncs.py index 3881b9fbe..53d5a73bf 100644 --- a/evennia/server/inputfuncs.py +++ b/evennia/server/inputfuncs.py @@ -35,7 +35,11 @@ _IDLE_COMMAND = settings.IDLE_COMMAND _IDLE_COMMAND = (_IDLE_COMMAND, ) if _IDLE_COMMAND == "idle" else (_IDLE_COMMAND, "idle") _GA = object.__getattribute__ _SA = object.__setattr__ -_NA = lambda o: "N/A" + + +def _NA(o): + return "N/A" + _ERROR_INPUT = "Inputfunc {name}({session}): Wrong/unrecognized input: {inp}" @@ -58,8 +62,8 @@ def text(session, *args, **kwargs): txt = args[0] if args else None - #explicitly check for None since text can be an empty string, which is - #also valid + # explicitly check for None since text can be an empty string, which is + # also valid if txt is None: return # this is treated as a command input @@ -72,10 +76,10 @@ def text(session, *args, **kwargs): puppet = session.puppet if puppet: txt = puppet.nicks.nickreplace(txt, - categories=("inputline", "channel"), include_account=True) + categories=("inputline", "channel"), include_account=True) else: txt = session.account.nicks.nickreplace(txt, - categories=("inputline", "channel"), include_account=False) + categories=("inputline", "channel"), include_account=False) kwargs.pop("options", None) cmdhandler(session, txt, callertype="session", session=session, **kwargs) session.update_session_counters() @@ -123,11 +127,11 @@ def default(session, cmdname, *args, **kwargs): """ err = "Session {sessid}: Input command not recognized:\n" \ - " name: '{cmdname}'\n" \ - " args, kwargs: {args}, {kwargs}".format(sessid=session.sessid, - cmdname=cmdname, - args=args, - kwargs=kwargs) + " name: '{cmdname}'\n" \ + " args, kwargs: {args}, {kwargs}".format(sessid=session.sessid, + cmdname=cmdname, + args=args, + kwargs=kwargs) if session.protocol_flags.get("INPUTDEBUG", False): session.msg(err) log_err(err) @@ -153,15 +157,12 @@ def browser_sessid(session, *args, **kwargs): uid = browsersession.get("logged_in", None) if uid: try: - account = AccountDB.objects.get(pk=uid) + account = AccountDB.objects.get(pk=uid) except Exception: return session.sessionhandler.login(session, account) - - - def client_options(session, *args, **kwargs): """ This allows the client an OOB way to inform us about its name and capabilities. @@ -189,12 +190,12 @@ def client_options(session, *args, **kwargs): if not kwargs or kwargs.get("get", False): # return current settings options = dict((key, flags[key]) for key in flags - if key.upper() in ("ANSI", "XTERM256", "MXP", - "UTF-8", "SCREENREADER", "ENCODING", - "MCCP", "SCREENHEIGHT", - "SCREENWIDTH", "INPUTDEBUG", - "RAW", "NOCOLOR", - "NOGOAHEAD")) + if key.upper() in ("ANSI", "XTERM256", "MXP", + "UTF-8", "SCREENREADER", "ENCODING", + "MCCP", "SCREENHEIGHT", + "SCREENWIDTH", "INPUTDEBUG", + "RAW", "NOCOLOR", + "NOGOAHEAD")) session.msg(client_options=options) return @@ -248,17 +249,18 @@ def client_options(session, *args, **kwargs): elif key == "nogoahead": flags["NOGOAHEAD"] = validate_bool(value) elif key in ('Char 1', 'Char.Skills 1', 'Char.Items 1', - 'Room 1', 'IRE.Rift 1', 'IRE.Composer 1'): + 'Room 1', 'IRE.Rift 1', 'IRE.Composer 1'): # ignore mudlet's default send (aimed at IRE games) pass - elif not key in ("options", "cmdid"): + elif key not in ("options", "cmdid"): err = _ERROR_INPUT.format( - name="client_settings", session=session, inp=key) + name="client_settings", session=session, inp=key) session.msg(text=err) session.protocol_flags = flags # we must update the portal as well session.sessionhandler.session_portal_sync(session) + # GMCP alias hello = client_options supports_set = client_options @@ -298,6 +300,7 @@ def login(session, *args, **kwargs): if account: session.sessionhandler.login(session, account) + _gettable = { "name": lambda obj: obj.key, "key": lambda obj: obj.key, @@ -305,6 +308,7 @@ _gettable = { "servername": lambda obj: settings.SERVERNAME } + def get_value(session, *args, **kwargs): """ Return the value of a given attribute or db_property on the @@ -335,7 +339,7 @@ def _testrepeat(**kwargs): _repeatable = {"test1": _testrepeat, # example only - "test2": _testrepeat} # " + "test2": _testrepeat} # " def repeat(session, *args, **kwargs): @@ -368,7 +372,6 @@ def repeat(session, *args, **kwargs): session.msg("Allowed repeating functions are: %s" % (", ".join(_repeatable))) - def unrepeat(session, *args, **kwargs): "Wrapper for OOB use" kwargs["stop"] = True @@ -415,7 +418,7 @@ def monitor(session, *args, **kwargs): else: # the handler will add fieldname and obj to the kwargs automatically MONITOR_HANDLER.add(obj, field_name, _on_monitor_change, idstring=session.sessid, - persistent=False, name=name, session=session) + persistent=False, name=name, session=session) def unmonitor(session, *args, **kwargs): diff --git a/evennia/server/manager.py b/evennia/server/manager.py index c9907ab98..a70d9734c 100644 --- a/evennia/server/manager.py +++ b/evennia/server/manager.py @@ -16,6 +16,7 @@ class ServerConfigManager(models.Manager): the server at run-time. """ + def conf(self, key=None, value=None, delete=False, default=None): """ Add, retrieve and manipulate config values. diff --git a/evennia/server/portal/irc.py b/evennia/server/portal/irc.py index cd7f5e53d..8d7510368 100644 --- a/evennia/server/portal/irc.py +++ b/evennia/server/portal/irc.py @@ -121,7 +121,7 @@ IRC_COLOR_MAP = dict([ (r'|[c', IRC_COLOR + IRC_NORMAL + "," + IRC_DCYAN), (r'|[w', IRC_COLOR + IRC_NORMAL + "," + IRC_GRAY), # light grey background (r'|[x', IRC_COLOR + IRC_NORMAL + "," + IRC_BLACK) # pure black background - ]) +]) RE_IRC_COLOR = re.compile(r"|".join([re.escape(key) for key in viewkeys(IRC_COLOR_MAP)]), re.DOTALL) RE_MXP = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) RE_ANSI_ESCAPES = re.compile(r"(%s)" % "|".join(("{{", "%%", "\\\\")), re.DOTALL) diff --git a/evennia/server/portal/mssp.py b/evennia/server/portal/mssp.py index 5ff0a7b31..f80594c74 100644 --- a/evennia/server/portal/mssp.py +++ b/evennia/server/portal/mssp.py @@ -22,12 +22,14 @@ MSSP_VAL = chr(2) # try to get the customized mssp info, if it exists. MSSPTable_CUSTOM = utils.variable_from_module(settings.MSSP_META_MODULE, "MSSPTable", default={}) + class Mssp(object): """ Implements the MSSP protocol. Add this to a variable on the telnet protocol to set it up. """ + def __init__(self, protocol): """ initialize MSSP by storing protocol on ourselves and calling @@ -80,108 +82,108 @@ class Mssp(object): """ - self.mssp_table = { + self.mssp_table = { - # Required fields + # Required fields - "NAME": "Evennia", - "PLAYERS": self.get_player_count, - "UPTIME" : self.get_uptime, + "NAME": "Evennia", + "PLAYERS": self.get_player_count, + "UPTIME": self.get_uptime, - # Generic + # Generic - "CRAWL DELAY": "-1", + "CRAWL DELAY": "-1", - "HOSTNAME": "", # current or new hostname - "PORT": ["4000"], # most important port should be last in list - "CODEBASE": "Evennia", - "CONTACT": "", # email for contacting the mud - "CREATED": "", # year MUD was created - "ICON": "", # url to icon 32x32 or larger; <32kb. - "IP": "", # current or new IP address - "LANGUAGE": "", # name of language used, e.g. English - "LOCATION": "", # full English name of server country - "MINIMUM AGE": "0", # set to 0 if not applicable - "WEBSITE": "www.evennia.com", + "HOSTNAME": "", # current or new hostname + "PORT": ["4000"], # most important port should be last in list + "CODEBASE": "Evennia", + "CONTACT": "", # email for contacting the mud + "CREATED": "", # year MUD was created + "ICON": "", # url to icon 32x32 or larger; <32kb. + "IP": "", # current or new IP address + "LANGUAGE": "", # name of language used, e.g. English + "LOCATION": "", # full English name of server country + "MINIMUM AGE": "0", # set to 0 if not applicable + "WEBSITE": "www.evennia.com", - # Categorisation + # Categorisation - "FAMILY": "Custom", # evennia goes under 'Custom' - "GENRE": "None", # Adult, Fantasy, Historical, Horror, Modern, None, or Science Fiction - "GAMEPLAY": "None", # Adventure, Educational, Hack and Slash, None, - # Player versus Player, Player versus Environment, - # Roleplaying, Simulation, Social or Strategy - "STATUS": "Open Beta", # Alpha, Closed Beta, Open Beta, Live - "GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew - "SUBGENRE": "None", # LASG, Medieval Fantasy, World War II, Frankenstein, - # Cyberpunk, Dragonlance, etc. Or None if not available. + "FAMILY": "Custom", # evennia goes under 'Custom' + "GENRE": "None", # Adult, Fantasy, Historical, Horror, Modern, None, or Science Fiction + "GAMEPLAY": "None", # Adventure, Educational, Hack and Slash, None, + # Player versus Player, Player versus Environment, + # Roleplaying, Simulation, Social or Strategy + "STATUS": "Open Beta", # Alpha, Closed Beta, Open Beta, Live + "GAMESYSTEM": "Custom", # D&D, d20 System, World of Darkness, etc. Use Custom if homebrew + "SUBGENRE": "None", # LASG, Medieval Fantasy, World War II, Frankenstein, + # Cyberpunk, Dragonlance, etc. Or None if not available. - # World + # World - "AREAS": "0", - "HELPFILES": "0", - "MOBILES": "0", - "OBJECTS": "0", - "ROOMS": "0", # use 0 if room-less - "CLASSES": "0", # use 0 if class-less - "LEVELS": "0", # use 0 if level-less - "RACES": "0", # use 0 if race-less - "SKILLS": "0", # use 0 if skill-less + "AREAS": "0", + "HELPFILES": "0", + "MOBILES": "0", + "OBJECTS": "0", + "ROOMS": "0", # use 0 if room-less + "CLASSES": "0", # use 0 if class-less + "LEVELS": "0", # use 0 if level-less + "RACES": "0", # use 0 if race-less + "SKILLS": "0", # use 0 if skill-less - # Protocols set to 1 or 0) + # Protocols set to 1 or 0) - "ANSI": "1", - "GMCP": "0", - "ATCP": "0", - "MCCP": "0", - "MCP": "0", - "MSDP": "0", - "MSP": "0", - "MXP": "0", - "PUEBLO": "0", - "SSL": "1", - "UTF-8": "1", - "ZMP": "0", - "VT100": "0", - "XTERM 256 COLORS": "0", + "ANSI": "1", + "GMCP": "0", + "ATCP": "0", + "MCCP": "0", + "MCP": "0", + "MSDP": "0", + "MSP": "0", + "MXP": "0", + "PUEBLO": "0", + "SSL": "1", + "UTF-8": "1", + "ZMP": "0", + "VT100": "0", + "XTERM 256 COLORS": "0", - # Commercial set to 1 or 0) + # Commercial set to 1 or 0) - "PAY TO PLAY": "0", - "PAY FOR PERKS": "0", + "PAY TO PLAY": "0", + "PAY FOR PERKS": "0", - # Hiring set to 1 or 0) + # Hiring set to 1 or 0) - "HIRING BUILDERS": "0", - "HIRING CODERS": "0", + "HIRING BUILDERS": "0", + "HIRING CODERS": "0", - # Extended variables + # Extended variables - # World + # World - "DBSIZE": "0", - "EXITS": "0", - "EXTRA DESCRIPTIONS": "0", - "MUDPROGS": "0", - "MUDTRIGS": "0", - "RESETS": "0", + "DBSIZE": "0", + "EXITS": "0", + "EXTRA DESCRIPTIONS": "0", + "MUDPROGS": "0", + "MUDTRIGS": "0", + "RESETS": "0", - # Game (set to 1, 0 or one of the given alternatives) + # Game (set to 1, 0 or one of the given alternatives) - "ADULT MATERIAL": "0", - "MULTICLASSING": "0", - "NEWBIE FRIENDLY": "0", - "PLAYER CITIES": "0", - "PLAYER CLANS": "0", - "PLAYER CRAFTING": "0", - "PLAYER GUILDS": "0", - "EQUIPMENT SYSTEM": "None", # "None", "Level", "Skill", "Both" - "MULTIPLAYING": "None", # "None", "Restricted", "Full" - "PLAYERKILLING": "None", # "None", "Restricted", "Full" - "QUEST SYSTEM": "None", # "None", "Immortal Run", "Automated", "Integrated" - "ROLEPLAYING": "None", # "None", "Accepted", "Encouraged", "Enforced" - "TRAINING SYSTEM": "None", # "None", "Level", "Skill", "Both" - "WORLD ORIGINALITY": "None", # "All Stock", "Mostly Stock", "Mostly Original", "All Original" + "ADULT MATERIAL": "0", + "MULTICLASSING": "0", + "NEWBIE FRIENDLY": "0", + "PLAYER CITIES": "0", + "PLAYER CLANS": "0", + "PLAYER CRAFTING": "0", + "PLAYER GUILDS": "0", + "EQUIPMENT SYSTEM": "None", # "None", "Level", "Skill", "Both" + "MULTIPLAYING": "None", # "None", "Restricted", "Full" + "PLAYERKILLING": "None", # "None", "Restricted", "Full" + "QUEST SYSTEM": "None", # "None", "Immortal Run", "Automated", "Integrated" + "ROLEPLAYING": "None", # "None", "Accepted", "Encouraged", "Enforced" + "TRAINING SYSTEM": "None", # "None", "Level", "Skill", "Both" + "WORLD ORIGINALITY": "None", # "All Stock", "Mostly Stock", "Mostly Original", "All Original" } # update the static table with the custom one diff --git a/evennia/server/portal/mxp.py b/evennia/server/portal/mxp.py index 996c9f3bc..44bc9628e 100644 --- a/evennia/server/portal/mxp.py +++ b/evennia/server/portal/mxp.py @@ -21,10 +21,11 @@ LINKS_SUB = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) MXP = chr(91) MXP_TEMPSECURE = "\x1B[4z" MXP_SEND = MXP_TEMPSECURE + \ - "" + \ - "\\2" + \ - MXP_TEMPSECURE + \ - "" + "" + \ + "\\2" + \ + MXP_TEMPSECURE + \ + "" + def mxp_parse(text): """ @@ -44,6 +45,7 @@ def mxp_parse(text): text = LINKS_SUB.sub(MXP_SEND, text) return text + class Mxp(object): """ Implements the MXP protocol. diff --git a/evennia/server/portal/naws.py b/evennia/server/portal/naws.py index 587631059..13f08c7f9 100644 --- a/evennia/server/portal/naws.py +++ b/evennia/server/portal/naws.py @@ -20,12 +20,14 @@ DEFAULT_HEIGHT = settings.CLIENT_DEFAULT_HEIGHT # try to get the customized mssp info, if it exists. + class Naws(object): """ Implements the NAWS protocol. Add this to a variable on the telnet protocol to set it up. """ + def __init__(self, protocol): """ initialize NAWS by storing protocol on ourselves and calling @@ -37,8 +39,8 @@ class Naws(object): """ self.naws_step = 0 self.protocol = protocol - self.protocol.protocol_flags['SCREENWIDTH'] = {0: DEFAULT_WIDTH} # windowID (0 is root):width - self.protocol.protocol_flags['SCREENHEIGHT'] = {0: DEFAULT_HEIGHT} # windowID:width + self.protocol.protocol_flags['SCREENWIDTH'] = {0: DEFAULT_WIDTH} # windowID (0 is root):width + self.protocol.protocol_flags['SCREENHEIGHT'] = {0: DEFAULT_HEIGHT} # windowID:width self.protocol.negotiationMap[NAWS] = self.negotiate_sizes self.protocol.do(NAWS).addCallbacks(self.do_naws, self.no_naws) @@ -72,9 +74,8 @@ class Naws(object): """ if len(options) == 4: - # NAWS is negotiated with 16bit words - width = options[0] + options[1] - self.protocol.protocol_flags['SCREENWIDTH'][0] = int(width.encode('hex'), 16) - height = options[2] + options[3] - self.protocol.protocol_flags['SCREENHEIGHT'][0] = int(height.encode('hex'), 16) - + # NAWS is negotiated with 16bit words + width = options[0] + options[1] + self.protocol.protocol_flags['SCREENWIDTH'][0] = int(width.encode('hex'), 16) + height = options[2] + options[3] + self.protocol.protocol_flags['SCREENHEIGHT'][0] = int(height.encode('hex'), 16) diff --git a/evennia/server/portal/portal.py b/evennia/server/portal/portal.py index 060796cd7..f8db12b9e 100644 --- a/evennia/server/portal/portal.py +++ b/evennia/server/portal/portal.py @@ -93,6 +93,7 @@ def _portal_maintenance(): session.disconnect(reason=reason) PORTAL_SESSIONS.disconnect(session) + if _IDLE_TIMEOUT > 0: # only start the maintenance task if we care about idling. _maintenance_task = LoopingCall(_portal_maintenance) @@ -188,6 +189,7 @@ class Portal(object): # # ------------------------------------------------------------- + # twistd requires us to define the variable 'application' so it knows # what to execute from. application = service.Application('Portal') diff --git a/evennia/server/portal/portalsessionhandler.py b/evennia/server/portal/portalsessionhandler.py index 6136283cc..8c24cb655 100644 --- a/evennia/server/portal/portalsessionhandler.py +++ b/evennia/server/portal/portalsessionhandler.py @@ -9,7 +9,7 @@ from collections import deque, namedtuple from twisted.internet import reactor from django.conf import settings from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, \ - PCONNSYNC, PDISCONNALL + PCONNSYNC, PDISCONNALL from evennia.utils.logger import log_trace # module import @@ -97,7 +97,7 @@ class PortalSessionHandler(SessionHandler): if len(_CONNECTION_QUEUE) > 1: session.data_out(text=[["%s DoS protection is active. You are queued to connect in %g seconds ..." % ( settings.SERVERNAME, - len(_CONNECTION_QUEUE)*_MIN_TIME_BETWEEN_CONNECTS)], {}]) + len(_CONNECTION_QUEUE) * _MIN_TIME_BETWEEN_CONNECTS)], {}]) now = time.time() if (now - self.connection_last < _MIN_TIME_BETWEEN_CONNECTS) or not self.portal.amp_protocol: if not session or not self.connection_task: @@ -428,4 +428,5 @@ class PortalSessionHandler(SessionHandler): except Exception: log_trace() + PORTAL_SESSIONS = PortalSessionHandler() diff --git a/evennia/server/portal/rss.py b/evennia/server/portal/rss.py index c906028f9..b29771940 100644 --- a/evennia/server/portal/rss.py +++ b/evennia/server/portal/rss.py @@ -21,11 +21,13 @@ if RSS_ENABLED: except ImportError: raise ImportError("RSS requires python-feedparser to be installed. Install or set RSS_ENABLED=False.") + class RSSReader(Session): """ A simple RSS reader using the feedparser module. """ + def __init__(self, factory, url, rate): """ Initialize the reader. @@ -112,6 +114,7 @@ class RSSReader(Session): """ return threads.deferToThread(self.get_new).addCallback(self._callback, init).addErrback(self._errback) + class RSSBotFactory(object): """ Initializes new bots. diff --git a/evennia/server/portal/ssh.py b/evennia/server/portal/ssh.py index fd36807dd..7f82faf12 100644 --- a/evennia/server/portal/ssh.py +++ b/evennia/server/portal/ssh.py @@ -66,6 +66,7 @@ class SshProtocol(Manhole, session.Session): here. """ + def __init__(self, starttuple): """ For setting up the account. If account is not None then we'll @@ -310,7 +311,7 @@ class ExtraInfoAuthServer(SSHUserAuthServer): c = credentials.UsernamePassword(self.user, password) c.transport = self.transport return self.portal.login(c, None, IConchUser).addErrback( - self._ebPassword) + self._ebPassword) class AccountDBPasswordChecker(object): @@ -375,6 +376,7 @@ class TerminalSessionTransport_getPeer(object): provide getPeer to the transport. This one does. """ + def __init__(self, proto, chainedProtocol, avatar, width, height): self.proto = proto self.avatar = avatar diff --git a/evennia/server/portal/ssl.py b/evennia/server/portal/ssl.py index a3d78fbef..209e08696 100644 --- a/evennia/server/portal/ssl.py +++ b/evennia/server/portal/ssl.py @@ -50,6 +50,7 @@ class SSLProtocol(TelnetProtocol): Communication is the same as telnet, except data transfer is done with encryption. """ + def __init__(self, *args, **kwargs): super(SSLProtocol, self).__init__(*args, **kwargs) self.protocol_name = "ssl" diff --git a/evennia/server/portal/suppress_ga.py b/evennia/server/portal/suppress_ga.py index 6326a424f..c13fea62b 100644 --- a/evennia/server/portal/suppress_ga.py +++ b/evennia/server/portal/suppress_ga.py @@ -20,12 +20,14 @@ SUPPRESS_GA = chr(3) # try to get the customized mssp info, if it exists. + class SuppressGA(object): """ Implements the SUPRESS-GO-AHEAD protocol. Add this to a variable on the telnet protocol to set it up. """ + def __init__(self, protocol): """ Initialize suppression of GO-AHEADs. diff --git a/evennia/server/portal/telnet.py b/evennia/server/portal/telnet.py index d79a6da94..39c236e9e 100644 --- a/evennia/server/portal/telnet.py +++ b/evennia/server/portal/telnet.py @@ -32,6 +32,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): clients) gets a telnet protocol instance assigned to them. All communication between game and player goes through here. """ + def __init__(self, *args, **kwargs): self.protocol_name = "telnet" super(TelnetProtocol, self).__init__(*args, **kwargs) @@ -146,7 +147,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): """ return (option == MCCP or option == ECHO or - option == suppress_ga.SUPPRESS_GA) + option == suppress_ga.SUPPRESS_GA) def disableLocal(self, option): """ @@ -332,11 +333,11 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): # by telling the client that WE WON'T echo, the client knows # that IT should echo. This is the expected behavior from # our perspective. - self.transport.write(mccp_compress(self, IAC+WONT+ECHO)) + self.transport.write(mccp_compress(self, IAC + WONT + ECHO)) else: # by telling the client that WE WILL echo, the client can # safely turn OFF its OWN echo. - self.transport.write(mccp_compress(self, IAC+WILL+ECHO)) + self.transport.write(mccp_compress(self, IAC + WILL + ECHO)) if raw: # no processing self.sendLine(text) diff --git a/evennia/server/portal/telnet_oob.py b/evennia/server/portal/telnet_oob.py index c1c2e6fc8..50c8df771 100644 --- a/evennia/server/portal/telnet_oob.py +++ b/evennia/server/portal/telnet_oob.py @@ -175,7 +175,7 @@ class TelnetOOB(object): """ msdp_cmdname = "{msdp_var}{msdp_cmdname}{msdp_val}".format( - msdp_var=MSDP_VAR, msdp_cmdname=cmdname, msdp_val=MSDP_VAL) + msdp_var=MSDP_VAR, msdp_cmdname=cmdname, msdp_val=MSDP_VAL) if not (args or kwargs): return msdp_cmdname @@ -191,11 +191,11 @@ class TelnetOOB(object): msdp_args += "{msdp_array_open}" \ "{msdp_args}" \ "{msdp_array_close}".format( - msdp_array_open=MSDP_ARRAY_OPEN, - msdp_array_close=MSDP_ARRAY_CLOSE, - msdp_args="".join("%s%s" - % (MSDP_VAL, json.dumps(val)) - for val in args)) + msdp_array_open=MSDP_ARRAY_OPEN, + msdp_array_close=MSDP_ARRAY_CLOSE, + msdp_args="".join("%s%s" + % (MSDP_VAL, json.dumps(val)) + for val in args)) msdp_kwargs = "" if kwargs: @@ -203,12 +203,12 @@ class TelnetOOB(object): msdp_kwargs += "{msdp_table_open}" \ "{msdp_kwargs}" \ "{msdp_table_close}".format( - msdp_table_open=MSDP_TABLE_OPEN, - msdp_table_close=MSDP_TABLE_CLOSE, - msdp_kwargs="".join("%s%s%s%s" - % (MSDP_VAR, key, MSDP_VAL, - json.dumps(val)) - for key, val in kwargs.iteritems())) + msdp_table_open=MSDP_TABLE_OPEN, + msdp_table_close=MSDP_TABLE_CLOSE, + msdp_kwargs="".join("%s%s%s%s" + % (MSDP_VAR, key, MSDP_VAL, + json.dumps(val)) + for key, val in kwargs.iteritems())) msdp_string = msdp_args + msdp_kwargs diff --git a/evennia/server/portal/ttype.py b/evennia/server/portal/ttype.py index 5e9ea3997..d2193bd27 100644 --- a/evennia/server/portal/ttype.py +++ b/evennia/server/portal/ttype.py @@ -34,6 +34,7 @@ class Ttype(object): telnet protocol. """ + def __init__(self, protocol): """ Initialize ttype by storing protocol on ourselves and calling @@ -132,8 +133,8 @@ class Ttype(object): term = option tupper = term.upper() # identify xterm256 based on flag - xterm256 = (tupper.endswith("-256COLOR") # Apple Terminal, old Tintin - or tupper.endswith("XTERM") and # old Tintin, Putty + xterm256 = (tupper.endswith("-256COLOR") # Apple Terminal, old Tintin or + tupper.endswith("XTERM") and # old Tintin, Putty not tupper.endswith("-COLOR")) if xterm256: self.protocol.protocol_flags['ANSI'] = True diff --git a/evennia/server/portal/webclient.py b/evennia/server/portal/webclient.py index 8c4266f13..df8db95c5 100644 --- a/evennia/server/portal/webclient.py +++ b/evennia/server/portal/webclient.py @@ -104,7 +104,7 @@ class WebSocketClient(Protocol, Session): """ cmdarray = json.loads(string) if cmdarray: - self.data_in(**{cmdarray[0]:[cmdarray[1], cmdarray[2]]}) + self.data_in(**{cmdarray[0]: [cmdarray[1], cmdarray[2]]}) def sendLine(self, line): """ @@ -199,7 +199,6 @@ class WebSocketClient(Protocol, Session): # send to client on required form [cmdname, args, kwargs] self.sendLine(json.dumps([cmd, args, kwargs])) - def send_prompt(self, *args, **kwargs): kwargs["options"].update({"send_prompt": True}) self.send_text(*args, **kwargs) diff --git a/evennia/server/portal/webclient_ajax.py b/evennia/server/portal/webclient_ajax.py index ef494be14..993e0a011 100644 --- a/evennia/server/portal/webclient_ajax.py +++ b/evennia/server/portal/webclient_ajax.py @@ -40,6 +40,7 @@ _KEEPALIVE = 30 # how often to check keepalive # extend this if one wants to send more # complex database objects too. + class LazyEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, Promise): @@ -86,7 +87,7 @@ class WebClient(resource.Resource): now = time.time() to_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) for csessid, remove in keep_alives: if remove: # keepalive timeout. Line is dead. @@ -203,7 +204,7 @@ class WebClient(resource.Resource): if sess: sess = sess[0] cmdarray = json.loads(request.args.get('data')[0]) - sess.sessionhandler.data_in(sess, **{cmdarray[0]:[cmdarray[1], cmdarray[2]]}) + sess.sessionhandler.data_in(sess, **{cmdarray[0]: [cmdarray[1], cmdarray[2]]}) return '""' def mode_receive(self, request): @@ -381,5 +382,5 @@ class WebClientSession(session.Session): """ if not cmdname == "options": - #print "ajax.send_default", cmdname, args, kwargs + # print "ajax.send_default", cmdname, args, kwargs self.client.lineSend(self.csessid, [cmdname, args, kwargs]) diff --git a/evennia/server/profiling/dummyrunner.py b/evennia/server/profiling/dummyrunner.py index 24093cb85..34b6acafb 100644 --- a/evennia/server/profiling/dummyrunner.py +++ b/evennia/server/profiling/dummyrunner.py @@ -50,7 +50,7 @@ from evennia.utils import mod_import, time_format DUMMYRUNNER_SETTINGS = mod_import(settings.DUMMYRUNNER_SETTINGS_MODULE) if not DUMMYRUNNER_SETTINGS: raise IOError("Error: Dummyrunner could not find settings file at %s" % - settings.DUMMYRUNNER_SETTINGS_MODULE) + settings.DUMMYRUNNER_SETTINGS_MODULE) DATESTRING = "%Y%m%d%H%M%S" @@ -177,6 +177,8 @@ until you see the initial login slows things too much. ICOUNT = 0 + + def idcounter(): """ Makes unique ids. @@ -191,6 +193,8 @@ def idcounter(): GCOUNT = 0 + + def gidcounter(): """ Makes globally unique ids. @@ -220,6 +224,7 @@ def makeiter(obj): # Client classes #------------------------------------------------------------ + class DummyClient(telnet.StatefulTelnetProtocol): """ Handles connection to a running Evennia server, @@ -238,14 +243,14 @@ class DummyClient(telnet.StatefulTelnetProtocol): self.key = "Dummy-%s" % self.cid self.gid = "%s-%s" % (time.strftime(DATESTRING), self.cid) self.istep = 0 - self.exits = [] # exit names created - self.objs = [] # obj names created + self.exits = [] # exit names created + self.objs = [] # obj names created self._connected = False self._loggedin = False self._logging_out = False self._report = "" - self._cmdlist = [] # already stepping in a cmd definition + self._cmdlist = [] # already stepping in a cmd definition self._login = self.factory.actions[0] self._logout = self.factory.actions[1] self._actions = self.factory.actions[2:] @@ -268,7 +273,7 @@ class DummyClient(telnet.StatefulTelnetProtocol): # start client tick d = LoopingCall(self.step) # dissipate exact step by up to +/- 0.5 second - timestep = TIMESTEP + (-0.5 + (random.random()*1.0)) + timestep = TIMESTEP + (-0.5 + (random.random() * 1.0)) d.start(timestep, now=True).addErrback(self.error) def connectionLost(self, reason): @@ -329,7 +334,7 @@ class DummyClient(telnet.StatefulTelnetProtocol): if rand < CHANCE_OF_LOGIN: # get the login commands self._cmdlist = list(makeiter(self._login(self))) - NLOGGED_IN += 1 # this is for book-keeping + NLOGGED_IN += 1 # this is for book-keeping print("connecting client %s (%i/%i)..." % (self.key, NLOGGED_IN, NCLIENTS)) self._loggedin = True else: @@ -350,6 +355,7 @@ class DummyClient(telnet.StatefulTelnetProtocol): class DummyFactory(protocol.ClientFactory): protocol = DummyClient + def __init__(self, actions): "Setup the factory base (shared by all clients)" self.actions = actions @@ -359,6 +365,7 @@ class DummyFactory(protocol.ClientFactory): # Starts clients and connects them to a running server. #------------------------------------------------------------ + def start_all_dummy_clients(nclients): """ Initialize all clients, connect them and start to step them @@ -379,7 +386,7 @@ def start_all_dummy_clients(nclients): pratio = 1.0 / sum(tup[0] for tup in actions[2:]) flogin, flogout, probs, cfuncs = actions[0], actions[1], [tup[0] * pratio for tup in actions[2:]], [tup[1] for tup in actions[2:]] # create cumulative probabilies for the random actions - cprobs = [sum(v for i,v in enumerate(probs) if i<=k) for k in range(len(probs))] + cprobs = [sum(v for i, v in enumerate(probs) if i <= k) for k in range(len(probs))] # rebuild a new, optimized action structure actions = (flogin, flogout) + tuple(zip(cprobs, cfuncs)) @@ -394,6 +401,7 @@ def start_all_dummy_clients(nclients): # Command line interface #------------------------------------------------------------ + if __name__ == '__main__': try: diff --git a/evennia/server/profiling/dummyrunner_settings.py b/evennia/server/profiling/dummyrunner_settings.py index e172935a2..da729d802 100644 --- a/evennia/server/profiling/dummyrunner_settings.py +++ b/evennia/server/profiling/dummyrunner_settings.py @@ -103,13 +103,14 @@ def c_login(client): client.exits.extend([exitname1, exitname2]) cmds = ('create %s %s' % (cname, cpwd), - 'connect %s %s' % (cname, cpwd), - '@dig %s' % START_ROOM % client.gid, - '@teleport %s' % START_ROOM % client.gid, - '@dig %s = %s, %s' % (roomname, exitname1, exitname2) - ) + 'connect %s %s' % (cname, cpwd), + '@dig %s' % START_ROOM % client.gid, + '@teleport %s' % START_ROOM % client.gid, + '@dig %s = %s, %s' % (roomname, exitname1, exitname2) + ) return cmds + def c_login_nodig(client): "logins, don't dig its own room" cname = DUMMY_NAME % client.gid @@ -119,12 +120,14 @@ def c_login_nodig(client): 'connect %s %s' % (cname, cpwd)) return cmds + def c_logout(client): "logouts of the game" return "@quit" # random commands + def c_looks(client): "looks at various objects" cmds = ["look %s" % obj for obj in client.objs] @@ -134,6 +137,7 @@ def c_looks(client): cmds = "look" return cmds + def c_examines(client): "examines various objects" cmds = ["examine %s" % obj for obj in client.objs] @@ -143,20 +147,23 @@ def c_examines(client): cmds = "examine me" return cmds + def c_idles(client): "idles" - cmds = ('idle','idle') + cmds = ('idle', 'idle') return cmds + def c_help(client): "reads help files" cmds = ('help', - 'help @teleport', - 'help look', - 'help @tunnel', - 'help @dig') + 'help @teleport', + 'help look', + 'help @tunnel', + 'help @dig') return cmds + def c_digs(client): "digs a new room, storing exit names on client" roomname = ROOM_TEMPLATE % client.counter() @@ -165,42 +172,48 @@ def c_digs(client): client.exits.extend([exitname1, exitname2]) return '@dig/tel %s = %s, %s' % (roomname, exitname1, exitname2) + def c_creates_obj(client): "creates normal objects, storing their name on client" objname = OBJ_TEMPLATE % client.counter() client.objs.append(objname) cmds = ('@create %s' % objname, - '@desc %s = "this is a test object' % objname, - '@set %s/testattr = this is a test attribute value.' % objname, - '@set %s/testattr2 = this is a second test attribute.' % objname) + '@desc %s = "this is a test object' % objname, + '@set %s/testattr = this is a test attribute value.' % objname, + '@set %s/testattr2 = this is a second test attribute.' % objname) return cmds + def c_creates_button(client): "creates example button, storing name on client" objname = TOBJ_TEMPLATE % client.counter() client.objs.append(objname) cmds = ('@create %s:%s' % (objname, TOBJ_TYPECLASS), - '@desc %s = test red button!' % objname) + '@desc %s = test red button!' % objname) return cmds + def c_socialize(client): "socializechats on channel" cmds = ('ooc Hello!', - 'ooc Testing ...', - 'ooc Testing ... times 2', - 'say Yo!', - 'emote stands looking around.') + 'ooc Testing ...', + 'ooc Testing ... times 2', + 'say Yo!', + 'emote stands looking around.') return cmds + def c_moves(client): "moves to a previously created room, using the stored exits" - cmds = client.exits # try all exits - finally one will work + cmds = client.exits # try all exits - finally one will work return "look" if not cmds else cmds + def c_moves_n(client): "move through north exit if available" return "north" + def c_moves_s(client): "move through south exit if available" return "south" @@ -215,8 +228,9 @@ def c_moves_s(client): # otherwise the system will normalize them. # -## "normal builder" definitionj -#ACTIONS = ( c_login, + +# "normal builder" definitionj +# ACTIONS = ( c_login, # c_logout, # (0.5, c_looks), # (0.08, c_examines), @@ -224,8 +238,8 @@ def c_moves_s(client): # (0.01, c_digs), # (0.01, c_creates_obj), # (0.3, c_moves)) -## "heavy" builder definition -#ACTIONS = ( c_login, +# "heavy" builder definition +# ACTIONS = ( c_login, # c_logout, # (0.2, c_looks), # (0.1, c_examines), @@ -234,8 +248,8 @@ def c_moves_s(client): # (0.1, c_creates_obj), # #(0.01, c_creates_button), # (0.2, c_moves)) -## "passive account" definition -#ACTIONS = ( c_login, +# "passive account" definition +# ACTIONS = ( c_login, # c_logout, # (0.7, c_looks), # #(0.1, c_examines), @@ -245,24 +259,24 @@ def c_moves_s(client): # #(0.1, c_creates_button), # #(0.4, c_moves)) # "inactive account" definition -#ACTIONS = (c_login_nodig, +# ACTIONS = (c_login_nodig, # c_logout, # (1.0, c_idles)) -## "normal account" definition -ACTIONS = ( c_login, - c_logout, - (0.01, c_digs), - (0.39, c_looks), - (0.2, c_help), - (0.4, c_moves)) +# "normal account" definition +ACTIONS = (c_login, + c_logout, + (0.01, c_digs), + (0.39, c_looks), + (0.2, c_help), + (0.4, c_moves)) # walking tester. This requires a pre-made # "loop" of multiple rooms that ties back # to limbo (using @tunnel and @open) -#ACTIONS = (c_login_nodig, +# ACTIONS = (c_login_nodig, # c_logout, # (1.0, c_moves_n)) -## "socializing heavy builder" definition -#ACTIONS = (c_login, +# "socializing heavy builder" definition +# ACTIONS = (c_login, # c_logout, # (0.1, c_socialize), # (0.1, c_looks), @@ -270,7 +284,7 @@ ACTIONS = ( c_login, # (0.1, c_creates_obj), # (0.2, c_digs), # (0.3, c_moves)) -## "heavy digger memory tester" definition -#ACTIONS = (c_login, +# "heavy digger memory tester" definition +# ACTIONS = (c_login, # c_logout, # (1.0, c_digs)) diff --git a/evennia/server/profiling/memplot.py b/evennia/server/profiling/memplot.py index abc08fa9a..e8d7e76b1 100644 --- a/evennia/server/profiling/memplot.py +++ b/evennia/server/profiling/memplot.py @@ -7,22 +7,25 @@ the script will append to this file if it already exists. Call this module directly to plot the log (requires matplotlib and numpy). """ from __future__ import division -import os, sys +import os +import sys import time -#TODO! +# TODO! #sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) #os.environ['DJANGO_SETTINGS_MODULE'] = 'game.settings' import ev from evennia.utils.idmapper import base as _idmapper LOGFILE = "logs/memoryusage.log" -INTERVAL = 30 # log every 30 seconds +INTERVAL = 30 # log every 30 seconds + class Memplot(ev.Script): """ Describes a memory plotting action. """ + def at_script_creation(self): "Called at script creation" self.key = "memplot" @@ -38,11 +41,12 @@ class Memplot(ev.Script): rmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "rss")).read()) / 1000.0 # resident memory vmem = float(os.popen('ps -p %d -o %s | tail -1' % (pid, "vsz")).read()) / 1000.0 # virtual memory total_num, cachedict = _idmapper.cache_size() - t0 = (time.time() - self.db.starttime) / 60.0 # save in minutes + t0 = (time.time() - self.db.starttime) / 60.0 # save in minutes with open(LOGFILE, "a") as f: f.write("%s, %s, %s, %s\n" % (t0, rmem, vmem, int(total_num))) + if __name__ == "__main__": # plot output from the file @@ -51,10 +55,10 @@ if __name__ == "__main__": import numpy data = numpy.genfromtxt("../../../game/" + LOGFILE, delimiter=",") - secs = data[:,0] - rmem = data[:,1] - vmem = data[:,2] - nobj = data[:,3] + secs = data[:, 0] + rmem = data[:, 1] + vmem = data[:, 2] + nobj = data[:, 3] # calculate derivative of obj creation #oderiv = (0.5*(nobj[2:] - nobj[:-2]) / (secs[2:] - secs[:-2])).copy() @@ -76,7 +80,7 @@ if __name__ == "__main__": ax2.set_ylabel("Number of objects") ax2.legend(loc="lower right") ax2.annotate("First 500 bots\nconnecting", xy=(10, 4000)) - ax2.annotate("Next 500 bots\nconnecting", xy=(350,10000)) + ax2.annotate("Next 500 bots\nconnecting", xy=(350, 10000)) #ax2.annotate("@reload", xy=(185,600)) # # plot mem vs cachesize @@ -91,8 +95,8 @@ if __name__ == "__main__": # ax1.plot(nobj, vmem, "b", label="VMEM", lw=2) # -## # empirical estimate of memory usage: rmem = 35.0 + 0.0157 * Ncache -## # Ncache = int((rmem - 35.0) / 0.0157) (rmem in MB) +# empirical estimate of memory usage: rmem = 35.0 + 0.0157 * Ncache +# Ncache = int((rmem - 35.0) / 0.0157) (rmem in MB) # # rderiv_aver = 0.0157 # fig = pp.figure() diff --git a/evennia/server/profiling/settings_mixin.py b/evennia/server/profiling/settings_mixin.py index 5c680e3ef..ee2439a9f 100644 --- a/evennia/server/profiling/settings_mixin.py +++ b/evennia/server/profiling/settings_mixin.py @@ -15,7 +15,7 @@ DUMMYRUNNER_MIXIN = True # account creations. The default one is safer but deliberately # very slow to make cracking harder. PASSWORD_HASHERS = ( - 'django.contrib.auth.hashers.MD5PasswordHasher', - ) + 'django.contrib.auth.hashers.MD5PasswordHasher', +) # make dummy clients able to test all commands PERMISSION_ACCOUNT_DEFAULT = "Developer" diff --git a/evennia/server/profiling/test_queries.py b/evennia/server/profiling/test_queries.py index 8dbd25308..050dd9794 100644 --- a/evennia/server/profiling/test_queries.py +++ b/evennia/server/profiling/test_queries.py @@ -4,7 +4,8 @@ query as well as count them for optimization testing. """ from __future__ import print_function -import sys, os +import sys +import os #sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))) #os.environ["DJANGO_SETTINGS_MODULE"] = "game.settings" from django.db import connection @@ -26,17 +27,18 @@ def count_queries(exec_string, setup_string): print(query["time"], query["sql"]) print("Number of queries: %s" % nqueries) + if __name__ == "__main__": # setup tests here setup_string = \ -""" + """ from evennia.objects.models import ObjectDB g = ObjectDB.objects.get(db_key="Griatch") """ exec_string = \ -""" + """ g.tags.all() """ count_queries(exec_string, setup_string) diff --git a/evennia/server/profiling/timetrace.py b/evennia/server/profiling/timetrace.py index 18a700077..00a37c3f3 100644 --- a/evennia/server/profiling/timetrace.py +++ b/evennia/server/profiling/timetrace.py @@ -4,6 +4,7 @@ Trace a message through the messaging system from __future__ import print_function import time + def timetrace(message, idstring, tracemessage="TEST_MESSAGE", final=False): """ Trace a message with time stamps. @@ -30,10 +31,10 @@ def timetrace(message, idstring, tracemessage="TEST_MESSAGE", final=False): else: t1 = time.time() # 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)) if final: - message = " **** %s (total %f) **** " % (tracemessage, t1-t0) + message = " **** %s (total %f) **** " % (tracemessage, t1 - t0) else: message = "%s %f %f" % (tracemessage, t1, t0) return message diff --git a/evennia/server/server.py b/evennia/server/server.py index a9db79541..65afc4d87 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -89,6 +89,7 @@ _FLUSH_CACHE = None _IDMAPPER_CACHE_MAXSIZE = settings.IDMAPPER_CACHE_MAXSIZE _GAMETIME_MODULE = None + def _server_maintenance(): """ This maintenance function handles repeated checks and updates that @@ -122,17 +123,20 @@ def _server_maintenance(): if _MAINTENANCE_COUNT % 3700 == 0: # validate channels off-sync with scripts evennia.CHANNEL_HANDLER.update() - ## Commenting this out, it is probably not needed - ## with CONN_MAX_AGE set. Keeping it as a reminder - ## if database-gone-away errors appears again /Griatch - #if _MAINTENANCE_COUNT % 18000 == 0: + + # Commenting this out, it is probably not needed + # with CONN_MAX_AGE set. Keeping it as a reminder + # if database-gone-away errors appears again /Griatch + # if _MAINTENANCE_COUNT % 18000 == 0: # connection.close() maintenance_task = LoopingCall(_server_maintenance) -maintenance_task.start(60, now=True) # call every minute +maintenance_task.start(60, now=True) # call every minute #------------------------------------------------------------ # Evennia Main Server object #------------------------------------------------------------ + + class Evennia(object): """ @@ -195,10 +199,10 @@ class Evennia(object): Optimize some SQLite stuff at startup since we can't save it to the database. """ - if ((".".join(str(i) for i in django.VERSION) < "1.2" and settings.DATABASES.get('default', {}).get('ENGINE') == "sqlite3") - or (hasattr(settings, 'DATABASES') - and settings.DATABASES.get("default", {}).get('ENGINE', None) - == 'django.db.backends.sqlite3')): + if ((".".join(str(i) for i in django.VERSION) < "1.2" and settings.DATABASES.get('default', {}).get('ENGINE') == "sqlite3") or + (hasattr(settings, 'DATABASES') and + settings.DATABASES.get("default", {}).get('ENGINE', None) == + 'django.db.backends.sqlite3')): cursor = connection.cursor() cursor.execute("PRAGMA cache_size=10000") cursor.execute("PRAGMA synchronous=OFF") @@ -253,7 +257,7 @@ class Evennia(object): # if this is the first start we might not have a "previous" # setup saved. Store it now. [ServerConfig.objects.conf(settings_names[i], tup[1]) - for i, tup in enumerate(settings_compare) if not tup[0]] + for i, tup in enumerate(settings_compare) if not tup[0]] def run_initial_setup(self): """ @@ -273,8 +277,8 @@ class Evennia(object): # modules and setup will resume from this step, retrying # the last failed module. When all are finished, the step # is set to -1 to show it does not need to be run again. - print(' Resuming initial setup from step %(last)s.' % \ - {'last': last_initial_setup_step}) + print(' Resuming initial setup from step %(last)s.' % + {'last': last_initial_setup_step}) initial_setup.handle_setup(int(last_initial_setup_step)) print('-' * 50) @@ -285,7 +289,7 @@ class Evennia(object): from evennia.objects.models import ObjectDB #from evennia.accounts.models import AccountDB - #update eventual changed defaults + # update eventual changed defaults self.update_defaults() [o.at_init() for o in ObjectDB.get_all_cached_instances()] @@ -382,7 +386,7 @@ class Evennia(object): yield [_SA(p, "is_connected", False) for p in AccountDB.get_all_cached_instances()] yield [o.at_server_shutdown() for o in ObjectDB.get_all_cached_instances()] yield [(p.unpuppet_all(), p.at_server_shutdown()) - for p in AccountDB.get_all_cached_instances()] + for p in AccountDB.get_all_cached_instances()] yield ObjectDB.objects.clear_all_sessids() yield [(s.pause(manual_pause=False), s.at_server_shutdown()) for s in ScriptDB.get_all_cached_instances()] ServerConfig.objects.conf("server_restart_mode", "reset") @@ -399,7 +403,7 @@ class Evennia(object): # for Windows we need to remove pid files manually os.remove(SERVER_PIDFILE) - if hasattr(self, "web_root"): # not set very first start + if hasattr(self, "web_root"): # not set very first start yield self.web_root.empty_threadpool() if not _reactor_stopping: @@ -420,7 +424,6 @@ class Evennia(object): if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.at_server_start() - def at_server_stop(self): """ This is called just before a server is shut down, regardless @@ -429,7 +432,6 @@ class Evennia(object): if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.at_server_stop() - def at_server_reload_start(self): """ This is called only when server starts back up after a reload. @@ -470,7 +472,6 @@ class Evennia(object): if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.at_server_reload_stop() - def at_server_cold_start(self): """ This is called only when the server starts "cold", i.e. after a @@ -489,7 +490,8 @@ class Evennia(object): if GUEST_ENABLED: for guest in AccountDB.objects.all().filter(db_typeclass_path=settings.BASE_GUEST_TYPECLASS): for character in guest.db._playable_characters: - if character: character.delete() + if character: + character.delete() guest.delete() if SERVER_STARTSTOP_MODULE: SERVER_STARTSTOP_MODULE.at_server_cold_start() @@ -507,6 +509,7 @@ class Evennia(object): # #------------------------------------------------------------ + # Tell the system the server is starting up; some things are not available yet ServerConfig.objects.conf("server_starting_mode", True) @@ -549,7 +552,7 @@ if WEBSERVER_ENABLED: # start a thread pool and define the root url (/) as a wsgi resource # recognized by Django threads = LockableThreadPool(minthreads=max(1, settings.WEBSERVER_THREADPOOL_LIMITS[0]), - maxthreads=max(1, settings.WEBSERVER_THREADPOOL_LIMITS[1])) + maxthreads=max(1, settings.WEBSERVER_THREADPOOL_LIMITS[1])) web_root = DjangoWebRoot(threads) # point our media resources to url /media @@ -597,4 +600,3 @@ if os.name == 'nt': # Windows only: Set PID file manually with open(SERVER_PIDFILE, 'w') as f: f.write(str(os.getpid())) - diff --git a/evennia/server/serversession.py b/evennia/server/serversession.py index 6de73d02e..8d5567a5a 100644 --- a/evennia/server/serversession.py +++ b/evennia/server/serversession.py @@ -35,6 +35,7 @@ from django.utils.translation import ugettext as _ class NDbHolder(object): """Holder for allowing property access of attributes""" + def __init__(self, obj, name, manager_name='attributes'): _SA(self, name, _GA(obj, manager_name)) _SA(self, 'name', name) @@ -65,6 +66,7 @@ class NAttributeHandler(object): by the `.ndb` handler in the same way as `.db` does for the `AttributeHandler`. """ + def __init__(self, obj): """ Initialized on the object @@ -160,6 +162,7 @@ class ServerSession(Session): through their session. """ + def __init__(self): """Initiate to avoid AttributeErrors down the line""" self.puppet = None diff --git a/evennia/server/session.py b/evennia/server/session.py index 414f572a6..70be0708d 100644 --- a/evennia/server/session.py +++ b/evennia/server/session.py @@ -83,7 +83,7 @@ class Session(object): self.cmd_total = 0 self.protocol_flags = {"ENCODING": "utf-8", - "SCREENREADER":False, + "SCREENREADER": False, "INPUTDEBUG": False, "RAW": False, "NOCOLOR": False} @@ -106,7 +106,7 @@ class Session(object): """ return dict((key, value) for key, value in self.__dict__.items() - if key in self._attrs_to_sync) + if key in self._attrs_to_sync) def load_sync_data(self, sessdata): """ diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index b9eeb1918..53c4671fc 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -39,8 +39,11 @@ _ServerConfig = None _ScriptDB = None _OOB_HANDLER = None + class DummySession(object): sessid = 0 + + DUMMYSESSION = DummySession() # AMP signals @@ -54,7 +57,7 @@ SSHUTD = chr(7) # server shutdown SSYNC = chr(8) # server session sync SCONN = chr(11) # server portal connection (for bots) PCONNSYNC = chr(12) # portal post-syncing session -PDISCONNALL = chr(13) # portal session discnnect all +PDISCONNALL = chr(13) # portal session discnnect all # i18n from django.utils.translation import ugettext as _ @@ -72,6 +75,7 @@ _INPUT_FUNCS = {} for modname in make_iter(settings.INPUT_FUNC_MODULES): _INPUT_FUNCS.update(callables_from_module(modname)) + def delayed_import(): """ Helper method for delayed import of all needed entities. @@ -222,9 +226,9 @@ class SessionHandler(dict): # we don't allow sending text = None, this must mean # that the text command is not to be used. continue - rkwargs[key] = [ [], {} ] + rkwargs[key] = [[], {}] elif isinstance(data, dict): - rkwargs[key] = [ [], _validate(data) ] + rkwargs[key] = [[], _validate(data)] elif hasattr(data, "__iter__"): if isinstance(data[-1], dict): if len(data) == 2: @@ -233,11 +237,11 @@ class SessionHandler(dict): else: rkwargs[key] = [[_validate(data[0])], _validate(data[1])] else: - rkwargs[key] = [ _validate(data[:-1]), _validate(data[-1]) ] + rkwargs[key] = [_validate(data[:-1]), _validate(data[-1])] else: - rkwargs[key] = [ _validate(data), {} ] + rkwargs[key] = [_validate(data), {}] else: - rkwargs[key] = [ [_validate(data)], {} ] + rkwargs[key] = [[_validate(data)], {}] rkwargs[key][1]["options"] = options return rkwargs @@ -306,7 +310,7 @@ class ServerSessionHandler(SessionHandler): sess.uid = None # show the first login command - self.data_in(sess, text=[[CMD_LOGINSTART],{}]) + self.data_in(sess, text=[[CMD_LOGINSTART], {}]) def portal_session_sync(self, portalsessiondata): """ @@ -362,7 +366,6 @@ class ServerSessionHandler(SessionHandler): # announce the reconnection self.announce_all(_(" ... Server restarted.")) - def portal_disconnect(self, session): """ Called from Portal when Portal session closed from the portal @@ -415,7 +418,7 @@ class ServerSessionHandler(SessionHandler): """ self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION, operation=SCONN, - protocol_path=protocol_path, config=configdict) + protocol_path=protocol_path, config=configdict) def portal_shutdown(self): """ @@ -466,14 +469,14 @@ class ServerSessionHandler(SessionHandler): nsess = len(self.sessions_from_account(account)) string = "Logged in: {account} {address} ({nsessions} session(s) total)" - string = string.format(account=account,address=session.address, nsessions=nsess) + string = string.format(account=account, address=session.address, nsessions=nsess) session.log(string) session.logged_in = True # sync the portal to the session if not testmode: self.server.amp_protocol.send_AdminServer2Portal(session, - operation=SLOGIN, - sessiondata={"logged_in": True}) + operation=SLOGIN, + sessiondata={"logged_in": True}) account.at_post_login(session=session) def disconnect(self, session, reason="", sync_portal=True): @@ -518,8 +521,8 @@ class ServerSessionHandler(SessionHandler): """ sessdata = self.get_all_sync_data() return self.server.amp_protocol.send_AdminServer2Portal(DUMMYSESSION, - operation=SSYNC, - sessiondata=sessdata) + operation=SSYNC, + sessiondata=sessdata) def session_portal_sync(self, session): """ @@ -550,7 +553,7 @@ class ServerSessionHandler(SessionHandler): reason=reason) def disconnect_duplicate_sessions(self, curr_session, - reason=_("Logged in from elsewhere. Disconnecting.")): + reason=_("Logged in from elsewhere. Disconnecting.")): """ Disconnects any existing sessions with the same user. @@ -561,9 +564,9 @@ class ServerSessionHandler(SessionHandler): """ uid = curr_session.uid doublet_sessions = [sess for sess in self.values() - if sess.logged_in - and sess.uid == uid - and sess != curr_session] + if sess.logged_in and + sess.uid == uid and + sess != curr_session] for session in doublet_sessions: self.disconnect(session, reason) @@ -576,8 +579,8 @@ class ServerSessionHandler(SessionHandler): tcurr = time.time() reason = _("Idle timeout exceeded, disconnecting.") for session in (session for session in self.values() - if session.logged_in and _IDLE_TIMEOUT > 0 - and (tcurr - session.cmd_last) > _IDLE_TIMEOUT): + if session.logged_in and _IDLE_TIMEOUT > 0 and + (tcurr - session.cmd_last) > _IDLE_TIMEOUT): self.disconnect(session, reason=reason) def account_count(self): @@ -757,10 +760,11 @@ class ServerSessionHandler(SessionHandler): _INPUT_FUNCS[cname](session, *cmdargs, **cmdkwargs) else: _INPUT_FUNCS["default"](session, cname, *cmdargs, **cmdkwargs) - except Exception, err: + except Exception as err: if input_debug: session.msg(err) log_trace() + SESSION_HANDLER = ServerSessionHandler() -SESSIONS = SESSION_HANDLER # legacy +SESSIONS = SESSION_HANDLER # legacy diff --git a/evennia/server/tests.py b/evennia/server/tests.py index 59bd8b8eb..7b39a2d7f 100644 --- a/evennia/server/tests.py +++ b/evennia/server/tests.py @@ -37,6 +37,7 @@ class EvenniaTestSuiteRunner(DiscoverRunner): This test runner only runs tests on the apps specified in evennia/ avoid running the large number of tests defined by Django """ + 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. diff --git a/evennia/server/webserver.py b/evennia/server/webserver.py index ceb072c5c..bdb1728bd 100644 --- a/evennia/server/webserver.py +++ b/evennia/server/webserver.py @@ -35,6 +35,7 @@ class LockableThreadPool(threadpool.ThreadPool): """ Threadpool that can be locked from accepting new requests. """ + def __init__(self, *args, **kwargs): self._accept_new = True threadpool.ThreadPool.__init__(self, *args, **kwargs) @@ -60,6 +61,7 @@ class HTTPChannelWithXForwardedFor(http.HTTPChannel): HTTP xforward class """ + def allHeadersReceived(self): """ Check to see if this is a reverse proxied connection. @@ -227,6 +229,7 @@ class WSGIWebServer(internet.TCPServer): call with WSGIWebServer(threadpool, port, wsgi_resource) """ + def __init__(self, pool, *args, **kwargs): """ This just stores the threadpool. diff --git a/evennia/settings_default.py b/evennia/settings_default.py index 27e379a20..d12c21ba9 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -245,7 +245,7 @@ DATABASES = { 'PASSWORD': '', 'HOST': '', 'PORT': '' - }} + }} # How long the django-database connection should be kept open, in seconds. # If you get errors about the database having gone away after long idle # periods, shorten this value (e.g. MySQL defaults to a timeout of 8 hrs) @@ -522,7 +522,7 @@ MAX_NR_CHARACTERS = 1 # The access hierarchy, in climbing order. A higher permission in the # hierarchy includes access of all levels below it. Used by the perm()/pperm() # lock functions, which accepts both plural and singular (Admin & Admins) -PERMISSION_HIERARCHY = ["Guest", # note-only used if GUEST_ENABLED=True +PERMISSION_HIERARCHY = ["Guest", # note-only used if GUEST_ENABLED=True "Player", "Helper", "Builder", @@ -559,7 +559,7 @@ GUEST_START_LOCATION = START_LOCATION # accounts/characters. The size of this list also determines how many # guests may be on the game at once. The default is a maximum of nine # guests, named Guest1 through Guest9. -GUEST_LIST = ["Guest" + str(s+1) for s in range(9)] +GUEST_LIST = ["Guest" + str(s + 1) for s in range(9)] ###################################################################### # In-game Channels created from server start @@ -576,17 +576,17 @@ GUEST_LIST = ["Guest" + str(s+1) for s in range(9)] # general "mud info" channel. Other channels beyond that # are up to the admin to design and call appropriately. DEFAULT_CHANNELS = [ - # public channel - {"key": "Public", - "aliases": ('ooc', 'pub'), - "desc": "Public discussion", - "locks": "control:perm(Admin);listen:all();send:all()"}, - # connection/mud info - {"key": "MudInfo", - "aliases": "", - "desc": "Connection log", - "locks": "control:perm(Developer);listen:perm(Admin);send:false()"} - ] + # public channel + {"key": "Public", + "aliases": ('ooc', 'pub'), + "desc": "Public discussion", + "locks": "control:perm(Admin);listen:all();send:all()"}, + # connection/mud info + {"key": "MudInfo", + "aliases": "", + "desc": "Connection log", + "locks": "control:perm(Developer);listen:perm(Admin);send:false()"} +] # Extra optional channel for receiving connection messages (" has (dis)connected"). # While the MudInfo channel will also receieve this, this channel is meant for non-staffers. CHANNEL_CONNECTINFO = None @@ -613,7 +613,7 @@ IRC_ENABLED = False # be installed (through package manager or from the website # http://code.google.com/p/feedparser/) RSS_ENABLED = False -RSS_UPDATE_INTERVAL = 60*10 # 10 minutes +RSS_UPDATE_INTERVAL = 60 * 10 # 10 minutes ###################################################################### # Django web features @@ -690,37 +690,37 @@ WEBSITE_TEMPLATE = 'website' WEBCLIENT_TEMPLATE = 'webclient' # The default options used by the webclient WEBCLIENT_OPTIONS = { - "gagprompt": True, # Gags prompt from the output window and keep them - # together with the input bar - "helppopup": True, # Shows help files in a new popup window - "notification_popup": False, # Shows notifications of new messages as - # popup windows - "notification_sound": False # Plays a sound for notifications of new - # messages - } + "gagprompt": True, # Gags prompt from the output window and keep them + # together with the input bar + "helppopup": True, # Shows help files in a new popup window + "notification_popup": False, # Shows notifications of new messages as + # popup windows + "notification_sound": False # Plays a sound for notifications of new + # messages +} # We setup the location of the website template as well as the admin site. TEMPLATES = [{ - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(GAME_DIR, "web", "template_overrides", WEBSITE_TEMPLATE), - os.path.join(GAME_DIR, "web", "template_overrides", WEBCLIENT_TEMPLATE), - os.path.join(GAME_DIR, "web", "template_overrides"), - os.path.join(EVENNIA_DIR, "web", "website", "templates", WEBSITE_TEMPLATE), - os.path.join(EVENNIA_DIR, "web", "website", "templates"), - os.path.join(EVENNIA_DIR, "web", "webclient", "templates", WEBCLIENT_TEMPLATE), - os.path.join(EVENNIA_DIR, "web", "webclient", "templates")], - 'APP_DIRS': True, - 'OPTIONS': { - "context_processors": [ - 'django.template.context_processors.i18n', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.template.context_processors.media', - 'django.template.context_processors.debug', - 'evennia.web.utils.general_context.general_context'] - } - }] + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [ + os.path.join(GAME_DIR, "web", "template_overrides", WEBSITE_TEMPLATE), + os.path.join(GAME_DIR, "web", "template_overrides", WEBCLIENT_TEMPLATE), + os.path.join(GAME_DIR, "web", "template_overrides"), + os.path.join(EVENNIA_DIR, "web", "website", "templates", WEBSITE_TEMPLATE), + os.path.join(EVENNIA_DIR, "web", "website", "templates"), + os.path.join(EVENNIA_DIR, "web", "webclient", "templates", WEBCLIENT_TEMPLATE), + os.path.join(EVENNIA_DIR, "web", "webclient", "templates")], + 'APP_DIRS': True, + 'OPTIONS': { + "context_processors": [ + 'django.template.context_processors.i18n', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.template.context_processors.media', + 'django.template.context_processors.debug', + 'evennia.web.utils.general_context.general_context'] + } +}] # MiddleWare are semi-transparent extensions to Django's functionality. # see http://www.djangoproject.com/documentation/middleware/ for a more detailed diff --git a/evennia/typeclasses/__init__.py b/evennia/typeclasses/__init__.py index c13ff64d6..00363f0b0 100644 --- a/evennia/typeclasses/__init__.py +++ b/evennia/typeclasses/__init__.py @@ -8,4 +8,3 @@ inherit from the models in this package. Here is also were the Attribute and Tag models are defined along with their handlers. """ - diff --git a/evennia/typeclasses/admin.py b/evennia/typeclasses/admin.py index 0eba3792e..47c0b457a 100644 --- a/evennia/typeclasses/admin.py +++ b/evennia/typeclasses/admin.py @@ -83,6 +83,7 @@ class TagFormSet(forms.BaseInlineFormSet): Object, where the handler is an AliasHandler, PermissionsHandler, or TagHandler, based on the type of tag. """ + def save(self, commit=True): def get_handler(finished_object): related = getattr(finished_object, self.related_field) diff --git a/evennia/typeclasses/managers.py b/evennia/typeclasses/managers.py index f212ea6b4..18379d568 100644 --- a/evennia/typeclasses/managers.py +++ b/evennia/typeclasses/managers.py @@ -412,7 +412,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager): typeclass_paths = set(self.values_list('db_typeclass_path', flat=True)) for typeclass_path in typeclass_paths: dbtotals[typeclass_path] = \ - self.filter(db_typeclass_path=typeclass_path).count() + self.filter(db_typeclass_path=typeclass_path).count() return dbtotals def typeclass_search(self, typeclass, include_children=False, include_parents=False): @@ -523,7 +523,7 @@ class TypeclassManager(TypedObjectManager): queries.append(part) # build query from components query = ' '.join(queries) - #TODO + # TODO def get(self, *args, **kwargs): """ diff --git a/evennia/typeclasses/migrations/0005_auto_20160625_1812.py b/evennia/typeclasses/migrations/0005_auto_20160625_1812.py index b922446e3..b3fd24f56 100644 --- a/evennia/typeclasses/migrations/0005_auto_20160625_1812.py +++ b/evennia/typeclasses/migrations/0005_auto_20160625_1812.py @@ -6,6 +6,7 @@ from django.db import migrations # Migration of old nick format to new + def update_nicks(apps, schema_editor): Attribute = apps.get_model("typeclasses", "Attribute") for nick in Attribute.objects.filter(db_attrtype="nick"): diff --git a/evennia/typeclasses/migrations/0006_auto_add_dbmodel_value_for_tags_attributes.py b/evennia/typeclasses/migrations/0006_auto_add_dbmodel_value_for_tags_attributes.py index 1cf8be816..f2cb9c23c 100644 --- a/evennia/typeclasses/migrations/0006_auto_add_dbmodel_value_for_tags_attributes.py +++ b/evennia/typeclasses/migrations/0006_auto_add_dbmodel_value_for_tags_attributes.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals from django.db import migrations + def update_tags_with_dbmodel(apps, schema_editor): ObjectDB = apps.get_model('objects', 'ObjectDB') @@ -42,5 +43,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(update_tags_with_dbmodel) + migrations.RunPython(update_tags_with_dbmodel) ] diff --git a/evennia/typeclasses/migrations/0007_tag_migrations_may_be_slow.py b/evennia/typeclasses/migrations/0007_tag_migrations_may_be_slow.py index eac208f6b..443ade55c 100644 --- a/evennia/typeclasses/migrations/0007_tag_migrations_may_be_slow.py +++ b/evennia/typeclasses/migrations/0007_tag_migrations_may_be_slow.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals from django.db import migrations + def update_tags_with_dbmodel(apps, schema_editor): Tag = apps.get_model('typeclasses', 'Tag') diff --git a/evennia/typeclasses/migrations/0008_lock_and_perm_rename.py b/evennia/typeclasses/migrations/0008_lock_and_perm_rename.py index 883572742..aee25c279 100644 --- a/evennia/typeclasses/migrations/0008_lock_and_perm_rename.py +++ b/evennia/typeclasses/migrations/0008_lock_and_perm_rename.py @@ -5,12 +5,13 @@ from __future__ import unicode_literals import re from django.db import migrations + def update_perms_and_locks(apps, schema_editor): # update all permissions Tag = apps.get_model('typeclasses', 'Tag') - perm_map = {"guests": "guest", "players": "player", "playerhelpers":"helper", - "builders": "builder", "wizards":"admin", "immortals": "developer"} + perm_map = {"guests": "guest", "players": "player", "playerhelpers": "helper", + "builders": "builder", "wizards": "admin", "immortals": "developer"} for perm in Tag.objects.filter(db_tagtype="permission"): if perm.db_key in perm_map: @@ -21,7 +22,8 @@ def update_perms_and_locks(apps, schema_editor): apps_models = [("objects", "ObjectDB"), ("accounts", "AccountDB"), ("scripts", "ScriptDB"), ("comms", "ChannelDB")] p_reg = re.compile(r"(?<=perm\()(\w+)(?=\))|(?<=perm_above\()(\w+)(?=\))", - re.IGNORECASE + re.UNICODE) + re.IGNORECASE + re.UNICODE) + def _sub(match): perm = match.group(1) return perm_map[perm.lower()].capitalize() if perm.lower() in perm_map else perm @@ -35,6 +37,7 @@ def update_perms_and_locks(apps, schema_editor): obj.db_lock_storage = repl_lock obj.save(update_fields=('db_lock_storage',)) + class Migration(migrations.Migration): dependencies = [ diff --git a/evennia/typeclasses/migrations/0009_rename_player_cmdsets_typeclasses.py b/evennia/typeclasses/migrations/0009_rename_player_cmdsets_typeclasses.py index 9c54892b9..dfbfdcbf6 100644 --- a/evennia/typeclasses/migrations/0009_rename_player_cmdsets_typeclasses.py +++ b/evennia/typeclasses/migrations/0009_rename_player_cmdsets_typeclasses.py @@ -41,12 +41,12 @@ def _case_sensitive_replace(string, old, new): all_upper = False # special cases - keep remaing case) if new_word.lower() in CASE_WORD_EXCEPTIONS: - result.append(new_word[ind+1:]) + result.append(new_word[ind + 1:]) # append any remaining characters from new elif all_upper: - result.append(new_word[ind+1:].upper()) + result.append(new_word[ind + 1:].upper()) else: - result.append(new_word[ind+1:].lower()) + result.append(new_word[ind + 1:].lower()) out.append("".join(result)) # if we have more new words than old ones, just add them verbatim out.extend([new_word for ind, new_word in enumerate(new_words) if ind >= len(old_words)]) diff --git a/evennia/typeclasses/models.py b/evennia/typeclasses/models.py index 296eaef70..cdbf8a91d 100644 --- a/evennia/typeclasses/models.py +++ b/evennia/typeclasses/models.py @@ -43,8 +43,8 @@ from evennia.utils.idmapper.models import SharedMemoryModel, SharedMemoryModelBa from evennia.typeclasses import managers from evennia.locks.lockhandler import LockHandler from evennia.utils.utils import ( - is_iter, inherits_from, lazy_property, - class_from_module) + is_iter, inherits_from, lazy_property, + class_from_module) from evennia.utils.logger import log_trace from .signals import remove_attributes_on_delete, post_save @@ -84,10 +84,10 @@ class TypeclassBase(SharedMemoryModelBase): # storage of stats attrs["typename"] = name - attrs["path"] = "%s.%s" % (attrs["__module__"], name) + attrs["path"] = "%s.%s" % (attrs["__module__"], name) # typeclass proxy setup - if not "Meta" in attrs: + if "Meta" not in attrs: class Meta(object): proxy = True app_label = attrs.get("__applabel__", "typeclasses") @@ -104,6 +104,7 @@ class TypeclassBase(SharedMemoryModelBase): class DbHolder(object): "Holder for allowing property access of attributes" + def __init__(self, obj, name, manager_name='attributes'): _SA(self, name, _GA(obj, manager_name)) _SA(self, 'name', name) @@ -164,17 +165,17 @@ class TypedObject(SharedMemoryModel): # This is the python path to the type class this object is tied to. The # typeclass is what defines what kind of Object this is) db_typeclass_path = models.CharField('typeclass', max_length=255, null=True, - help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.") + help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.") # Creation date. This is not changed once the object is created. db_date_created = models.DateTimeField('creation date', editable=False, auto_now_add=True) # Lock storage db_lock_storage = models.TextField('locks', blank=True, - help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.") + help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Not defining a lock means no access is granted.") # many2many relationships db_attributes = models.ManyToManyField(Attribute, - help_text='attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).') + help_text='attributes on this object. An attribute can hold any pickle-able python object (see docs for special cases).') db_tags = models.ManyToManyField(Tag, - help_text='tags on this object. Tags are simple string markers to identify, group and alias objects.') + help_text='tags on this object. Tags are simple string markers to identify, group and alias objects.') # Database manager objects = managers.TypedObjectManager() @@ -258,7 +259,6 @@ class TypedObject(SharedMemoryModel): super(TypedObject, self).__init__(*args, **kwargs) self.set_class_from_typeclass(typeclass_path=typeclass_path) - # initialize all handlers in a lazy fashion @lazy_property def attributes(self): @@ -284,7 +284,6 @@ class TypedObject(SharedMemoryModel): def nattributes(self): return NAttributeHandler(self) - class Meta(object): """ Django setup info. @@ -488,11 +487,10 @@ class TypedObject(SharedMemoryModel): # if we get to this point, the class is ok. - if inherits_from(self, "evennia.scripts.models.ScriptDB"): if self.interval > 0: - raise RuntimeError("Cannot use swap_typeclass on time-dependent " \ - "Script '%s'.\nStop and start a new Script of the " \ + raise RuntimeError("Cannot use swap_typeclass on time-dependent " + "Script '%s'.\nStop and start a new Script of the " "right type instead." % self.key) self.typeclass_path = new_typeclass.path diff --git a/evennia/typeclasses/signals.py b/evennia/typeclasses/signals.py index 14526c7c2..49a245706 100644 --- a/evennia/typeclasses/signals.py +++ b/evennia/typeclasses/signals.py @@ -11,6 +11,3 @@ def post_save(sender, instance, created, **kwargs): def remove_attributes_on_delete(sender, instance, **kwargs): instance.db_attributes.all().delete() - - - diff --git a/evennia/typeclasses/tags.py b/evennia/typeclasses/tags.py index 67f0662f2..b3eb433ee 100644 --- a/evennia/typeclasses/tags.py +++ b/evennia/typeclasses/tags.py @@ -25,6 +25,7 @@ _TYPECLASS_AGGRESSIVE_CACHE = settings.TYPECLASS_AGGRESSIVE_CACHE # #------------------------------------------------------------ + class Tag(models.Model): """ Tags are quick markers for objects in-game. An typeobject can have @@ -105,9 +106,9 @@ class TagHandler(object): def _fullcache(self): "Cache all tags of this object" - query = {"%s__id" % self._model : self._objid, - "tag__db_model" : self._model, - "tag__db_tagtype" : self._tagtype} + query = {"%s__id" % self._model: self._objid, + "tag__db_model": self._model, + "tag__db_tagtype": self._tagtype} tags = [conn.tag for conn in getattr(self.obj, self._m2m_fieldname).through.objects.filter(**query)] self._cache = dict(("%s-%s" % (to_str(tag.db_key).lower(), tag.db_category.lower() if tag.db_category else None), @@ -149,11 +150,11 @@ class TagHandler(object): if tag: return [tag] # return cached entity else: - query = {"%s__id" % self._model : self._objid, - "tag__db_model" : self._model, - "tag__db_tagtype" : self._tagtype, - "tag__db_key__iexact" : key.lower(), - "tag__db_category__iexact" : category.lower() if category else None} + query = {"%s__id" % self._model: self._objid, + "tag__db_model": self._model, + "tag__db_tagtype": self._tagtype, + "tag__db_key__iexact": key.lower(), + "tag__db_category__iexact": category.lower() if category else None} conn = getattr(self.obj, self._m2m_fieldname).through.objects.filter(**query) if conn: tag = conn[0].tag @@ -168,12 +169,12 @@ class TagHandler(object): return [tag for key, tag in self._cache.items() if key.endswith(catkey)] else: # we have to query to make this category up-date in the cache - query = {"%s__id" % self._model : self._objid, - "tag__db_model" : self._model, - "tag__db_tagtype" : self._tagtype, - "tag__db_category__iexact" : category.lower() if category else None} + query = {"%s__id" % self._model: self._objid, + "tag__db_model": self._model, + "tag__db_tagtype": self._tagtype, + "tag__db_category__iexact": category.lower() if category else None} tags = [conn.tag for conn in getattr(self.obj, - self._m2m_fieldname).through.objects.filter(**query)] + self._m2m_fieldname).through.objects.filter(**query)] for tag in tags: cachekey = "%s-%s" % (tag.db_key, category) self._cache[cachekey] = tag @@ -192,7 +193,7 @@ class TagHandler(object): tag_obj (tag): The newly saved tag """ - if not key: # don't allow an empty key in cache + if not key: # don't allow an empty key in cache return key, category = key.strip().lower(), category.strip().lower() if category else category cachekey = "%s-%s" % (key, category) @@ -260,7 +261,7 @@ class TagHandler(object): # will overload data on an existing tag since that is not # considered part of making the tag unique) tagobj = self.obj.__class__.objects.create_tag(key=tagstr, category=category, data=data, - tagtype=self._tagtype) + tagtype=self._tagtype) getattr(self.obj, self._m2m_fieldname).add(tagobj) self._setcache(tagstr, category, tagobj) @@ -287,7 +288,7 @@ class TagHandler(object): for keystr in make_iter(key): # note - the _getcache call removes case sensitivity for us ret.extend([tag if return_tagobj else to_str(tag.db_key) - for tag in self._getcache(keystr, category)]) + for tag in self._getcache(keystr, category)]) return ret[0] if len(ret) == 1 else (ret if ret else default) def remove(self, key, category=None): @@ -311,7 +312,7 @@ class TagHandler(object): # that when no objects reference the tag anymore (but how to check)? # For now, tags are never deleted, only their connection to objects. tagobj = getattr(self.obj, self._m2m_fieldname).filter(db_key=tagstr, db_category=category, - db_model=self._model, db_tagtype=self._tagtype) + db_model=self._model, db_tagtype=self._tagtype) if tagobj: getattr(self.obj, self._m2m_fieldname).remove(tagobj[0]) self._delcache(key, category) @@ -326,7 +327,7 @@ class TagHandler(object): category. """ - query = {"%s__id" % self._model : self._objid, "tag__db_model": self._model, "tag__db_tagtype": self._tagtype} + query = {"%s__id" % self._model: self._objid, "tag__db_model": self._model, "tag__db_tagtype": self._tagtype} if category: query["tag__db_category"] = category.strip().lower() getattr(self.obj, self._m2m_fieldname).through.objects.filter(**query).delete() @@ -408,4 +409,3 @@ class PermissionHandler(TagHandler): """ _tagtype = "permission" - diff --git a/evennia/utils/ansi.py b/evennia/utils/ansi.py index c9ea5c622..b78ad142c 100644 --- a/evennia/utils/ansi.py +++ b/evennia/utils/ansi.py @@ -142,7 +142,7 @@ class ANSIParser(object): (r'|[C', ANSI_BACK_CYAN), (r'|[W', ANSI_BACK_WHITE), # light grey background (r'|[X', ANSI_BACK_BLACK) # pure black background - ] + ] ansi_xterm256_bright_bg_map = [ # "bright" ANSI backgrounds using xterm256 since ANSI @@ -186,7 +186,7 @@ class ANSIParser(object): # prepare regex matching brightbg_sub = re.compile(r"|".join([r"(? nrstripped: break @@ -1161,7 +1166,7 @@ class ANSIString(with_metaclass(ANSIMeta, unicode)): ic -= 1 ir2 -= 1 rstripped = rstripped[::-1] - return ANSIString(lstripped + raw[ir1:ir2+1] + rstripped) + return ANSIString(lstripped + raw[ir1:ir2 + 1] + rstripped) def lstrip(self, chars=None): """ @@ -1214,7 +1219,7 @@ class ANSIString(with_metaclass(ANSIMeta, unicode)): nlen = len(clean) nrstripped = nlen - len(clean.rstrip(chars)) rstripped = "" - ic, ir2 = nlen-1, len(raw)-1 + ic, ir2 = nlen - 1, len(raw) - 1 while nrstripped: if nlen - ic > nrstripped: break @@ -1224,7 +1229,7 @@ class ANSIString(with_metaclass(ANSIMeta, unicode)): ic -= 1 ir2 -= 1 rstripped = rstripped[::-1] - return ANSIString(raw[:ir2+1] + rstripped) + return ANSIString(raw[:ir2 + 1] + rstripped) def join(self, iterable): """ diff --git a/evennia/utils/batchprocessors.py b/evennia/utils/batchprocessors.py index 70a741c71..87a12daa6 100644 --- a/evennia/utils/batchprocessors.py +++ b/evennia/utils/batchprocessors.py @@ -248,6 +248,7 @@ class BatchCommandProcessor(object): This class implements a batch-command processor. """ + def parse_file(self, pythonpath): """ This parses the lines of a batchfile according to the following @@ -413,5 +414,6 @@ class BatchCodeProcessor(object): return err return None + BATCHCMD = BatchCommandProcessor() BATCHCODE = BatchCodeProcessor() diff --git a/evennia/utils/create.py b/evennia/utils/create.py index 16291ea4b..99dce4944 100644 --- a/evennia/utils/create.py +++ b/evennia/utils/create.py @@ -122,6 +122,7 @@ def create_object(typeclass=None, key=None, location=None, home=None, new_object.save() return new_object + # alias for create_object object = create_object @@ -179,14 +180,22 @@ def create_script(typeclass=None, key=None, obj=None, account=None, locks=None, # validate input kwarg = {} - if key: kwarg["db_key"] = key - if account: kwarg["db_account"] = dbid_to_obj(account, _ScriptDB) - if obj: kwarg["db_obj"] = dbid_to_obj(obj, _ScriptDB) - if interval: kwarg["db_interval"] = interval - if start_delay: kwarg["db_start_delay"] = start_delay - if repeats: kwarg["db_repeats"] = repeats - if persistent: kwarg["db_persistent"] = persistent - if desc: kwarg["db_desc"] = desc + if key: + kwarg["db_key"] = key + if account: + kwarg["db_account"] = dbid_to_obj(account, _ScriptDB) + if obj: + kwarg["db_obj"] = dbid_to_obj(obj, _ScriptDB) + if interval: + kwarg["db_interval"] = interval + if start_delay: + kwarg["db_start_delay"] = start_delay + if repeats: + kwarg["db_repeats"] = repeats + if persistent: + kwarg["db_persistent"] = persistent + if desc: + kwarg["db_desc"] = desc # create new instance new_script = typeclass(**kwarg) @@ -201,6 +210,7 @@ def create_script(typeclass=None, key=None, obj=None, account=None, locks=None, new_script.save() return new_script + # alias script = create_script @@ -250,6 +260,8 @@ def create_help_entry(key, entrytext, category="General", locks=None, aliases=No except Exception: logger.log_trace() return None + + # alias help_entry = create_help_entry @@ -301,6 +313,8 @@ def create_message(senderobj, message, channels=None, receivers=None, locks=None new_message.locks.add(locks) new_message.save() return new_message + + message = create_message @@ -348,6 +362,7 @@ def create_channel(key, aliases=None, desc=None, new_channel.save() return new_channel + channel = create_channel @@ -357,11 +372,10 @@ channel = create_channel def create_account(key, email, password, - typeclass=None, - is_superuser=False, - locks=None, permissions=None, - report_to=None): - + typeclass=None, + is_superuser=False, + locks=None, permissions=None, + report_to=None): """ This creates a new account. @@ -416,8 +430,8 @@ def create_account(key, email, password, now = timezone.now() email = typeclass.objects.normalize_email(email) new_account = typeclass(username=key, email=email, - is_staff=is_superuser, is_superuser=is_superuser, - last_login=now, date_joined=now) + is_staff=is_superuser, is_superuser=is_superuser, + last_login=now, date_joined=now) new_account.set_password(password) new_account._createdict = dict(locks=locks, permissions=permissions, report_to=report_to) # saving will trigger the signal that calls the @@ -426,5 +440,6 @@ def create_account(key, email, password, new_account.save() return new_account + # alias account = create_account diff --git a/evennia/utils/dbserialize.py b/evennia/utils/dbserialize.py index 76895b4e9..83d3863ab 100644 --- a/evennia/utils/dbserialize.py +++ b/evennia/utils/dbserialize.py @@ -56,13 +56,22 @@ def _get_mysql_db_version(): # initialization and helpers + _GA = object.__getattribute__ _SA = object.__setattr__ _FROM_MODEL_MAP = None _TO_MODEL_MAP = None _SESSION_HANDLER = None -_IS_PACKED_DBOBJ = lambda o: type(o) == tuple and len(o) == 4 and o[0] == '__packed_dbobj__' -_IS_PACKED_SESSION = lambda o: type(o) == tuple and len(o) == 3 and o[0] == '__packed_session__' + + +def _IS_PACKED_DBOBJ(o): + return isinstance(o, tuple) and len(o) == 4 and o[0] == '__packed_dbobj__' + + +def _IS_PACKED_SESSION(o): + return isinstance(o, tuple) and len(o) == 3 and o[0] == '__packed_session__' + + if uses_database("mysql") and _get_mysql_db_version() < '5.6.4': # mysql <5.6.4 don't support millisecond precision _DATESTRING = "%Y:%m:%d-%H:%M:%S:000000" @@ -112,6 +121,7 @@ def _init_globals(): def _save(method): """method decorator that saves data to Attribute""" + def save_wrapper(self, *args, **kwargs): self.__doc__ = method.__doc__ ret = method(self, *args, **kwargs) @@ -127,6 +137,7 @@ class _SaverMutable(object): obj.db.mylist[1][2] = "test" (allocation to a nested list) will not save the updated value to the database. """ + def __init__(self, *args, **kwargs): """store all properties for tracking the tree""" self._parent = kwargs.pop("_parent", None) @@ -196,6 +207,7 @@ class _SaverList(_SaverMutable, MutableSequence): """ A list that saves itself to an Attribute when updated. """ + def __init__(self, *args, **kwargs): super(_SaverList, self).__init__(*args, **kwargs) self._data = list() @@ -223,6 +235,7 @@ class _SaverDict(_SaverMutable, MutableMapping): """ A dict that stores changes to an Attribute when updated """ + def __init__(self, *args, **kwargs): super(_SaverDict, self).__init__(*args, **kwargs) self._data = dict() @@ -235,6 +248,7 @@ class _SaverSet(_SaverMutable, MutableSet): """ A set that saves to an Attribute when updated """ + def __init__(self, *args, **kwargs): super(_SaverSet, self).__init__(*args, **kwargs) self._data = set() @@ -255,6 +269,7 @@ class _SaverOrderedDict(_SaverMutable, MutableMapping): """ An ordereddict that can be saved and operated on. """ + def __init__(self, *args, **kwargs): super(_SaverOrderedDict, self).__init__(*args, **kwargs) self._data = OrderedDict() @@ -267,6 +282,7 @@ class _SaverDeque(_SaverMutable): """ A deque that can be saved and operated on. """ + def __init__(self, *args, **kwargs): super(_SaverDeque, self).__init__(*args, **kwargs) self._data = deque() diff --git a/evennia/utils/eveditor.py b/evennia/utils/eveditor.py index d04582c32..2ec3865ee 100644 --- a/evennia/utils/eveditor.py +++ b/evennia/utils/eveditor.py @@ -378,7 +378,7 @@ class CmdLineInput(CmdEditorBase): indent = "off" self.caller.msg("|b%02i|||n (|g%s|n) %s" % ( - cline, indent, raw(line))) + cline, indent, raw(line))) else: self.caller.msg("|b%02i|||n %s" % (cline, raw(self.args))) @@ -643,7 +643,7 @@ class CmdEditorGroup(CmdEditorBase): indent = editor._indent if indent >= 0: caller.msg("Decreased indentation: new indentation is {}.".format( - indent)) + indent)) else: caller.msg("|rManual indentation is OFF.|n Use := to turn it on.") else: @@ -655,7 +655,7 @@ class CmdEditorGroup(CmdEditorBase): indent = editor._indent if indent >= 0: caller.msg("Increased indentation: new indentation is {}.".format( - indent)) + indent)) else: caller.msg("|rManual indentation is OFF.|n Use := to turn it on.") else: @@ -780,7 +780,7 @@ class EvEditor(object): caller.attributes.add("_eveditor_buffer_temp", (self._buffer, self._undo_buffer)) caller.attributes.add("_eveditor_unsaved", False) caller.attributes.add("_eveditor_indent", 0) - except Exception, err: + except Exception as err: caller.msg(_ERROR_PERSISTENT_SAVING.format(error=err)) logger.log_trace(_TRACE_PERSISTENT_SAVING) persistent = False @@ -929,7 +929,7 @@ class EvEditor(object): nchars = len(buf) sep = self._sep - header = "|n" + sep * 10 + "Line Editor [%s]" % self._key + sep * (_DEFAULT_WIDTH-20-len(self._key)) + header = "|n" + sep * 10 + "Line Editor [%s]" % self._key + sep * (_DEFAULT_WIDTH - 20 - len(self._key)) footer = "|n" + sep * 10 +\ "[l:%02i w:%03i c:%04i]" % (nlines, nwords, nchars) + sep * 12 + "(:h for help)" + sep * 28 if linenums: @@ -956,10 +956,10 @@ class EvEditor(object): """ keywords = { - "elif ": ["if "], - "else:": ["if ", "try"], - "except": ["try:"], - "finally:": ["try:"], + "elif ": ["if "], + "else:": ["if ", "try"], + "except": ["try:"], + "finally:": ["try:"], } opening_tags = ("if ", "try:", "for ", "while ") diff --git a/evennia/utils/evform.py b/evennia/utils/evform.py index eae6ba02d..b601c8bfc 100644 --- a/evennia/utils/evform.py +++ b/evennia/utils/evform.py @@ -166,6 +166,7 @@ def _to_ansi(obj, regexable=False): else: return ANSIString(to_unicode(obj), regexable=regexable) + class EvForm(object): """ This object is instantiated with a text file and parses @@ -174,6 +175,7 @@ class EvForm(object): EvCell or Tables. """ + def __init__(self, filename=None, cells=None, tables=None, form=None, **kwargs): """ Initiate the form @@ -194,7 +196,7 @@ class EvForm(object): self.filename = filename self.input_form_dict = form - self.cells_mapping = dict((to_str(key, force_string=True), value) for key, value in cells.items()) if cells else {} + self.cells_mapping = dict((to_str(key, force_string=True), value) for key, value in cells.items()) if cells else {} self.tables_mapping = dict((to_str(key, force_string=True), value) for key, value in tables.items()) if tables else {} self.cellchar = "x" @@ -232,7 +234,7 @@ class EvForm(object): table_coords = {} # Locate the identifier tags and the horizontal end coords for all forms - re_cellchar = re.compile(r"%s+([^%s%s]+)%s+" % (cellchar, INVALID_FORMCHARS, cellchar, cellchar)) + re_cellchar = re.compile(r"%s+([^%s%s]+)%s+" % (cellchar, INVALID_FORMCHARS, cellchar, cellchar)) re_tablechar = re.compile(r"%s+([^%s%s|+])%s+" % (tablechar, INVALID_FORMCHARS, tablechar, tablechar)) for iy, line in enumerate(_to_ansi(form, regexable=True)): # find cells @@ -262,16 +264,16 @@ class EvForm(object): # scan up to find top of rectangle dy_up = 0 if iy > 0: - for i in range(1,iy): - if all(form[iy-i][ix] == cellchar for ix in range(leftix, rightix)): + for i in range(1, iy): + if all(form[iy - i][ix] == cellchar for ix in range(leftix, rightix)): dy_up += 1 else: break # find bottom edge of rectangle dy_down = 0 - if iy < nform-1: - for i in range(1,nform-iy-1): - if all(form[iy+i][ix] == cellchar for ix in range(leftix, rightix)): + if iy < nform - 1: + for i in range(1, nform - iy - 1): + if all(form[iy + i][ix] == cellchar for ix in range(leftix, rightix)): dy_down += 1 else: break @@ -284,13 +286,13 @@ class EvForm(object): # we have all the coordinates we need. Create EvCell. data = self.cells_mapping.get(key, "") - #if key == "1": + # if key == "1": - options = { "pad_left":0, "pad_right":0, "pad_top":0, "pad_bottom":0, "align":"l", "valign":"t", "enforce_size":True} + options = {"pad_left": 0, "pad_right": 0, "pad_top": 0, "pad_bottom": 0, "align": "l", "valign": "t", "enforce_size": True} options.update(custom_options) - #if key=="4": + # if key=="4": - mapping[key] = (iyup, leftix, width, height, EvCell(data, width=width, height=height,**options)) + mapping[key] = (iyup, leftix, width, height, EvCell(data, width=width, height=height, **options)) # get rectangles and assign Tables for key, (iy, leftix, rightix) in table_coords.items(): @@ -298,16 +300,16 @@ class EvForm(object): # scan up to find top of rectangle dy_up = 0 if iy > 0: - for i in range(1,iy): - if all(form[iy-i][ix] == tablechar for ix in range(leftix, rightix)): + for i in range(1, iy): + if all(form[iy - i][ix] == tablechar for ix in range(leftix, rightix)): dy_up += 1 else: break # find bottom edge of rectangle dy_down = 0 - if iy < nform-1: - for i in range(1,nform-iy-1): - if all(form[iy+i][ix] == tablechar for ix in range(leftix, rightix)): + if iy < nform - 1: + for i in range(1, nform - iy - 1): + if all(form[iy + i][ix] == tablechar for ix in range(leftix, rightix)): dy_down += 1 else: break @@ -321,8 +323,8 @@ class EvForm(object): # we have all the coordinates we need. Create Table. table = self.tables_mapping.get(key, None) - options = { "pad_left":0, "pad_right":0, "pad_top":0, "pad_bottom":0, - "align":"l", "valign":"t", "enforce_size":True} + options = {"pad_left": 0, "pad_right": 0, "pad_top": 0, "pad_bottom": 0, + "align": "l", "valign": "t", "enforce_size": True} options.update(custom_options) if table: @@ -343,9 +345,9 @@ class EvForm(object): # rect is a list of lines, each wide rect = cell_or_table.get() for il, rectline in enumerate(rect): - formline = form[iy0+il] + formline = form[iy0 + il] # insert new content, replacing old - form[iy0+il] = formline[:ix0] + rectline + formline[ix0+width:] + form[iy0 + il] = formline[:ix0] + rectline + formline[ix0 + width:] return form def map(self, cells=None, tables=None, **kwargs): @@ -366,7 +368,7 @@ class EvForm(object): kwargs.pop("width", None) kwargs.pop("height", None) - new_cells = dict((to_str(key, force_string=True), value) for key, value in cells.items()) if cells else {} + new_cells = dict((to_str(key, force_string=True), value) for key, value in cells.items()) if cells else {} new_tables = dict((to_str(key, force_string=True), value) for key, value in tables.items()) if tables else {} self.cells_mapping.update(new_cells) @@ -424,6 +426,7 @@ class EvForm(object): "prints the form" return unicode(ANSIString("\n").join([line for line in self.form])) + def _test(): "test evform. This is used by the unittest system." form = EvForm("evennia.utils.evform_test") @@ -434,18 +437,18 @@ def _test(): 3: "A sturdy fellow", 4: 12, 5: 10, - 6: 5, + 6: 5, 7: 18, 8: 10, - 9: 3}) + 9: 3}) # create the EvTables - tableA = EvTable("HP","MV","MP", - table=[["**"], ["*****"], ["***"]], - border="incols") + tableA = EvTable("HP", "MV", "MP", + table=[["**"], ["*****"], ["***"]], + border="incols") tableB = EvTable("Skill", "Value", "Exp", - table=[["Shooting", "Herbalism", "Smithing"], - [12,14,9],["550/1200", "990/1400", "205/900"]], - border="incols") + table=[["Shooting", "Herbalism", "Smithing"], + [12, 14, 9], ["550/1200", "990/1400", "205/900"]], + border="incols") # add the tables to the proper ids in the form form.map(tables={"A": tableA, "B": tableB}) diff --git a/evennia/utils/evform_test.py b/evennia/utils/evform_test.py index dfe43bf35..572a2fff5 100644 --- a/evennia/utils/evform_test.py +++ b/evennia/utils/evform_test.py @@ -28,5 +28,3 @@ FORM = """ | | | -----------`------------------------------------- """ - - diff --git a/evennia/utils/evmenu.py b/evennia/utils/evmenu.py index 37d0a4f76..523f6ec00 100644 --- a/evennia/utils/evmenu.py +++ b/evennia/utils/evmenu.py @@ -258,7 +258,7 @@ class CmdEvMenuNode(Command): if not menu: # can't restore from a session err = "Menu object not found as %s.ndb._menutree!" % orig_caller - orig_caller.msg(err) # don't give the session as a kwarg here, direct to original + orig_caller.msg(err) # don't give the session as a kwarg here, direct to original raise EvMenuError(err) # we must do this after the caller with the menui has been correctly identified since it # can be either Account, Object or Session (in the latter case this info will be superfluous). @@ -299,6 +299,7 @@ class EvMenu(object): a menufile.py instruction. """ + def __init__(self, caller, menudata, startnode="start", cmdset_mergetype="Replace", cmdset_priority=1, auto_quit=True, auto_look=True, auto_help=True, @@ -419,8 +420,8 @@ class EvMenu(object): # assign kwargs as initialization vars on ourselves. if set(("_startnode", "_menutree", "_session", "_persistent", - "cmd_on_exit", "default", "nodetext", "helptext", - "options", "cmdset_mergetype", "auto_quit")).intersection(set(kwargs.keys())): + "cmd_on_exit", "default", "nodetext", "helptext", + "options", "cmdset_mergetype", "auto_quit")).intersection(set(kwargs.keys())): raise RuntimeError("One or more of the EvMenu `**kwargs` is reserved by EvMenu for internal use.") for key, val in kwargs.iteritems(): setattr(self, key, val) @@ -686,7 +687,7 @@ class EvMenu(object): goto, execute = dic.get("goto", None), dic.get("exec", None) self.default = (goto, execute) else: - keys = list(make_iter(dic.get("key", str(inum+1).strip()))) + keys = list(make_iter(dic.get("key", str(inum + 1).strip()))) desc = dic.get("desc", dic.get("text", _ERR_NO_OPTION_DESC).strip()) goto, execute = dic.get("goto", None), dic.get("exec", None) if keys: @@ -810,14 +811,14 @@ class EvMenu(object): # add a default white color to key 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 # get the amount of rows needed (start with 4 rows) nrows = 4 while nrows * ncols < nlist: nrows += 1 - ncols = nlist // nrows # number of full columns - nlastcol = nlist % nrows # number of elements in last column + ncols = nlist // nrows # number of full columns + nlastcol = nlist % nrows # number of elements in last column # get the final column count ncols = ncols + 1 if nlastcol > 0 else ncols @@ -826,7 +827,7 @@ class EvMenu(object): table.extend([" " for i in range(nrows - nlastcol)]) # build the actual table grid - table = [table[icol * nrows : (icol * nrows) + nrows] for icol in range(0, ncols)] + table = [table[icol * nrows: (icol * nrows) + nrows] for icol in range(0, ncols)] # adjust the width of each column for icol in range(len(table)): @@ -857,7 +858,6 @@ class EvMenu(object): return separator1 + "|n" + nodetext + "|n" + separator2 + "|n" + optionstext - # ------------------------------------------------------------------------------------------------- # # Simple input shortcuts diff --git a/evennia/utils/evmore.py b/evennia/utils/evmore.py index bef35f75f..3dc936bd3 100644 --- a/evennia/utils/evmore.py +++ b/evennia/utils/evmore.py @@ -44,7 +44,7 @@ _SCREEN_HEIGHT = settings.CLIENT_DEFAULT_HEIGHT # text _DISPLAY = \ -"""{text} + """{text} (|wmore|n [{pageno}/{pagemax}] retur|wn|n|||wb|nack|||wt|nop|||we|nnd|||wq|nuit)""" @@ -82,6 +82,7 @@ class CmdMore(Command): # return or n, next more.page_next() + class CmdMoreLook(Command): """ Override look to display window and prevent OOCLook from firing @@ -89,6 +90,7 @@ class CmdMoreLook(Command): key = "look" aliases = ["l"] auto_help = False + def func(self): """ Implement the command @@ -118,8 +120,9 @@ class EvMore(object): """ The main pager object """ + def __init__(self, caller, text, always_page=False, session=None, - justify_kwargs=None, exit_on_lastpage=False, **kwargs): + justify_kwargs=None, exit_on_lastpage=False, **kwargs): """ Initialization of the text handler. @@ -159,8 +162,8 @@ class EvMore(object): self._session = session # set up individual pages for different sessions - height = max(4, session.protocol_flags.get("SCREENHEIGHT", {0:_SCREEN_HEIGHT})[0] - 4) - width = session.protocol_flags.get("SCREENWIDTH", {0:_SCREEN_WIDTH})[0] + height = max(4, session.protocol_flags.get("SCREENHEIGHT", {0: _SCREEN_HEIGHT})[0] - 4) + width = session.protocol_flags.get("SCREENWIDTH", {0: _SCREEN_WIDTH})[0] if justify_kwargs is False: # no justification. Simple division by line @@ -183,7 +186,7 @@ class EvMore(object): # always limit number of chars to 10 000 per page height = min(10000 // max(1, width), height) - self._pages = ["\n".join(lines[i:i+height]) for i in range(0, len(lines), height)] + self._pages = ["\n".join(lines[i:i + height]) for i in range(0, len(lines), height)] self._npages = len(self._pages) self._npos = 0 @@ -246,7 +249,6 @@ class EvMore(object): if self.exit_on_lastpage and self._pos == self._pos >= self._npages - 1: self.page_quit() - def page_back(self): """ Scroll the text back up, at the most to the top. @@ -283,5 +285,4 @@ def msg(caller, text="", always_page=False, session=None, justify_kwargs=None, * """ EvMore(caller, text, always_page=always_page, session=session, - justify_kwargs=justify_kwargs, **kwargs) - + justify_kwargs=justify_kwargs, **kwargs) diff --git a/evennia/utils/evtable.py b/evennia/utils/evtable.py index 6587776de..d7af6b46c 100644 --- a/evennia/utils/evtable.py +++ b/evennia/utils/evtable.py @@ -322,6 +322,7 @@ class EvCell(object): and resize as needed. """ + def __init__(self, data, **kwargs): """ Args: @@ -472,7 +473,7 @@ class EvCell(object): """ if m_len(text) > width: crop_string = self.crop_string - return text[:width-m_len(crop_string)] + crop_string + return text[:width - m_len(crop_string)] + crop_string return text def _reformat(self): @@ -661,7 +662,7 @@ class EvCell(object): left = self.border_left_char * self.border_left + ANSIString('|n') right = ANSIString('|n') + self.border_right_char * self.border_right - cwidth = self.width + self.pad_left + self.pad_right + max(0, self.border_left-1) + max(0, self.border_right-1) + cwidth = self.width + self.pad_left + self.pad_right + max(0, self.border_left - 1) + max(0, self.border_right - 1) vfill = self.corner_top_left_char if left else "" vfill += cwidth * self.border_top_char @@ -849,7 +850,7 @@ class EvCell(object): return unicode(ANSIString("\n").join(self.formatted)) -## EvColumn class +# EvColumn class class EvColumn(object): """ @@ -861,7 +862,8 @@ class EvColumn(object): incorporated into an EvTable (like EvCells) """ - def __init__(self, *args, **kwargs): + + def __init__(self, *args, **kwargs): """ Args: Text for each row in the column @@ -918,7 +920,7 @@ class EvColumn(object): self.column.extend([EvCell(data, **self.options) for data in args]) else: # insert cells before given index - ypos = min(len(self.column)-1, max(0, int(ypos))) + ypos = min(len(self.column) - 1, max(0, int(ypos))) new_cells = [EvCell(data, **self.options) for data in args] self.column = self.column[:ypos] + new_cells + self.column[ypos:] # self._balance(**kwargs) @@ -1223,7 +1225,7 @@ class EvTable(object): """ Add borders to table. This is called from self._balance. """ - nx, ny = self.ncols-1, self.nrows-1 + nx, ny = self.ncols - 1, self.nrows - 1 options = self.options for ix, col in enumerate(self.worktable): for iy, cell in enumerate(col): @@ -1254,7 +1256,7 @@ class EvTable(object): self.worktable[icol].reformat(**options) if nrow < nrowmax: # add more rows to too-short columns - empty_rows = ["" for _ in range(nrowmax-nrow)] + empty_rows = ["" for _ in range(nrowmax - nrow)] self.worktable[icol].add_rows(*empty_rows) self.ncols = ncols self.nrows = nrowmax @@ -1441,7 +1443,7 @@ class EvTable(object): self.table.append(column) else: # insert column - xpos = min(wtable-1, max(0, int(xpos))) + xpos = min(wtable - 1, max(0, int(xpos))) self.table.insert(xpos, column) self.ncols += 1 # self._balance() @@ -1491,7 +1493,7 @@ class EvTable(object): col.add_rows(row[icol], **options) else: # insert row elsewhere - ypos = min(htable-1, max(0, int(ypos))) + ypos = min(htable - 1, max(0, int(ypos))) for icol, col in enumerate(self.table): col.add_rows(row[icol], ypos=ypos, **options) self.nrows += 1 @@ -1575,9 +1577,9 @@ def _test(): print(unicode(table)) return table + def _test2(): table = EvTable("|yHeading1|n", "|B|[GHeading2|n", "Heading3") for i in range(100): table.add_row("This is col 0, row %i" % i, "|gThis is col 1, row |w%i|n|g.|n" % i, "This is col 2, row %i" % i) return table - diff --git a/evennia/utils/gametime.py b/evennia/utils/gametime.py index b2674fed5..793e34814 100644 --- a/evennia/utils/gametime.py +++ b/evennia/utils/gametime.py @@ -86,7 +86,7 @@ def server_epoch(): global _SERVER_EPOCH if not _SERVER_EPOCH: _SERVER_EPOCH = ServerConfig.objects.conf("server_epoch", default=None) \ - or time.time() - runtime() + or time.time() - runtime() return _SERVER_EPOCH @@ -224,12 +224,12 @@ def schedule(callback, repeat=False, sec=None, min=None, repeats=-1 if repeat else 1) script.db.callback = callback script.db.gametime = { - "sec": sec, - "min": min, - "hour": hour, - "day": day, - "month": month, - "year": year, + "sec": sec, + "min": min, + "hour": hour, + "day": day, + "month": month, + "year": year, } return script diff --git a/evennia/utils/idmapper/manager.py b/evennia/utils/idmapper/manager.py index e9354b343..9053149e8 100644 --- a/evennia/utils/idmapper/manager.py +++ b/evennia/utils/idmapper/manager.py @@ -3,6 +3,7 @@ IDmapper extension to the default manager. """ from django.db.models.manager import Manager + class SharedMemoryManager(Manager): # CL: this ensures our manager is used when accessing instances via # ForeignKey etc. (see docs) diff --git a/evennia/utils/idmapper/models.py b/evennia/utils/idmapper/models.py index a28e32980..fcda53cdf 100644 --- a/evennia/utils/idmapper/models.py +++ b/evennia/utils/idmapper/models.py @@ -10,7 +10,10 @@ from __future__ import absolute_import, division from builtins import object from future.utils import listitems, listvalues, with_metaclass -import os, threading, gc, time +import os +import threading +import gc +import time from weakref import WeakValueDictionary from twisted.internet.reactor import callFromThread from django.core.exceptions import ObjectDoesNotExist, FieldError @@ -22,7 +25,7 @@ from evennia.utils.utils import dbref, get_evennia_pids, to_str from .manager import SharedMemoryManager -AUTO_FLUSH_MIN_INTERVAL = 60.0 * 5 # at least 5 mins between cache flushes +AUTO_FLUSH_MIN_INTERVAL = 60.0 * 5 # at least 5 mins between cache flushes _GA = object.__getattribute__ _SA = object.__setattr__ @@ -39,9 +42,10 @@ PROC_MODIFIED_OBJS = WeakValueDictionary() # subprocess or not) _SELF_PID = os.getpid() _SERVER_PID, _PORTAL_PID = get_evennia_pids() -_IS_SUBPROCESS = (_SERVER_PID and _PORTAL_PID) and not _SELF_PID in (_SERVER_PID, _PORTAL_PID) +_IS_SUBPROCESS = (_SERVER_PID and _PORTAL_PID) and _SELF_PID not in (_SERVER_PID, _PORTAL_PID) _IS_MAIN_THREAD = threading.currentThread().getName() == "MainThread" + class SharedMemoryModelBase(ModelBase): # CL: upstream had a __new__ method that skipped ModelBase's __new__ if # SharedMemoryModelBase was not in the model class's ancestors. It's not @@ -69,7 +73,6 @@ class SharedMemoryModelBase(ModelBase): cls.cache_instance(cached_instance, new=True) return cached_instance - def _prepare(cls): """ Prepare the cache, making sure that proxies of the same db base @@ -102,7 +105,7 @@ class SharedMemoryModelBase(ModelBase): """ attrs["typename"] = cls.__name__ - attrs["path"] = "%s.%s" % (attrs["__module__"], name) + attrs["path"] = "%s.%s" % (attrs["__module__"], name) attrs["_is_deleted"] = False # set up the typeclass handling only if a variable _is_typeclass is set on the class @@ -113,14 +116,17 @@ class SharedMemoryModelBase(ModelBase): if _GA(cls, "_is_deleted"): raise ObjectDoesNotExist("Cannot access %s: Hosting object was already deleted." % fname) return _GA(cls, fieldname) + def _get_foreign(cls, fname): "Wrapper for returning foreignkey fields" if _GA(cls, "_is_deleted"): raise ObjectDoesNotExist("Cannot access %s: Hosting object was already deleted." % fname) return _GA(cls, fieldname) + def _set_nonedit(cls, fname, value): "Wrapper for blocking editing of field" raise FieldError("Field %s cannot be edited." % fname) + def _set(cls, fname, value): "Wrapper for setting database field" if _GA(cls, "_is_deleted"): @@ -130,6 +136,7 @@ class SharedMemoryModelBase(ModelBase): # primary key assigned already (won't be set when first creating object) update_fields = [fname] if _GA(cls, "_get_pk_val")(_GA(cls, "_meta")) is not None else None _GA(cls, "save")(update_fields=update_fields) + def _set_foreign(cls, fname, value): "Setter only used on foreign key relations, allows setting with #dbref" if _GA(cls, "_is_deleted"): @@ -153,9 +160,11 @@ class SharedMemoryModelBase(ModelBase): # primary key assigned already (won't be set when first creating object) update_fields = [fname] if _GA(cls, "_get_pk_val")(_GA(cls, "_meta")) is not None else None _GA(cls, "save")(update_fields=update_fields) + def _del_nonedit(cls, fname): "wrapper for not allowing deletion" raise FieldError("Field %s cannot be edited." % fname) + def _del(cls, fname): "Wrapper for clearing database field - sets it to None" _SA(cls, fname, None) @@ -163,29 +172,31 @@ class SharedMemoryModelBase(ModelBase): _GA(cls, "save")(update_fields=update_fields) # wrapper factories - fget = lambda cls: _get(cls, fieldname) + def fget(cls): return _get(cls, fieldname) if not editable: - fset = lambda cls, val: _set_nonedit(cls, fieldname, val) + def fset(cls, val): return _set_nonedit(cls, fieldname, val) elif foreignkey: - fget = lambda cls: _get_foreign(cls, fieldname) - fset = lambda cls, val: _set_foreign(cls, fieldname, val) + def fget(cls): return _get_foreign(cls, fieldname) + + def fset(cls, val): return _set_foreign(cls, fieldname, val) else: - fset = lambda cls, val: _set(cls, fieldname, val) - fdel = lambda cls: _del(cls, fieldname) if editable else _del_nonedit(cls,fieldname) + def fset(cls, val): return _set(cls, fieldname, val) + + def fdel(cls): return _del(cls, fieldname) if editable else _del_nonedit(cls, fieldname) # set docstrings for auto-doc fget.__doc__ = "A wrapper for getting database field `%s`." % fieldname fset.__doc__ = "A wrapper for setting (and saving) database field `%s`." % fieldname fdel.__doc__ = "A wrapper for deleting database field `%s`." % fieldname # assigning attrs[wrappername] = property(fget, fset, fdel) - #type(cls).__setattr__(cls, wrappername, property(fget, fset, fdel))#, doc)) + # type(cls).__setattr__(cls, wrappername, property(fget, fset, fdel))#, doc)) # exclude some models that should not auto-create wrapper fields if cls.__name__ in ("ServerConfig", "TypeNick"): return # dynamically create the wrapper properties for all fields not already handled (manytomanyfields are always handlers) for fieldname, field in ((fname, field) for fname, field in listitems(attrs) - if fname.startswith("db_") and type(field).__name__ != "ManyToManyField"): + if fname.startswith("db_") and type(field).__name__ != "ManyToManyField"): foreignkey = type(field).__name__ == "ForeignKey" wrappername = "dbid" if fieldname == "id" else fieldname.replace("db_", "", 1) if wrappername not in attrs: @@ -394,7 +405,7 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)): for fieldname in kwargs.get("update_fields")) else: # meta.fields are already field objects; get them all - new =True + new = True update_fields = self._meta.fields for field in update_fields: fieldname = field.name @@ -454,7 +465,9 @@ def flush_cache(**kwargs): cls.flush_instance_cache() # run the python garbage collector return gc.collect() -#request_finished.connect(flush_cache) + + +# request_finished.connect(flush_cache) post_migrate.connect(flush_cache) @@ -467,6 +480,8 @@ def flush_cached_instance(sender, instance, **kwargs): if not hasattr(instance, 'flush_cached_instance'): return sender.flush_cached_instance(instance, force=True) + + pre_delete.connect(flush_cached_instance) @@ -478,10 +493,14 @@ def update_cached_instance(sender, instance, **kwargs): if not hasattr(instance, 'cache_instance'): return sender.cache_instance(instance) + + post_save.connect(update_cached_instance) LAST_FLUSH = None + + def conditional_flush(max_rmem, force=False): """ Flush the cache if the estimated memory usage exceeds `max_rmem`. @@ -530,8 +549,8 @@ def conditional_flush(max_rmem, force=False): if ((now - LAST_FLUSH) < AUTO_FLUSH_MIN_INTERVAL) and not force: # too soon after last flush. - logger.log_warn("Warning: Idmapper flush called more than "\ - "once in %s min interval. Check memory usage." % (AUTO_FLUSH_MIN_INTERVAL/60.0)) + logger.log_warn("Warning: Idmapper flush called more than " + "once in %s min interval. Check memory usage." % (AUTO_FLUSH_MIN_INTERVAL / 60.0)) return if os.name == "nt": @@ -549,6 +568,7 @@ def conditional_flush(max_rmem, force=False): flush_cache() LAST_FLUSH = now + def cache_size(mb=True): """ Calculate statistics about the cache. @@ -564,8 +584,9 @@ def cache_size(mb=True): total_num, {objclass:total_num, ...} """ - numtotal = [0] # use mutable to keep reference through recursion + numtotal = [0] # use mutable to keep reference through recursion classdict = {} + def get_recurse(submodels): for submodel in submodels: subclasses = submodel.__subclasses__() diff --git a/evennia/utils/idmapper/tests.py b/evennia/utils/idmapper/tests.py index 4c22b9e40..4647b4a82 100644 --- a/evennia/utils/idmapper/tests.py +++ b/evennia/utils/idmapper/tests.py @@ -6,22 +6,27 @@ from django.test import TestCase from .models import SharedMemoryModel from django.db import models + class Category(SharedMemoryModel): name = models.CharField(max_length=32) + class RegularCategory(models.Model): name = models.CharField(max_length=32) + class Article(SharedMemoryModel): name = models.CharField(max_length=32) category = models.ForeignKey(Category) category2 = models.ForeignKey(RegularCategory) + class RegularArticle(models.Model): name = models.CharField(max_length=32) category = models.ForeignKey(Category) category2 = models.ForeignKey(RegularCategory) + class SharedMemorysTest(TestCase): # TODO: test for cross model relation (singleton to regular) @@ -58,7 +63,7 @@ class SharedMemorysTest(TestCase): #article_list = Article.objects.all().select_related('category') #last_article = article_list[0] - #for article in article_list[1:]: + # for article in article_list[1:]: # self.assertEquals(article.category2 is last_article.category2, False) # last_article = article @@ -70,5 +75,3 @@ class SharedMemorysTest(TestCase): pk = article.pk article.delete() self.assertEquals(pk not in Article.__instance_cache__, True) - - diff --git a/evennia/utils/inlinefuncs.py b/evennia/utils/inlinefuncs.py index 4c60a3dba..eba36a324 100644 --- a/evennia/utils/inlinefuncs.py +++ b/evennia/utils/inlinefuncs.py @@ -161,7 +161,7 @@ def clr(*args, **kwargs): # found. This will be overloaded by any nomatch function defined in # the imported modules. _INLINE_FUNCS = {"nomatch": lambda *args, **kwargs: "", - "stackfull": lambda *args, **kwargs: "\n (not parsed: inlinefunc stack size exceeded.)"} + "stackfull": lambda *args, **kwargs: "\n (not parsed: inlinefunc stack size exceeded.)"} # load custom inline func modules. @@ -172,7 +172,7 @@ for module in utils.make_iter(settings.INLINEFUNC_MODULES): if module == "server.conf.inlinefuncs": # a temporary warning since the default module changed name raise ImportError("Error: %s\nPossible reason: mygame/server/conf/inlinefunc.py should " - "be renamed to mygame/server/conf/inlinefuncs.py (note the S at the end)." % err) + "be renamed to mygame/server/conf/inlinefuncs.py (note the S at the end)." % err) else: raise @@ -189,7 +189,7 @@ except AttributeError: # regex definitions -_RE_STARTTOKEN = re.compile(r"(?.*?)(?(?\\'|\\"|\\\)|\\$\w+\()| # escaped tokens should re-appear in text (?P[\w\s.-\/#!%\^&\*;:=\-_`~\|\(}{\[\]]+|\"{1}|\'{1}) # everything else should also be included""", - re.UNICODE + re.IGNORECASE + re.VERBOSE + re.DOTALL) + re.UNICODE + re.IGNORECASE + re.VERBOSE + re.DOTALL) # Cache for function lookups. _PARSING_CACHE = utils.LimitedSizeOrderedDict(size_limit=1000) + class ParseStack(list): """ Custom stack that always concatenates strings together when the @@ -221,6 +222,7 @@ class ParseStack(list): string + string] """ + def __init__(self, *args, **kwargs): super(ParseStack, self).__init__(*args, **kwargs) # always start stack with the empty string @@ -247,6 +249,7 @@ class ParseStack(list): class InlinefuncError(RuntimeError): pass + def parse_inlinefunc(string, strip=False, **kwargs): """ Parse the incoming string. @@ -347,7 +350,7 @@ def parse_inlinefunc(string, strip=False, **kwargs): args.append("") else: # all other args should merge into one string - args[-1] += _run_stack(arg, depth=depth+1) + args[-1] += _run_stack(arg, depth=depth + 1) # execute the inlinefunc at this point or strip it. kwargs["inlinefunc_stack_depth"] = depth retval = "" if strip else func(*args, **kwargs) @@ -360,6 +363,7 @@ def parse_inlinefunc(string, strip=False, **kwargs): # Nick templating # + """ This supports the use of replacement templates in nicks: @@ -451,5 +455,3 @@ def parse_nick_template(string, template_regex, outtemplate): if match: return outtemplate.format(**match.groupdict()) return string - - diff --git a/evennia/utils/logger.py b/evennia/utils/logger.py index 9048c5de1..ce3bfe9a1 100644 --- a/evennia/utils/logger.py +++ b/evennia/utils/logger.py @@ -83,6 +83,8 @@ def log_trace(errmsg=None): log.msg('[EE] %s' % line) except Exception: log.msg('[EE] %s' % errmsg) + + log_tracemsg = log_trace @@ -100,6 +102,8 @@ def log_err(errmsg): errmsg = str(e) for line in errmsg.splitlines(): log.msg('[EE] %s' % line) + + # log.err('ERROR: %s' % (errmsg,)) log_errmsg = log_err @@ -118,6 +122,8 @@ def log_warn(warnmsg): warnmsg = str(e) for line in warnmsg.splitlines(): log.msg('[WW] %s' % line) + + # log.msg('WARNING: %s' % (warnmsg,)) log_warnmsg = log_warn @@ -134,6 +140,8 @@ def log_info(infomsg): infomsg = str(e) for line in infomsg.splitlines(): log.msg('[..] %s' % line) + + log_infomsg = log_info @@ -150,6 +158,8 @@ def log_dep(depmsg): depmsg = str(e) for line in depmsg.splitlines(): log.msg('[DP] %s' % line) + + log_depmsg = log_dep @@ -207,6 +217,7 @@ class EvenniaLogFile(logfile.LogFile): """ return self._file.readlines(*args, **kwargs) + _LOG_FILE_HANDLES = {} # holds open log handles @@ -307,7 +318,7 @@ def tail_log_file(filename, offset, nlines, callback=None): lines_found = filehandle.readlines() block_count -= 1 # return the right number of lines - lines_found = lines_found[-nlines-offset:-offset if offset else None] + lines_found = lines_found[-nlines - offset:-offset if offset else None] if callback: callback(lines_found) return None diff --git a/evennia/utils/picklefield.py b/evennia/utils/picklefield.py index 5adedc94e..449c6a812 100644 --- a/evennia/utils/picklefield.py +++ b/evennia/utils/picklefield.py @@ -34,7 +34,7 @@ from ast import literal_eval from copy import deepcopy from base64 import b64encode, b64decode from zlib import compress, decompress -#import six # this is actually a pypy component, not in default syslib +# import six # this is actually a pypy component, not in default syslib import django from django.core.exceptions import ValidationError from django.db import models @@ -54,12 +54,13 @@ except ImportError: # python 3.x does not have cPickle module try: - from cPickle import loads, dumps # cpython 2.x + from cPickle import loads, dumps # cpython 2.x except ImportError: - from pickle import loads, dumps # cpython 3.x, other interpreters + from pickle import loads, dumps # cpython 3.x, other interpreters DEFAULT_PROTOCOL = 2 + class PickledObject(str): """ A subclass of string so it can be told whether a string is a pickled @@ -105,12 +106,12 @@ def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL value = dumps(deepcopy(value), protocol=pickle_protocol) if compress_object: value = compress(value) - value = b64encode(value).decode() # decode bytes to str + value = b64encode(value).decode() # decode bytes to str return PickledObject(value) def dbsafe_decode(value, compress_object=False): - value = value.encode() # encode str to bytes + value = value.encode() # encode str to bytes value = b64decode(value) if compress_object: value = decompress(value) @@ -198,7 +199,7 @@ class PickledObjectField(models.Field): # If the field doesn't have a default, then we punt to models.Field. return super(PickledObjectField, self).get_default() - #def to_python(self, value): + # def to_python(self, value): def from_db_value(self, value, *args): """ B64decode and unpickle the object, optionally decompressing it. diff --git a/evennia/utils/prettytable.py b/evennia/utils/prettytable.py index 7e0a9a8a9..c13e07797 100644 --- a/evennia/utils/prettytable.py +++ b/evennia/utils/prettytable.py @@ -78,6 +78,7 @@ _re = re.compile("\033\[[0-9;]*m") def _ansi(method): """decorator for converting ansi in input""" + def wrapper(self, *args, **kwargs): def convert(inp): if isinstance(inp, basestring): @@ -110,7 +111,6 @@ class PrettyTable(object): @_ansi def __init__(self, field_names=None, **kwargs): - """Return a new PrettyTable instance Arguments: @@ -223,15 +223,15 @@ class PrettyTable(object): # Uneven padding # Put more space on right if text is of odd length... if _str_block_width(text) % 2: - return (excess//2)*" " + text + (excess//2 + 1)*" " + return (excess // 2) * " " + text + (excess // 2 + 1) * " " # and more space on left if text is of even length else: - return (excess//2 + 1)*" " + text + (excess//2)*" " + return (excess // 2 + 1) * " " + text + (excess // 2) * " " # Why distribute extra space this way? To match the behaviour of # the inbuilt str.center() method. else: # Equal padding on either side - return (excess//2)*" " + text + (excess//2)*" " + return (excess // 2) * " " + text + (excess // 2) * " " def __getattr__(self, name): @@ -252,7 +252,7 @@ class PrettyTable(object): new = PrettyTable() new.field_names = self.field_names for attr in self._options: - setattr(new, "_"+attr, getattr(self, "_"+attr)) + setattr(new, "_" + attr, getattr(self, "_" + attr)) setattr(new, "_align", getattr(self, "_align")) if isinstance(index, slice): for row in self._rows[index]: @@ -823,7 +823,7 @@ class PrettyTable(object): self._validate_option(option, kwargs[option]) options[option] = kwargs[option] else: - options[option] = getattr(self, "_"+option) + options[option] = getattr(self, "_" + option) return options ############################## @@ -893,7 +893,6 @@ class PrettyTable(object): @_ansi def add_row(self, row): - """Add a row to the table Arguments: @@ -905,24 +904,22 @@ class PrettyTable(object): raise Exception("Row has incorrect number of values, (actual) %d!=%d (expected)" % (len(row), len(self._field_names))) if not self._field_names: - self.field_names = [("Field %d" % (n+1)) for n in range(0, len(row))] + self.field_names = [("Field %d" % (n + 1)) for n in range(0, len(row))] self._rows.append(list(row)) def del_row(self, row_index): - """Delete a row to the table Arguments: row_index - The index of the row you want to delete. Indexing starts at 0.""" - if row_index > len(self._rows)-1: + if row_index > len(self._rows) - 1: raise Exception("Can't delete row at index %d, table only has %d rows!" % (row_index, len(self._rows))) del self._rows[row_index] @_ansi def add_column(self, fieldname, column, align="l", valign="t"): - """Add a column to the table. Arguments: @@ -940,20 +937,18 @@ class PrettyTable(object): self._align[fieldname] = align self._valign[fieldname] = valign for i in range(0, len(column)): - if len(self._rows) < i+1: + if len(self._rows) < i + 1: self._rows.append([]) self._rows[i].append(column[i]) else: raise Exception("Column length %d does not match number of rows %d!" % (len(column), len(self._rows))) def clear_rows(self): - """Delete all rows from the table but keep the current field names""" self._rows = [] def clear(self): - """Delete all rows and field names from the table, maintaining nothing but styling options""" self._rows = [] @@ -1017,7 +1012,7 @@ class PrettyTable(object): if options["sortby"]: sortindex = self._field_names.index(options["sortby"]) # Decorate - rows = [[row[sortindex]]+row for row in rows] + rows = [[row[sortindex]] + row for row in rows] # Sort rows.sort(reverse=options["reversesort"], key=options["sort_key"]) # Undecorate @@ -1035,7 +1030,6 @@ class PrettyTable(object): ############################## def get_string(self, **kwargs): - """Return string representation of table in current state. Arguments: @@ -1106,12 +1100,12 @@ class PrettyTable(object): bits = [options["horizontal_char"]] # For tables with no data or fieldnames if not self._field_names: - bits.append(options["junction_char"]) - return "".join(bits) + bits.append(options["junction_char"]) + return "".join(bits) for field, width in zip(self._field_names, self._widths): if options["fields"] and field not in options["fields"]: continue - bits.append((width+lpad+rpad)*options["horizontal_char"]) + bits.append((width + lpad + rpad) * options["horizontal_char"]) if options['vrules'] == ALL: bits.append(options["junction_char"]) else: @@ -1232,8 +1226,8 @@ class PrettyTable(object): bits[y].append(options["vertical_char"]) if options["border"] and options["hrules"] == ALL: - bits[row_height-1].append("\n") - bits[row_height-1].append(self._hrule) + bits[row_height - 1].append("\n") + bits[row_height - 1].append(self._hrule) for y in range(0, row_height): bits[y] = "".join(bits[y]) @@ -1245,7 +1239,6 @@ class PrettyTable(object): ############################## def get_html_string(self, **kwargs): - """Return string representation of HTML formatted version of table in current state. Arguments: @@ -1515,7 +1508,7 @@ class TableHandler(HTMLParser): iterates over the row and make each field unique """ for i in range(0, len(fields)): - for j in range(i+1, len(fields)): + for j in range(i + 1, len(fields)): if fields[i] == fields[j]: fields[j] += "'" @@ -1566,5 +1559,6 @@ def main(): x.add_row(["Perth", 5386, 1554769, 869.4]) print(x) + if __name__ == "__main__": main() diff --git a/evennia/utils/search.py b/evennia/utils/search.py index c058d5acd..74634da5b 100644 --- a/evennia/utils/search.py +++ b/evennia/utils/search.py @@ -212,6 +212,7 @@ def search_script_attribute(key=None, category=None, value=None, strvalue=None): def search_channel_attribute(key=None, category=None, value=None, strvalue=None): return Channel.objects.get_by_attribute(key=key, category=category, value=value, strvalue=strvalue) + # search for attribute objects search_attribute_object = ObjectDB.objects.get_attribute @@ -243,6 +244,8 @@ def search_object_by_tag(key=None, category=None): """ return ObjectDB.objects.get_by_tag(key=key, category=category) + + search_tag = search_object_by_tag # this is the most common case @@ -302,5 +305,6 @@ def search_channel_tag(key=None, category=None): """ return Channel.objects.get_by_tag(key=key, category=category) + # search for tag objects (not the objects they are attached to search_tag_object = ObjectDB.objects.get_tag diff --git a/evennia/utils/spawner.py b/evennia/utils/spawner.py index 965819ca7..423fc8225 100644 --- a/evennia/utils/spawner.py +++ b/evennia/utils/spawner.py @@ -307,35 +307,35 @@ if __name__ == "__main__": # testing protparents = { - "NOBODY": {}, - # "INFINITE" : { - # "prototype":"INFINITE" - # }, - "GOBLIN": { - "key": "goblin grunt", - "health": lambda: randint(20, 30), - "resists": ["cold", "poison"], - "attacks": ["fists"], - "weaknesses": ["fire", "light"] - }, - "GOBLIN_WIZARD": { - "prototype": "GOBLIN", - "key": "goblin wizard", - "spells": ["fire ball", "lighting bolt"] - }, - "GOBLIN_ARCHER": { - "prototype": "GOBLIN", - "key": "goblin archer", - "attacks": ["short bow"] - }, - "ARCHWIZARD": { - "attacks": ["archwizard staff"], - }, - "GOBLIN_ARCHWIZARD": { - "key": "goblin archwizard", - "prototype": ("GOBLIN_WIZARD", "ARCHWIZARD") - } + "NOBODY": {}, + # "INFINITE" : { + # "prototype":"INFINITE" + # }, + "GOBLIN": { + "key": "goblin grunt", + "health": lambda: randint(20, 30), + "resists": ["cold", "poison"], + "attacks": ["fists"], + "weaknesses": ["fire", "light"] + }, + "GOBLIN_WIZARD": { + "prototype": "GOBLIN", + "key": "goblin wizard", + "spells": ["fire ball", "lighting bolt"] + }, + "GOBLIN_ARCHER": { + "prototype": "GOBLIN", + "key": "goblin archer", + "attacks": ["short bow"] + }, + "ARCHWIZARD": { + "attacks": ["archwizard staff"], + }, + "GOBLIN_ARCHWIZARD": { + "key": "goblin archwizard", + "prototype": ("GOBLIN_WIZARD", "ARCHWIZARD") } + } # test print([o.key for o in spawn(protparents["GOBLIN"], protparents["GOBLIN_ARCHWIZARD"], diff --git a/evennia/utils/test_resources.py b/evennia/utils/test_resources.py index 05a15df67..47716e979 100644 --- a/evennia/utils/test_resources.py +++ b/evennia/utils/test_resources.py @@ -53,8 +53,8 @@ class EvenniaTest(TestCase): dummysession = ServerSession() dummysession.init_session("telnet", ("localhost", "testmode"), SESSIONS) dummysession.sessid = 1 - SESSIONS.portal_connect(dummysession.get_sync_data()) # note that this creates a new Session! - session = SESSIONS.session_from_sessid(1) # the real session + SESSIONS.portal_connect(dummysession.get_sync_data()) # note that this creates a new Session! + session = SESSIONS.session_from_sessid(1) # the real session SESSIONS.login(session, self.account, testmode=True) self.session = session diff --git a/evennia/utils/tests.py b/evennia/utils/tests.py index 8636ff19a..78ceb45a7 100644 --- a/evennia/utils/tests.py +++ b/evennia/utils/tests.py @@ -12,6 +12,7 @@ from evennia import utils from django.conf import settings + class ANSIStringTestCase(TestCase): def checker(self, ansi, raw, clean): """ @@ -209,6 +210,7 @@ class TestListToString(TestCase): with addquote and endsep [1,2,3] -> '"1", "2" and "3"' """ + def test_list_to_string(self): self.assertEqual('1, 2, 3', utils.list_to_string([1, 2, 3], endsep="")) self.assertEqual('"1", "2", "3"', utils.list_to_string([1, 2, 3], endsep="", addquote=True)) @@ -221,6 +223,7 @@ class TestMLen(TestCase): Verifies that m_len behaves like len in all situations except those where MXP may be involved. """ + def test_non_mxp_string(self): self.assertEqual(utils.m_len('Test_string'), 11) @@ -325,15 +328,19 @@ class TestTextToHTMLparser(TestCase): '' 'http://example.com/') + from evennia.utils import evmenu from mock import Mock + + class TestEvMenu(TestCase): "Run the EvMenu test." + def setUp(self): self.caller = Mock() self.caller.msg = Mock() self.menu = evmenu.EvMenu(self.caller, "evennia.utils.evmenu", startnode="test_start_node", - persistent=True, cmdset_mergetype="Replace", testval="val", testval2="val2") + persistent=True, cmdset_mergetype="Replace", testval="val", testval2="val2") def test_kwargsave(self): self.assertTrue(hasattr(self.menu, "testval")) @@ -345,6 +352,7 @@ from evennia.utils import inlinefuncs class TestInlineFuncs(TestCase): """Test the nested inlinefunc module""" + def test_nofunc(self): self.assertEqual(inlinefuncs.parse_inlinefunc( "as$382ewrw w we w werw,|44943}"), @@ -375,6 +383,7 @@ class TestInlineFuncs(TestCase): 'this should be $pad("""escaped,""" and """instead,""" cropped $crop(with a long,5) text., 80)'), "this should be escaped, and instead, cropped with text. ") + from evennia.utils import evform @@ -424,7 +433,8 @@ class TestEvForm(TestCase): def test_ansi_escape(self): # note that in a msg() call, the result would be the correct |-----, # in a print, ansi only gets called once, so ||----- is the result - self.assertEqual(unicode(evform.EvForm(form={"FORM":"\n||-----"})), "||-----") + self.assertEqual(unicode(evform.EvForm(form={"FORM": "\n||-----"})), "||-----") + class TestTimeformat(TestCase): """ @@ -472,11 +482,11 @@ class TestTimeformat(TestCase): self.assertEqual(utils.time_format(3725, 2), "1 hour, 2 minutes") self.assertEqual(utils.time_format(86350, 2), "23 hours, 59 minutes") self.assertEqual(utils.time_format(86800, 2), - "1 day, 0 hours, 6 minutes") + "1 day, 0 hours, 6 minutes") self.assertEqual(utils.time_format(130800, 2), - "1 day, 12 hours, 20 minutes") + "1 day, 12 hours, 20 minutes") self.assertEqual(utils.time_format(530800, 2), - "6 days, 3 hours, 26 minutes") + "6 days, 3 hours, 26 minutes") def test_style_3(self): """Test the style 3 of time_format.""" @@ -486,17 +496,17 @@ class TestTimeformat(TestCase): self.assertEqual(utils.time_format(300, 3), "5 minutes 0 seconds") self.assertEqual(utils.time_format(660, 3), "11 minutes 0 seconds") self.assertEqual(utils.time_format(3600, 3), - "1 hour, 0 minutes") + "1 hour, 0 minutes") self.assertEqual(utils.time_format(3725, 3), - "1 hour, 2 minutes 5 seconds") + "1 hour, 2 minutes 5 seconds") self.assertEqual(utils.time_format(86350, 3), - "23 hours, 59 minutes 10 seconds") + "23 hours, 59 minutes 10 seconds") self.assertEqual(utils.time_format(86800, 3), - "1 day, 0 hours, 6 minutes 40 seconds") + "1 day, 0 hours, 6 minutes 40 seconds") self.assertEqual(utils.time_format(130800, 3), - "1 day, 12 hours, 20 minutes 0 seconds") + "1 day, 12 hours, 20 minutes 0 seconds") self.assertEqual(utils.time_format(530800, 3), - "6 days, 3 hours, 26 minutes 40 seconds") + "6 days, 3 hours, 26 minutes 40 seconds") def test_style_4(self): """Test the style 4 of time_format.""" diff --git a/evennia/utils/text2html.py b/evennia/utils/text2html.py index f04382b20..94c558753 100644 --- a/evennia/utils/text2html.py +++ b/evennia/utils/text2html.py @@ -30,48 +30,48 @@ class TextToHTMLparser(object): tabstop = 4 # mapping html color name <-> ansi code. hilite = ANSI_HILITE - unhilite = ANSI_UNHILITE # this will be stripped - there is no css equivalent. - normal = ANSI_NORMAL # " + unhilite = ANSI_UNHILITE # this will be stripped - there is no css equivalent. + normal = ANSI_NORMAL # " underline = ANSI_UNDERLINE blink = ANSI_BLINK inverse = ANSI_INVERSE # this will produce an outline; no obvious css equivalent? colorcodes = [ - ('color-000', unhilite + ANSI_BLACK), # pure black - ('color-001', unhilite + ANSI_RED), - ('color-002', unhilite + ANSI_GREEN), - ('color-003', unhilite + ANSI_YELLOW), - ('color-004', unhilite + ANSI_BLUE), - ('color-005', unhilite + ANSI_MAGENTA), - ('color-006', unhilite + ANSI_CYAN), - ('color-007', unhilite + ANSI_WHITE), # light grey - ('color-008', hilite + ANSI_BLACK), # dark grey - ('color-009', hilite + ANSI_RED), - ('color-010', hilite + ANSI_GREEN), - ('color-011', hilite + ANSI_YELLOW), - ('color-012', hilite + ANSI_BLUE), - ('color-013', hilite + ANSI_MAGENTA), - ('color-014', hilite + ANSI_CYAN), - ('color-015', hilite + ANSI_WHITE) # pure white - ] + [("color-%03i" % (i+16), XTERM256_FG % ("%i" % (i+16))) for i in xrange(240)] + ('color-000', unhilite + ANSI_BLACK), # pure black + ('color-001', unhilite + ANSI_RED), + ('color-002', unhilite + ANSI_GREEN), + ('color-003', unhilite + ANSI_YELLOW), + ('color-004', unhilite + ANSI_BLUE), + ('color-005', unhilite + ANSI_MAGENTA), + ('color-006', unhilite + ANSI_CYAN), + ('color-007', unhilite + ANSI_WHITE), # light grey + ('color-008', hilite + ANSI_BLACK), # dark grey + ('color-009', hilite + ANSI_RED), + ('color-010', hilite + ANSI_GREEN), + ('color-011', hilite + ANSI_YELLOW), + ('color-012', hilite + ANSI_BLUE), + ('color-013', hilite + ANSI_MAGENTA), + ('color-014', hilite + ANSI_CYAN), + ('color-015', hilite + ANSI_WHITE) # pure white + ] + [("color-%03i" % (i + 16), XTERM256_FG % ("%i" % (i + 16))) for i in xrange(240)] colorback = [ - ('bgcolor-000', ANSI_BACK_BLACK), # pure black - ('bgcolor-001', ANSI_BACK_RED), - ('bgcolor-002', ANSI_BACK_GREEN), - ('bgcolor-003', ANSI_BACK_YELLOW), - ('bgcolor-004', ANSI_BACK_BLUE), - ('bgcolor-005', ANSI_BACK_MAGENTA), - ('bgcolor-006', ANSI_BACK_CYAN), - ('bgcolor-007', ANSI_BACK_WHITE), # light grey - ('bgcolor-008', hilite + ANSI_BACK_BLACK), # dark grey - ('bgcolor-009', hilite + ANSI_BACK_RED), - ('bgcolor-010', hilite + ANSI_BACK_GREEN), - ('bgcolor-011', hilite + ANSI_BACK_YELLOW), - ('bgcolor-012', hilite + ANSI_BACK_BLUE), - ('bgcolor-013', hilite + ANSI_BACK_MAGENTA), - ('bgcolor-014', hilite + ANSI_BACK_CYAN), - ('bgcolor-015', hilite + ANSI_BACK_WHITE), # pure white - ] + [("bgcolor-%03i" % (i+16), XTERM256_BG % ("%i" % (i+16))) for i in range(240)] + ('bgcolor-000', ANSI_BACK_BLACK), # pure black + ('bgcolor-001', ANSI_BACK_RED), + ('bgcolor-002', ANSI_BACK_GREEN), + ('bgcolor-003', ANSI_BACK_YELLOW), + ('bgcolor-004', ANSI_BACK_BLUE), + ('bgcolor-005', ANSI_BACK_MAGENTA), + ('bgcolor-006', ANSI_BACK_CYAN), + ('bgcolor-007', ANSI_BACK_WHITE), # light grey + ('bgcolor-008', hilite + ANSI_BACK_BLACK), # dark grey + ('bgcolor-009', hilite + ANSI_BACK_RED), + ('bgcolor-010', hilite + ANSI_BACK_GREEN), + ('bgcolor-011', hilite + ANSI_BACK_YELLOW), + ('bgcolor-012', hilite + ANSI_BACK_BLUE), + ('bgcolor-013', hilite + ANSI_BACK_MAGENTA), + ('bgcolor-014', hilite + ANSI_BACK_CYAN), + ('bgcolor-015', hilite + ANSI_BACK_WHITE), # pure white + ] + [("bgcolor-%03i" % (i + 16), XTERM256_BG % ("%i" % (i + 16))) for i in range(240)] # make sure to escape [ #colorcodes = [(c, code.replace("[", r"\[")) for c, code in colorcodes] @@ -80,8 +80,8 @@ class TextToHTMLparser(object): bg_colormap = dict((code, clr) for clr, code in colorback) # create stop markers - fgstop = "(?:\033\[1m|\033\[22m)*\033\[3[0-8].*?m|\033\[0m|$" - bgstop = "(?:\033\[1m|\033\[22m)*\033\[4[0-8].*?m|\033\[0m|$" + fgstop = "(?:\033\[1m|\033\[22m)*\033\[3[0-8].*?m|\033\[0m|$" + bgstop = "(?:\033\[1m|\033\[22m)*\033\[4[0-8].*?m|\033\[0m|$" # extract color markers, tagging the start marker and the text marked re_fgs = re.compile("((?:\033\[1m|\033\[22m)*\033\[3[0-8].*?m)(.*?)(?=" + fgstop + ")") @@ -93,9 +93,9 @@ class TextToHTMLparser(object): re_uline = re.compile("(?:%s)(.*?)(?=%s|%s)" % (underline.replace("[", r"\["), fgstop, bgstop)) re_blink = re.compile("(?:%s)(.*?)(?=%s|%s)" % (blink.replace("[", r"\["), fgstop, bgstop)) re_inverse = re.compile("(?:%s)(.*?)(?=%s|%s)" % (inverse.replace("[", r"\["), fgstop, bgstop)) - re_string = re.compile(r'(?P[<&>])|(?P [ \t]+)|(?P^ )|(?P\r\n|\r|\n)', re.S|re.M|re.I) + re_string = re.compile(r'(?P[<&>])|(?P [ \t]+)|(?P^ )|(?P\r\n|\r|\n)', re.S | re.M | re.I) re_url = re.compile(r'((?:ftp|www|https?)\W+(?:(?!\.(?:\s|$)|&\w+;)[^"\',;$*^\\(){}<>\[\]\s])+)(\.(?:\s|$)|&\w+;|)') - re_mxplink = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) + re_mxplink = re.compile(r'\|lc(.*?)\|lt(.*?)\|le', re.DOTALL) def _sub_fg(self, colormatch): code, text = colormatch.groups() @@ -135,7 +135,7 @@ class TextToHTMLparser(object): """ text = self.re_hilite.sub(r'\1', text) - return self.re_unhilite.sub(r'\1', text) # strip unhilite - there is no equivalent in css. + return self.re_unhilite.sub(r'\1', text) # strip unhilite - there is no equivalent in css. def re_underline(self, text): """ @@ -245,9 +245,9 @@ class TextToHTMLparser(object): """ cmd, text = [grp.replace('\"', "\\"") for grp in match.groups()] - val = r'''{text}'''.format(cmd=cmd, text=text) + val = r'''{text}'''.format(cmd=cmd, text=text) return val def sub_text(self, match): @@ -306,6 +306,7 @@ class TextToHTMLparser(object): return result + HTML_PARSER = TextToHTMLparser() diff --git a/evennia/utils/txws.py b/evennia/utils/txws.py index cf5f45744..66be721ae 100644 --- a/evennia/utils/txws.py +++ b/evennia/utils/txws.py @@ -36,6 +36,7 @@ from twisted.protocols.policies import ProtocolWrapper, WrappingFactory from twisted.python import log from twisted.web.http import datetimeToString + class WSException(Exception): """ Something stupid happened here. @@ -54,6 +55,7 @@ class WSException(Exception): # RFC6455 - RFC 6455. The official WebSocket protocol standard. The protocol # number is 13, but otherwise it is identical to HyBi-07. + HYBI00, HYBI07, HYBI10, RFC6455 = list(range(4)) # States of the state machine. Because there are no reliable byte counts for @@ -87,6 +89,7 @@ decoders = { # Fake HTTP stuff, and a couple convenience methods for examining fake HTTP # headers. + def http_headers(s): """ Create a dictionary of data from raw HTTP headers. @@ -104,13 +107,15 @@ def http_headers(s): return d + def is_websocket(headers): """ Determine whether a given set of headers is asking for WebSockets. """ - return ("upgrade" in headers.get("Connection", "").lower() - and headers.get("Upgrade").lower() == "websocket") + return ("upgrade" in headers.get("Connection", "").lower() and + headers.get("Upgrade").lower() == "websocket") + def is_hybi00(headers): """ @@ -124,6 +129,7 @@ def is_hybi00(headers): # Authentication for WS. + def complete_hybi00(headers, challenge): """ Generate the response for a HyBi-00 challenge. @@ -139,6 +145,7 @@ def complete_hybi00(headers, challenge): return md5(nonce).digest() + def make_accept(key): """ Create an "accept" response for a given key. @@ -154,6 +161,7 @@ def make_accept(key): # Separated out to make unit testing a lot easier. # Frames are bonghits in newer WS versions, so helpers are appreciated. + def make_hybi00_frame(buf): """ Make a HyBi-00 frame from some data. @@ -164,6 +172,7 @@ def make_hybi00_frame(buf): return "\x00%s\xff" % buf + def parse_hybi00_frames(buf): """ Parse HyBi-00 frames, returning unwrapped frames and any unmatched data. @@ -192,6 +201,7 @@ def parse_hybi00_frames(buf): buf = buf[tail:] return frames, buf + def mask(buf, key): """ Mask or unmask a buffer of bytes with a masking key. @@ -206,6 +216,7 @@ def mask(buf, key): buf[i] = chr(ord(char) ^ key[i % 4]) return "".join(buf) + def make_hybi07_frame(buf, opcode=0x1): """ Make a HyBi-07 frame. @@ -226,6 +237,7 @@ def make_hybi07_frame(buf, opcode=0x1): frame = "%s%s%s" % (header, length, buf) return frame + def make_hybi07_frame_dwim(buf): """ Make a HyBi-07 frame with binary or text data according to the type of buf. @@ -239,6 +251,7 @@ def make_hybi07_frame_dwim(buf): else: raise TypeError("In binary support mode, frame data must be either str or unicode") + def parse_hybi07_frames(buf): """ Parse HyBi-07 frames in a highly compliant manner. @@ -259,7 +272,7 @@ def parse_hybi07_frames(buf): # At least one of the reserved flags is set. Pork chop sandwiches! raise WSException("Reserved flag in HyBi-07 frame (%d)" % header) #frames.append(("", CLOSE)) - #return frames, buf + # return frames, buf # Get the opcode, and translate it to a local enum which we actually # care about. @@ -329,6 +342,7 @@ def parse_hybi07_frames(buf): return frames, buf[start:] + class WebSocketProtocol(ProtocolWrapper): """ Protocol which wraps another protocol to provide a WebSockets transport @@ -538,7 +552,7 @@ class WebSocketProtocol(ProtocolWrapper): log.msg("Can't support protocol version %s!" % version) return False - self.validationMade() # custom Evennia addition + self.validationMade() # custom Evennia addition return True def dataReceived(self, data): @@ -639,6 +653,7 @@ class WebSocketProtocol(ProtocolWrapper): self.loseConnection() + class WebSocketFactory(WrappingFactory): """ Factory which wraps another factory to provide WebSockets transports for diff --git a/evennia/utils/utils.py b/evennia/utils/utils.py index 458bee38c..daa5379fb 100644 --- a/evennia/utils/utils.py +++ b/evennia/utils/utils.py @@ -44,6 +44,7 @@ _DA = object.__delattr__ _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH + def is_iter(iterable): """ Checks if an object behaves iterably. @@ -96,6 +97,8 @@ def wrap(text, width=_DEFAULT_WIDTH, indent=0): text = to_unicode(text) indent = " " * indent return to_str(textwrap.fill(text, width, initial_indent=indent, subsequent_indent=indent)) + + # alias - fill fill = wrap @@ -437,27 +440,27 @@ def time_format(seconds, style=0): """ Only return the highest unit. """ - if days >= 730: # Several years + if days >= 730: # Several years return "{} years".format(days // 365) - elif days >= 365: # One year + elif days >= 365: # One year return "a year" - elif days >= 62: # Several months + elif days >= 62: # Several months return "{} months".format(days // 31) - elif days >= 31: # One month + elif days >= 31: # One month return "a month" - elif days >= 2: # Several days + elif days >= 2: # Several days return "{} days".format(days) elif days > 0: return "a day" - elif hours >= 2: # Several hours + elif hours >= 2: # Several hours return "{} hours".format(hours) - elif hours > 0: # One hour + elif hours > 0: # One hour return "an hour" - elif minutes >= 2: # Several minutes + elif minutes >= 2: # Several minutes return "{} minutes".format(minutes) - elif minutes > 0: # One minute + elif minutes > 0: # One minute return "a minute" - elif seconds >= 2: # Several seconds + elif seconds >= 2: # Several seconds return "{} seconds".format(seconds) elif seconds == 1: return "a second" @@ -593,9 +596,9 @@ def dbref(inp, reqhash=True): """ if reqhash: num = (int(inp.lstrip('#')) if (isinstance(inp, basestring) and - inp.startswith("#") and - inp.lstrip('#').isdigit()) - else None) + inp.startswith("#") and + inp.lstrip('#').isdigit()) + else None) return num if num > 0 else None elif isinstance(inp, basestring): inp = inp.lstrip('#') @@ -641,6 +644,7 @@ def dbref_to_obj(inp, objclass, raise_errors=True): raise return inp + # legacy alias dbid_to_obj = dbref_to_obj @@ -788,7 +792,7 @@ def to_str(obj, encoding='utf-8', force_string=False): # if we get to this point we have not found any way to convert this string. Try to parse it manually, try: return latinify(obj, '?') - except Exception, err: + except Exception as err: raise Exception("%s, Error: Unicode could not encode unicode string '%s'(%s) to a bytestring. " % (err, obj, encoding)) return obj @@ -921,6 +925,7 @@ def uses_database(name="sqlite3"): _TASK_HANDLER = None + def delay(timedelay, callback, *args, **kwargs): """ Delay the return of a value. @@ -989,7 +994,7 @@ def clean_object_caches(obj): # on-object property cache [_DA(obj, cname) for cname in viewkeys(obj.__dict__) - if cname.startswith("_cached_db_")] + if cname.startswith("_cached_db_")] try: hashid = _GA(obj, "hashid") _TYPECLASSMODELS._ATTRIBUTE_CACHE[hashid] = {} @@ -1001,6 +1006,8 @@ def clean_object_caches(obj): _PPOOL = None _PCMD = None _PROC_ERR = "A process has ended with a probable error condition: process ended by signal 9." + + def run_async(to_execute, *args, **kwargs): """ Runs a function or executes a code snippet asynchronously. @@ -1094,7 +1101,7 @@ def check_evennia_dependencies(): errstring = errstring.strip() if errstring: mlen = max(len(line) for line in errstring.split("\n")) - logger.log_err("%s\n%s\n%s" % ("-"*mlen, errstring, '-'*mlen)) + logger.log_err("%s\n%s\n%s" % ("-" * mlen, errstring, '-' * mlen)) return not_error @@ -1333,7 +1340,7 @@ def fuzzy_import_from_module(path, variable, default=None, defaultpaths=None): try: mod = import_module(modpath) except ImportError as ex: - if not str(ex).startswith ("No module named %s" % modpath): + if not str(ex).startswith("No module named %s" % modpath): # this means the module was found but it # triggers an ImportError on import. raise ex @@ -1396,6 +1403,8 @@ def class_from_module(path, defaultpaths=None): err += "." raise ImportError(err) return cls + + # alias object_from_module = class_from_module @@ -1430,7 +1439,7 @@ def string_similarity(string1, string2): vec2 = [string2.count(v) for v in vocabulary] try: 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))) + (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. @@ -1456,9 +1465,9 @@ def string_suggestions(string, vocabulary, cutoff=0.6, maxnum=3): """ 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] + 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): @@ -1589,6 +1598,7 @@ def get_evennia_pids(): return int(server_pid), int(portal_pid) return None, None + from gc import get_referents from sys import getsizeof @@ -1622,12 +1632,13 @@ def deepsize(obj, max_depth=4): idr = id(ref) if idr not in dct: dct[idr] = (ref, getsizeof(ref, default=0)) - _recurse(ref, dct, depth+1) + _recurse(ref, dct, depth + 1) sizedict = {} _recurse(obj, sizedict, 0) size = getsizeof(obj) + sum([p[1] for p in sizedict.values()]) return size + # lazy load handler _missing = object() @@ -1651,6 +1662,7 @@ class lazy_property(object): property "attributes" on the object. """ + def __init__(self, func, name=None, doc=None): """Store all properties for now""" self.__name__ = name or func.__name__ @@ -1668,6 +1680,7 @@ class lazy_property(object): obj.__dict__[self.__name__] = value return value + _STRIP_ANSI = None _RE_CONTROL_CHAR = re.compile('[%s]' % re.escape(''.join([unichr(c) for c in range(0, 32)]))) # + range(127,160)]))) @@ -1707,7 +1720,7 @@ def calledby(callerdepth=1): stack = inspect.stack() # we must step one extra level back in stack since we don't want # to include the call of this function itself. - callerdepth = min(max(2, callerdepth + 1), len(stack)-1) + callerdepth = min(max(2, callerdepth + 1), len(stack) - 1) frame = inspect.stack()[callerdepth] path = os.path.sep.join(frame[1].rsplit(os.path.sep, 2)[-2:]) return "[called by '%s': %s:%s %s]" % (frame[3], path, frame[2], frame[4]) @@ -1774,15 +1787,15 @@ def at_search_result(matches, caller, query="", quiet=False, **kwargs): matches = None elif len(matches) > 1: error = kwargs.get("multimatch_string") or \ - _("More than one match for '%s' (please narrow target):\n" % query) + _("More than one match for '%s' (please narrow target):\n" % query) for num, result in enumerate(matches): # we need to consider Commands, where .aliases is a list aliases = result.aliases.all() if hasattr(result.aliases, "all") else result.aliases error += _MULTIMATCH_TEMPLATE.format( - number=num + 1, - name=result.get_display_name(caller) if hasattr(result, "get_display_name") else query, - aliases=" [%s]" % ";".join(aliases) if aliases else "", - info=result.get_extra_info(caller)) + number=num + 1, + name=result.get_display_name(caller) if hasattr(result, "get_display_name") else query, + aliases=" [%s]" % ";".join(aliases) if aliases else "", + info=result.get_extra_info(caller)) matches = None else: # exactly one match @@ -1800,6 +1813,7 @@ class LimitedSizeOrderedDict(OrderedDict): grow out of bounds. """ + def __init__(self, *args, **kwargs): """ Limited-size ordered dict. @@ -1814,7 +1828,7 @@ class LimitedSizeOrderedDict(OrderedDict): """ super(LimitedSizeOrderedDict, self).__init__() self.size_limit = kwargs.get("size_limit", None) - self.filo = not kwargs.get("fifo", True) # FIFO inverse of FILO + self.filo = not kwargs.get("fifo", True) # FIFO inverse of FILO self._check_size() def _check_size(self): @@ -1850,7 +1864,3 @@ def get_game_dir_path(): else: os.chdir(os.pardir) raise RuntimeError("server/conf/settings.py not found: Must start from inside game dir.") - - - - diff --git a/evennia/web/__init__.py b/evennia/web/__init__.py index 03578813e..b81aa5093 100644 --- a/evennia/web/__init__.py +++ b/evennia/web/__init__.py @@ -4,4 +4,3 @@ Django to relate the database contents to web pages. Also the basic webclient and the website are defined in here (the webserver itself is found under the `server` package). """ - diff --git a/evennia/web/urls.py b/evennia/web/urls.py index 8539ae82b..332c96903 100644 --- a/evennia/web/urls.py +++ b/evennia/web/urls.py @@ -14,11 +14,11 @@ from django.views.generic import RedirectView urlpatterns = [ # Front page (note that we shouldn't specify namespace here since we will # not be able to load django-auth/admin stuff (will probably work in Django>1.9) - url(r'^', include('evennia.web.website.urls')),#, namespace='website', app_name='website')), + url(r'^', include('evennia.web.website.urls')), # , namespace='website', app_name='website')), # webclient url(r'^webclient/', include('evennia.web.webclient.urls', namespace='webclient', app_name='webclient')), # favicon - url(r'^favicon\.ico$', RedirectView.as_view(url='/media/images/favicon.ico', permanent=False)) - ] + url(r'^favicon\.ico$', RedirectView.as_view(url='/media/images/favicon.ico', permanent=False)) +] diff --git a/evennia/web/utils/backends.py b/evennia/web/utils/backends.py index 5d495af13..3c3b5e009 100644 --- a/evennia/web/utils/backends.py +++ b/evennia/web/utils/backends.py @@ -1,40 +1,42 @@ from django.contrib.auth.backends import ModelBackend from django.contrib.auth import get_user_model + class CaseInsensitiveModelBackend(ModelBackend): - """ - By default ModelBackend does case _sensitive_ username - authentication, which isn't what is generally expected. This - backend supports case insensitive username authentication. + """ + By default ModelBackend does case _sensitive_ username + authentication, which isn't what is generally expected. This + backend supports case insensitive username authentication. - """ - def authenticate(self, username=None, password=None, autologin=None): - """ - Custom authenticate with bypass for auto-logins + """ - Args: - username (str, optional): Name of user to authenticate. - password (str, optional): Password of user - autologin (Account, optional): If given, assume this is - an already authenticated account and bypass authentication. - """ - if autologin: - # Note: Setting .backend on account is critical in order to - # be allowed to call django.auth.login(account) later. This - # is necessary for the auto-login feature of the webclient, - # but it's important to make sure Django doesn't change this - # requirement or the name of the property down the line. /Griatch - autologin.backend = "evennia.web.utils.backends.CaseInsensitiveModelBackend" - return autologin - else: - # In this case .backend will be assigned automatically - # somewhere along the way. - Account = get_user_model() - try: - account = Account.objects.get(username__iexact=username) - if account.check_password(password): - return account - else: - return None - except Account.DoesNotExist: - return None + def authenticate(self, username=None, password=None, autologin=None): + """ + Custom authenticate with bypass for auto-logins + + Args: + username (str, optional): Name of user to authenticate. + password (str, optional): Password of user + autologin (Account, optional): If given, assume this is + an already authenticated account and bypass authentication. + """ + if autologin: + # Note: Setting .backend on account is critical in order to + # be allowed to call django.auth.login(account) later. This + # is necessary for the auto-login feature of the webclient, + # but it's important to make sure Django doesn't change this + # requirement or the name of the property down the line. /Griatch + autologin.backend = "evennia.web.utils.backends.CaseInsensitiveModelBackend" + return autologin + else: + # In this case .backend will be assigned automatically + # somewhere along the way. + Account = get_user_model() + try: + account = Account.objects.get(username__iexact=username) + if account.check_password(password): + return account + else: + return None + except Account.DoesNotExist: + return None diff --git a/evennia/web/utils/general_context.py b/evennia/web/utils/general_context.py index cd7de633e..2abd31afb 100644 --- a/evennia/web/utils/general_context.py +++ b/evennia/web/utils/general_context.py @@ -38,6 +38,7 @@ WEBSOCKET_CLIENT_ENABLED = settings.WEBSOCKET_CLIENT_ENABLED WEBSOCKET_PORT = settings.WEBSOCKET_CLIENT_PORT WEBSOCKET_URL = settings.WEBSOCKET_CLIENT_URL + def general_context(request): """ Returns common Evennia-related context stuff, which @@ -50,9 +51,9 @@ def general_context(request): 'evennia_entityapps': GAME_ENTITIES, 'evennia_setupapps': GAME_SETUP, 'evennia_connectapps': CONNECTIONS, - 'evennia_websiteapps':WEBSITE, - "webclient_enabled" : WEBCLIENT_ENABLED, - "websocket_enabled" : WEBSOCKET_CLIENT_ENABLED, - "websocket_port" : WEBSOCKET_PORT, - "websocket_url" : WEBSOCKET_URL + 'evennia_websiteapps': WEBSITE, + "webclient_enabled": WEBCLIENT_ENABLED, + "websocket_enabled": WEBSOCKET_CLIENT_ENABLED, + "websocket_port": WEBSOCKET_PORT, + "websocket_url": WEBSOCKET_URL } diff --git a/evennia/web/webclient/urls.py b/evennia/web/webclient/urls.py index 4031dde04..cf2f82214 100644 --- a/evennia/web/webclient/urls.py +++ b/evennia/web/webclient/urls.py @@ -6,4 +6,4 @@ from django.conf.urls import * from evennia.web.webclient import views as webclient_views urlpatterns = [ - url(r'^$', webclient_views.webclient, name="index")] + url(r'^$', webclient_views.webclient, name="index")] diff --git a/evennia/web/website/templatetags/addclass.py b/evennia/web/website/templatetags/addclass.py index 53c6a5302..8e8f69e2c 100644 --- a/evennia/web/website/templatetags/addclass.py +++ b/evennia/web/website/templatetags/addclass.py @@ -2,6 +2,7 @@ from django import template register = template.Library() + @register.filter(name='addclass') def addclass(field, given_class): existing_classes = field.field.widget.attrs.get('class', None) diff --git a/evennia/web/website/urls.py b/evennia/web/website/urls.py index 827db1f89..2f5dd8884 100644 --- a/evennia/web/website/urls.py +++ b/evennia/web/website/urls.py @@ -14,19 +14,19 @@ from evennia.web.website import views as website_views admin.autodiscover() urlpatterns = [ - url(r'^$', website_views.page_index, name="index"), - url(r'^tbi/', website_views.to_be_implemented, name='to_be_implemented'), + url(r'^$', website_views.page_index, name="index"), + url(r'^tbi/', website_views.to_be_implemented, name='to_be_implemented'), - # User Authentication (makes login/logout url names available) - url(r'^authenticate', include('django.contrib.auth.urls')), + # User Authentication (makes login/logout url names available) + url(r'^authenticate', include('django.contrib.auth.urls')), - # Django original admin page. Make this URL is always available, whether - # we've chosen to use Evennia's custom admin or not. - url(r'django_admin/', website_views.admin_wrapper, name="django_admin"), + # Django original admin page. Make this URL is always available, whether + # we've chosen to use Evennia's custom admin or not. + url(r'django_admin/', website_views.admin_wrapper, name="django_admin"), - # Admin docs - url(r'^admin/doc/', include('django.contrib.admindocs.urls')) - ] + # Admin docs + url(r'^admin/doc/', include('django.contrib.admindocs.urls')) +] if settings.EVENNIA_ADMIN: urlpatterns += [ diff --git a/setup.py b/setup.py index 0702fa9e6..68d8bdbeb 100644 --- a/setup.py +++ b/setup.py @@ -64,6 +64,7 @@ def package_data(): file_set.append(file_name) return file_set + # setup the package setup( name='evennia',