Make evennia lib importable without created gamedir

This commit is contained in:
Griatch 2021-02-27 13:39:14 +01:00
parent d2046c9e0d
commit b5ddc52536
9 changed files with 62 additions and 32 deletions

View file

@ -30,6 +30,7 @@
- Make MuxCommand `lhs/rhslist` always be lists, also if empty (used to be the empty string)
- Fix typo in UnixCommand contrib, where `help` was given as `--hel`.
- Latin (la) i18n translation (jamalainm)
- Made the `evennia` dir possible to use without gamedir for purpose of doc generation.
### Evennia 0.9.5 (2019-2020)

View file

@ -379,19 +379,6 @@ def _init():
del SystemCmds
del _EvContainer
# typeclases
from .utils.utils import class_from_module
BASE_ACCOUNT_TYPECLASS = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
BASE_OBJECT_TYPECLASS = class_from_module(settings.BASE_OBJECT_TYPECLASS)
BASE_CHARACTER_TYPECLASS = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
BASE_ROOM_TYPECLASS = class_from_module(settings.BASE_ROOM_TYPECLASS)
BASE_EXIT_TYPECLASS = class_from_module(settings.BASE_EXIT_TYPECLASS)
BASE_CHANNEL_TYPECLASS = class_from_module(settings.BASE_CHANNEL_TYPECLASS)
BASE_SCRIPT_TYPECLASS = class_from_module(settings.BASE_SCRIPT_TYPECLASS)
BASE_GUEST_TYPECLASS = class_from_module(settings.BASE_GUEST_TYPECLASS)
del class_from_module
# delayed starts - important so as to not back-access evennia before it has
# finished initializing
GLOBAL_SCRIPTS.start()

View file

@ -19,7 +19,8 @@ from evennia.utils import create, logger, utils, evtable
from evennia.utils.utils import make_iter, class_from_module
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
CHANNEL_DEFAULT_TYPECLASS = class_from_module(settings.BASE_CHANNEL_TYPECLASS)
CHANNEL_DEFAULT_TYPECLASS = class_from_module(
settings.BASE_CHANNEL_TYPECLASS, fallback=settings.FALLBACK_CHANNEL_TYPECLASS)
# limit symbol import for API

View file

@ -31,7 +31,6 @@ from evennia.utils.utils import get_evennia_version, mod_import, make_iter, clas
from evennia.server.portal.portalsessionhandler import PORTAL_SESSIONS
from evennia.utils import logger
from evennia.server.webserver import EvenniaReverseProxyResource
from django.db import connection
# we don't need a connection to the database so close it right away
@ -434,4 +433,6 @@ if WEBSERVER_ENABLED:
for plugin_module in PORTAL_SERVICES_PLUGIN_MODULES:
# external plugin services to start
plugin_module.start_plugin_services(PORTAL)
if plugin_module:
plugin_module.start_plugin_services(PORTAL)

View file

@ -511,6 +511,18 @@ START_LOCATION = "#2"
# out of sync between the processes. Keep on unless you face such
# issues.
TYPECLASS_AGGRESSIVE_CACHE = True
# These are fallbacks for BASE typeclasses failing to load. Usually needed only
# during doc building. The system expects these to *always* load correctly, so
# only modify if you are making fundamental changes to how objects/accounts
# work and know what you are doing
FALLBACK_ACCOUNT_TYPECLASS = "evennia.accounts.accounts.DefaultAccount"
FALLBACK_OBJECT_TYPECLASS = "evennia.objects.objects.DefaultObject"
FALLBACK_CHARACTER_TYPECLASS = "evennia.objects.objects.DefaultCharacter"
FALLBACK_ROOM_TYPECLASS = "evennia.objects.objects.DefaultRoom"
FALLBACK_EXIT_TYPECLASS = "evennia.objects.objects.DefaultExit"
FALLBACK_CHANNEL_TYPECLASS = "evennia.comms.comms.DefaultChannel"
FALLBACK_SCRIPT_TYPECLASS = "evennia.scripts.scripts.DefaultScript"
######################################################################
# Options and validators

View file

@ -1319,6 +1319,9 @@ def variable_from_module(module, variable=None, default=None):
mod = mod_import(module)
if not mod:
return default
if variable:
result = []
for var in make_iter(variable):
@ -1410,7 +1413,7 @@ def fuzzy_import_from_module(path, variable, default=None, defaultpaths=None):
return default
def class_from_module(path, defaultpaths=None):
def class_from_module(path, defaultpaths=None, fallback=None):
"""
Return a class from a module, given the class' full python path. This is
primarily used to convert db_typeclass_path:s to classes.
@ -1419,6 +1422,10 @@ def class_from_module(path, defaultpaths=None):
path (str): Full Python dot-path to module.
defaultpaths (iterable, optional): If a direct import from `path` fails,
try subsequent imports by prepending those paths to `path`.
fallback (str): If all other attempts fail, use this path as a fallback.
This is intended as a last-resport. In the example of Evennia
loading, this would be a path to a default parent class in the
evennia repo itself.
Returns:
class (Class): An uninstatiated class recovered from path.
@ -1472,7 +1479,13 @@ def class_from_module(path, defaultpaths=None):
err += "\nPaths searched:\n %s" % "\n ".join(paths)
else:
err += "."
raise ImportError(err)
logger.log_err(err)
if fallback:
logger.log_warn(f"Falling back to {fallback}.")
return class_from_module(fallback)
else:
# even fallback fails
raise ImportError(err)
return cls

View file

@ -52,7 +52,8 @@ class AccountForm(UserCreationForm):
"""
# The model/typeclass this form creates
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS,
fallback=settings.FALLBACK_ACCOUNT_TYPECLASS)
# The fields to display on the form, in the given order
fields = ("username", "email")
@ -87,7 +88,8 @@ class ObjectForm(EvenniaForm, ModelForm):
"""
# The model/typeclass this form creates
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
model = class_from_module(settings.BASE_OBJECT_TYPECLASS,
fallback=settings.FALLBACK_OBJECT_TYPECLASS)
# The fields to display on the form, in the given order
fields = ("db_key",)
@ -140,7 +142,8 @@ class CharacterForm(ObjectForm):
"""
# Get the correct object model
model = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
model = class_from_module(settings.BASE_CHARACTER_TYPECLASS,
fallback=settings.FALLBACK_CHARACTER_TYPECLASS)
# Allow entry of the 'key' field
fields = ("db_key",)

View file

@ -125,7 +125,8 @@ class ChannelDetailTest(EvenniaWebTest):
def setUp(self):
super(ChannelDetailTest, self).setUp()
klass = class_from_module(self.channel_typeclass)
klass = class_from_module(self.channel_typeclass,
fallback=settings.FALLBACK_CHANNEL_TYPECLASS)
# Create a channel
klass.create("demo")

View file

@ -32,6 +32,7 @@ from django.utils.text import slugify
_BASE_CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
# typeclass fallbacks
def _gamestats():
# Some misc. configurable stuff.
@ -48,11 +49,14 @@ def _gamestats():
nobjs = ObjectDB.objects.count()
nobjs = nobjs or 1 # fix zero-div error with empty database
Character = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
Character = class_from_module(settings.BASE_CHARACTER_TYPECLASS,
fallback=settings.FALLBACK_CHARACTER_TYPECLASS)
nchars = Character.objects.all_family().count()
Room = class_from_module(settings.BASE_ROOM_TYPECLASS)
Room = class_from_module(settings.BASE_ROOM_TYPECLASS,
fallback=settings.FALLBACK_ROOM_TYPECLASS)
nrooms = Room.objects.all_family().count()
Exit = class_from_module(settings.BASE_EXIT_TYPECLASS)
Exit = class_from_module(settings.BASE_EXIT_TYPECLASS,
fallback=settings.FALLBACK_EXIT_TYPECLASS)
nexits = Exit.objects.all_family().count()
nothers = nobjs - nchars - nrooms - nexits
@ -269,7 +273,8 @@ class ObjectDetailView(EvenniaDetailView):
#
# So when you extend it, this line should look simple, like:
# model = Object
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
model = class_from_module(settings.BASE_OBJECT_TYPECLASS,
fallback=settings.FALLBACK_OBJECT_TYPECLASS)
# What HTML template you wish to use to display this page.
template_name = "website/object_detail.html"
@ -372,7 +377,8 @@ class ObjectCreateView(LoginRequiredMixin, EvenniaCreateView):
"""
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
model = class_from_module(settings.BASE_OBJECT_TYPECLASS,
fallback=settings.FALLBACK_OBJECT_TYPECLASS)
class ObjectDeleteView(LoginRequiredMixin, ObjectDetailView, EvenniaDeleteView):
@ -387,7 +393,8 @@ class ObjectDeleteView(LoginRequiredMixin, ObjectDetailView, EvenniaDeleteView):
"""
# -- Django constructs --
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
model = class_from_module(settings.BASE_OBJECT_TYPECLASS,
fallback=settings.FALLBACK_OBJECT_TYPECLASS)
template_name = "website/object_confirm_delete.html"
# -- Evennia constructs --
@ -430,7 +437,8 @@ class ObjectUpdateView(LoginRequiredMixin, ObjectDetailView, EvenniaUpdateView):
"""
# -- Django constructs --
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
model = class_from_module(settings.BASE_OBJECT_TYPECLASS,
fallback=settings.FALLBACK_OBJECT_TYPECLASS)
# -- Evennia constructs --
access_type = "edit"
@ -513,7 +521,8 @@ class AccountMixin(TypeclassMixin):
"""
# -- Django constructs --
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS,
fallback=settings.FALLBACK_ACCOUNT_TYPECLASS)
form_class = website_forms.AccountForm
@ -578,7 +587,8 @@ class CharacterMixin(TypeclassMixin):
"""
# -- Django constructs --
model = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
model = class_from_module(settings.BASE_CHARACTER_TYPECLASS,
fallback=settings.FALLBACK_CHARACTER_TYPECLASS)
form_class = website_forms.CharacterForm
success_url = reverse_lazy("character-manage")
@ -817,7 +827,8 @@ class ChannelMixin(TypeclassMixin):
"""
# -- Django constructs --
model = class_from_module(settings.BASE_CHANNEL_TYPECLASS)
model = class_from_module(settings.BASE_CHANNEL_TYPECLASS,
fallback=settings.FALLBACK_CHANNEL_TYPECLASS)
# -- Evennia constructs --
page_title = "Channels"