Merge branch 'master' of https://github.com/trhr/evennia into aws-s3-cdn

This commit is contained in:
trhr 2020-02-28 20:31:15 -06:00
commit 6e30dce735
5 changed files with 94 additions and 9 deletions

View file

@ -6,6 +6,7 @@ All commands in Evennia inherit from the 'Command' class in this module.
"""
import re
import math
import inspect
from django.conf import settings
@ -74,6 +75,13 @@ def _init_command(cls, **kwargs):
cls.is_exit = False
if not hasattr(cls, "help_category"):
cls.help_category = "general"
# make sure to pick up the parent's docstring if the child class is
# missing one (important for auto-help)
if cls.__doc__ is None:
for parent_class in inspect.getmro(cls):
if parent_class.__doc__ is not None:
cls.__doc__ = parent_class.__doc__
break
cls.help_category = cls.help_category.lower()

View file

@ -19,6 +19,7 @@ from evennia.utils.eveditor import EvEditor
from evennia.utils.evmore import EvMore
from evennia.prototypes import spawner, prototypes as protlib, menus as olc_menus
from evennia.utils.ansi import raw
from evennia.prototypes.menus import _format_diff_text_and_options
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
@ -1912,8 +1913,8 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
Usage:
typeclass[/switch] <object> [= typeclass.path]
type ''
parent ''
typeclass/prototype <object> = prototype_key
typeclass/list/show [typeclass.path]
swap - this is a shorthand for using /force/reset flags.
update - this is a shorthand for using the /force/reload flag.
@ -1930,9 +1931,12 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
list - show available typeclasses. Only typeclasses in modules actually
imported or used from somewhere in the code will show up here
(those typeclasses are still available if you know the path)
prototype - clean and overwrite the object with the specified
prototype key - effectively making a whole new object.
Example:
type button = examples.red_button.RedButton
type/prototype button=a red button
If the typeclass_path is not given, the current object's typeclass is
assumed.
@ -1954,7 +1958,7 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
key = "typeclass"
aliases = ["type", "parent", "swap", "update"]
switch_options = ("show", "examine", "update", "reset", "force", "list")
switch_options = ("show", "examine", "update", "reset", "force", "list", "prototype")
locks = "cmd:perm(typeclass) or perm(Builder)"
help_category = "Building"
@ -2038,6 +2042,27 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
new_typeclass = self.rhs or obj.path
prototype = None
if "prototype" in self.switches:
key = self.rhs
prototype = protlib.search_prototype(key=key)
if len(prototype) > 1:
caller.msg(
"More than one match for {}:\n{}".format(
key, "\n".join(proto.get("prototype_key", "") for proto in prototype)
)
)
return
elif prototype:
# one match
prototype = prototype[0]
else:
# no match
caller.msg("No prototype '{}' was found.".format(key))
return
new_typeclass = prototype["typeclass"]
self.switches.append("force")
if "show" in self.switches or "examine" in self.switches:
string = "%s's current typeclass is %s." % (obj.name, obj.__class__)
caller.msg(string)
@ -2070,11 +2095,38 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
hooks = "at_object_creation" if update else "all"
old_typeclass_path = obj.typeclass_path
# special prompt for the user in cases where we want
# to confirm changes.
if "prototype" in self.switches:
diff, _ = spawner.prototype_diff_from_object(prototype, obj)
txt, options = _format_diff_text_and_options(diff, objects=[obj])
prompt = "Applying prototype '%s' over '%s' will cause the follow changes:\n%s\n" % \
(
prototype["key"],
obj.name,
"\n".join(txt)
)
if not reset:
prompt += "\n|yWARNING:|n Use the /reset switch to apply the prototype over a blank state."
prompt += "\nAre you sure you want to apply these changes [yes]/no?"
answer = yield (prompt)
if answer and answer in ("no", "n"):
caller.msg(
"Canceled: No changes were applied."
)
return
# we let this raise exception if needed
obj.swap_typeclass(
new_typeclass, clean_attributes=reset, clean_cmdsets=reset, run_start_hooks=hooks
)
if "prototype" in self.switches:
modified = spawner.batch_update_objects_with_prototype(prototype, objects=[obj])
prototype_success = modified > 0
if not prototype_success:
caller.msg("Prototype %s failed to apply." % prototype["key"])
if is_same:
string = "%s updated its existing typeclass (%s).\n" % (obj.name, obj.path)
else:
@ -2091,6 +2143,8 @@ class CmdTypeclass(COMMAND_DEFAULT_CLASS):
string += " All old attributes where deleted before the swap."
else:
string += " Attributes set before swap were not removed."
if "prototype" in self.switches and prototype_success:
string += " Prototype '%s' was successfully applied over the object type." % prototype["key"]
caller.msg(string)

View file

@ -991,6 +991,27 @@ class TestBuilding(CommandTest):
"All object creation hooks were run. All old attributes where deleted before the swap.",
)
from evennia.prototypes.prototypes import homogenize_prototype
test_prototype = [homogenize_prototype(
{"prototype_key": "testkey",
"prototype_tags": [],
"typeclass": "typeclasses.objects.Object",
"key":"replaced_obj",
"attrs": [("foo", "bar", None, ""),
("desc", "protdesc", None, "")]})]
with mock.patch("evennia.commands.default.building.protlib.search_prototype",
new=mock.MagicMock(return_value=test_prototype)) as mprot:
self.call(
building.CmdTypeclass(),
"/prototype Obj=testkey",
"replaced_obj changed typeclass from "
"evennia.objects.objects.DefaultObject to "
"typeclasses.objects.Object.\nAll object creation hooks were "
"run. Attributes set before swap were not removed. Prototype "
"'replaced_obj' was successfully applied over the object type."
)
assert self.obj1.db.desc == "protdesc"
def test_lock(self):
self.call(building.CmdLock(), "", "Usage: ")
self.call(building.CmdLock(), "Obj = test:all()", "Added lock 'test:all()' to Obj.")

View file

@ -388,19 +388,18 @@ if WEBSERVER_ENABLED:
webclientstr = "webclient-websocket%s: %s" % (w_ifacestr, port)
INFO_DICT["webclient"].append(webclientstr)
web_root = Website(web_root, logPath=settings.HTTP_LOG_FILE)
web_root.is_portal = True
if WEB_PLUGINS_MODULE:
try:
web_root = WEB_PLUGINS_MODULE.at_webproxy_root_creation(web_root)
except Exception as e: # Legacy user has not added an at_webproxy_root_creation function in existing web plugins file
except Exception as e: # Legacy user has not added an at_webproxy_root_creation function in existing web plugins file
INFO_DICT["errors"] = (
"WARNING: WEB_PLUGINS_MODULE is enabled but at_webproxy_root_creation() not found - "
"copy 'evennia/game_template/server/conf/web_plugins.py to mygame/server/conf."
)
web_root = Website(web_root, logPath=settings.HTTP_LOG_FILE)
web_root.is_portal = True
proxy_service = internet.TCPServer(proxyport, web_root, interface=interface)
proxy_service.setName("EvenniaWebProxy%s:%s" % (ifacestr, proxyport))
PORTAL.services.addService(proxy_service)

View file

@ -7,9 +7,12 @@ pytz
django-sekizai
inflect
autobahn >= 17.9.3
model_mommy
# try to resolve dependency issue in py3.7
attrs >= 19.2.0
# testing and development
model_mommy
mock >= 1.0.1
anything
black