diff --git a/Utils.py b/Utils.py index 02bc8e8f..4fe9c1b4 100644 --- a/Utils.py +++ b/Utils.py @@ -477,7 +477,7 @@ class RestrictedUnpickler(pickle.Unpickler): mod = importlib.import_module(module) obj = getattr(mod, name) if issubclass(obj, (self.options_module.Option, self.options_module.PlandoConnection, - self.options_module.PlandoText)): + self.options_module.PlandoItem, self.options_module.PlandoText)): return obj # Forbid everything else. raise pickle.UnpicklingError(f"global '{module}.{name}' is forbidden") diff --git a/test/general/test_options.py b/test/general/test_options.py index d8ce7017..f5111e91 100644 --- a/test/general/test_options.py +++ b/test/general/test_options.py @@ -1,7 +1,7 @@ import unittest from BaseClasses import PlandoOptions -from Options import ItemLinks, Choice +from Options import Choice, ItemLinks, PlandoConnections, PlandoItems, PlandoTexts from Utils import restricted_dumps from worlds.AutoWorld import AutoWorldRegister @@ -72,8 +72,8 @@ class TestOptions(unittest.TestCase): for link in item_links.values(): self.assertEqual(link.value[0], item_link_group[0]) - def test_pickle_dumps(self): - """Test options can be pickled into database for WebHost generation""" + def test_pickle_dumps_default(self): + """Test that default option values can be pickled into database for WebHost generation""" for gamename, world_type in AutoWorldRegister.world_types.items(): if not world_type.hidden: for option_key, option in world_type.options_dataclass.type_hints.items(): @@ -81,3 +81,23 @@ class TestOptions(unittest.TestCase): restricted_dumps(option.from_any(option.default)) if issubclass(option, Choice) and option.default in option.name_lookup: restricted_dumps(option.from_text(option.name_lookup[option.default])) + + def test_pickle_dumps_plando(self): + """Test that plando options using containers of a custom type can be pickled""" + # The base PlandoConnections class can't be instantiated directly, create a subclass and then cast it + class TestPlandoConnections(PlandoConnections): + entrances = {"An Entrance"} + exits = {"An Exit"} + plando_connection_value = PlandoConnections( + TestPlandoConnections.from_any([{"entrance": "An Entrance", "exit": "An Exit"}]) + ) + + plando_values = { + "PlandoConnections": plando_connection_value, + "PlandoItems": PlandoItems.from_any([{"item": "Something", "location": "Somewhere"}]), + "PlandoTexts": PlandoTexts.from_any([{"text": "Some text.", "at": "text_box"}]), + } + + for option_key, value in plando_values.items(): + with self.subTest(option=option_key): + restricted_dumps(value) diff --git a/worlds/generic/__init__.py b/worlds/generic/__init__.py index fa53f31f..2e614eba 100644 --- a/worlds/generic/__init__.py +++ b/worlds/generic/__init__.py @@ -1,4 +1,5 @@ from typing import NamedTuple, Union +from typing_extensions import deprecated import logging from BaseClasses import Item, Tutorial, ItemClassification @@ -49,7 +50,8 @@ class GenericWorld(World): return Item(name, ItemClassification.filler, -1, self.player) raise InvalidItemError(name) - +@deprecated("worlds.generic.PlandoItem is deprecated and will be removed in the next version. " + "Use Options.PlandoItem(s) instead.") class PlandoItem(NamedTuple): item: str location: str