mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Merge pull request #2922 from InspectorCaracal/global-scripts-raise
Allow global scripts container to raise import errors
This commit is contained in:
commit
2b328defcc
3 changed files with 65 additions and 72 deletions
|
|
@ -19,8 +19,6 @@ from evennia.utils import logger
|
|||
from evennia.utils.utils import callables_from_module, class_from_module
|
||||
|
||||
SCRIPTDB = None
|
||||
_BASE_SCRIPT_TYPECLASS = None
|
||||
|
||||
|
||||
class Container:
|
||||
"""
|
||||
|
|
@ -201,27 +199,11 @@ class GlobalScriptContainer(Container):
|
|||
initialized.
|
||||
|
||||
"""
|
||||
if self.loaded_data:
|
||||
# we don't always load this, it collides with doc generation
|
||||
global _BASE_SCRIPT_TYPECLASS
|
||||
if not _BASE_SCRIPT_TYPECLASS:
|
||||
_BASE_SCRIPT_TYPECLASS = class_from_module(settings.BASE_SCRIPT_TYPECLASS)
|
||||
|
||||
if self.typeclass_storage is None:
|
||||
self.typeclass_storage = {}
|
||||
for key, data in list(self.loaded_data.items()):
|
||||
try:
|
||||
typeclass = data.get("typeclass", settings.BASE_SCRIPT_TYPECLASS)
|
||||
script_typeclass = class_from_module(typeclass)
|
||||
assert issubclass(script_typeclass, _BASE_SCRIPT_TYPECLASS)
|
||||
self.typeclass_storage[key] = script_typeclass
|
||||
except Exception:
|
||||
logger.log_trace(
|
||||
f"GlobalScriptContainer could not start import global script {key}. "
|
||||
"It will be removed (skipped)."
|
||||
)
|
||||
# Let's remove this key/value. We want to let other scripts load.
|
||||
self.loaded_data.pop(key)
|
||||
typeclass = data.get("typeclass", settings.BASE_SCRIPT_TYPECLASS)
|
||||
self.typeclass_storage[key] = class_from_module(typeclass, fallback=settings.BASE_SCRIPT_TYPECLASS)
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""
|
||||
|
|
|
|||
10
evennia/utils/tests/data/broken_script.py
Normal file
10
evennia/utils/tests/data/broken_script.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
"""
|
||||
Defines a script module with a broken import, to catch the specific error case
|
||||
in loading global scripts where the module can be parsed but has broken
|
||||
dependencies.
|
||||
"""
|
||||
|
||||
from evennia import nonexistent_module, DefaultScript
|
||||
|
||||
class BrokenScript(DefaultScript):
|
||||
pass
|
||||
|
|
@ -4,78 +4,79 @@ from evennia.utils import containers
|
|||
from django.conf import settings
|
||||
from django.test import override_settings
|
||||
from evennia.utils.utils import class_from_module
|
||||
from evennia import DefaultScript
|
||||
|
||||
_BASE_SCRIPT_TYPECLASS = class_from_module(settings.BASE_SCRIPT_TYPECLASS)
|
||||
_BASE_TYPECLASS = class_from_module(settings.BASE_SCRIPT_TYPECLASS)
|
||||
|
||||
class GoodScript(_BASE_SCRIPT_TYPECLASS):
|
||||
pass
|
||||
class GoodScript(DefaultScript):
|
||||
pass
|
||||
|
||||
class BadScript:
|
||||
"""Not subclass of _BASE_SCRIPT_TYPECLASS,"""
|
||||
pass
|
||||
|
||||
class WorseScript(_BASE_SCRIPT_TYPECLASS):
|
||||
"""objects will fail upon call"""
|
||||
@property
|
||||
def objects(self):
|
||||
from evennia import module_that_doesnt_exist
|
||||
class InvalidScript:
|
||||
pass
|
||||
|
||||
class TestGlobalScriptContainer(unittest.TestCase):
|
||||
|
||||
def test_init_with_no_scripts(self):
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
def test_init_with_no_scripts(self):
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
|
||||
self.assertEqual(len(gsc.loaded_data), 0)
|
||||
self.assertEqual(len(gsc.loaded_data), 0)
|
||||
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {}})
|
||||
def test_init_with_typeclassless_script(self):
|
||||
@override_settings(GLOBAL_SCRIPTS={})
|
||||
def test_start_with_no_scripts(self):
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
gsc.start()
|
||||
|
||||
self.assertEqual(len(gsc.loaded_data), 1)
|
||||
self.assertIn('script_name', gsc.loaded_data)
|
||||
self.assertEqual(len(gsc.typeclass_storage), 0)
|
||||
|
||||
def test_start_with_no_scripts(self):
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {}})
|
||||
def test_start_with_typeclassless_script(self):
|
||||
"""No specified typeclass should fallback to base"""
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
|
||||
gsc.start()
|
||||
gsc.start()
|
||||
|
||||
self.assertEqual(len(gsc.typeclass_storage), 0)
|
||||
self.assertEqual(len(gsc.typeclass_storage), 1)
|
||||
self.assertIn('script_name', gsc.typeclass_storage)
|
||||
self.assertEqual(gsc.typeclass_storage['script_name'], _BASE_TYPECLASS)
|
||||
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {}})
|
||||
def test_start_with_typeclassless_script_defaults_to_base(self):
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.test_containers.NoScript'}})
|
||||
def test_start_with_nonexistent_script(self):
|
||||
"""Missing script class should fall back to base"""
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
|
||||
gsc.start()
|
||||
gsc.start()
|
||||
|
||||
self.assertEqual(len(gsc.typeclass_storage), 1)
|
||||
self.assertIn('script_name', gsc.typeclass_storage)
|
||||
self.assertEqual(gsc.typeclass_storage['script_name'], _BASE_SCRIPT_TYPECLASS)
|
||||
self.assertEqual(len(gsc.typeclass_storage), 1)
|
||||
self.assertIn('script_name', gsc.typeclass_storage)
|
||||
self.assertEqual(gsc.typeclass_storage['script_name'], _BASE_TYPECLASS)
|
||||
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.test_containers.GoodScript'}})
|
||||
def test_start_with_typeclassed_script_loads_it(self):
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.test_containers.GoodScript'}})
|
||||
def test_start_with_valid_script(self):
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
|
||||
gsc.start()
|
||||
gsc.start()
|
||||
|
||||
self.assertEqual(len(gsc.typeclass_storage), 1)
|
||||
self.assertIn('script_name', gsc.typeclass_storage)
|
||||
self.assertEqual(gsc.typeclass_storage['script_name'], GoodScript)
|
||||
self.assertEqual(len(gsc.typeclass_storage), 1)
|
||||
self.assertIn('script_name', gsc.typeclass_storage)
|
||||
self.assertEqual(gsc.typeclass_storage['script_name'], GoodScript)
|
||||
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.test_containers.BadScript'}})
|
||||
def test_start_with_bad_typeclassed_script_skips_it(self):
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.test_containers.InvalidScript'}})
|
||||
def test_start_with_invalid_script(self):
|
||||
"""Script class doesn't implement required methods methods"""
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
|
||||
gsc.start()
|
||||
with self.assertRaises(AttributeError) as err:
|
||||
gsc.start()
|
||||
# check for general attribute failure on the invalid class to preserve against future code-rder changes
|
||||
self.assertTrue(str(err.exception).startswith("type object 'InvalidScript' has no attribute"), err.exception)
|
||||
|
||||
self.assertEqual(len(gsc.typeclass_storage), 0)
|
||||
self.assertNotIn('script_name', gsc.typeclass_storage)
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.data.broken_script.BrokenScript'}})
|
||||
def test_start_with_broken_script(self):
|
||||
"""Un-importable script should traceback"""
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
|
||||
@override_settings(GLOBAL_SCRIPTS={'script_name': {'typeclass': 'evennia.utils.tests.test_containers.WorstScript'}})
|
||||
def test_start_with_worst_typeclassed_script_skips_it(self):
|
||||
gsc = containers.GlobalScriptContainer()
|
||||
|
||||
gsc.start()
|
||||
|
||||
self.assertEqual(len(gsc.typeclass_storage), 0)
|
||||
self.assertNotIn('script_name', gsc.typeclass_storage)
|
||||
with self.assertRaises(Exception) as err:
|
||||
gsc.start()
|
||||
# exception raised by imported module
|
||||
self.assertTrue(str(err.exception).startswith("cannot import name 'nonexistent_module' from 'evennia'"), err.exception)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue