mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-06-07 13:15:53 +02:00
Register content by module name, to avoid circular imports.
This commit is contained in:
@@ -14,4 +14,3 @@ from dungeonsheets import background, features, race, spells, weapons, mechanics
|
||||
from dungeonsheets.character import Character
|
||||
from dungeonsheets.content_registry import import_homebrew
|
||||
from dungeonsheets.content import __version__
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
from dungeonsheets.content_registry import default_content_registry
|
||||
|
||||
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
|
||||
class Shield:
|
||||
"""A shield that can be worn on one hand."""
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
from dungeonsheets import features as feats
|
||||
from dungeonsheets.content_registry import default_content_registry
|
||||
|
||||
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
|
||||
class Background:
|
||||
|
||||
@@ -1,20 +1,51 @@
|
||||
"""Registries for looking up pre-defined game content.
|
||||
|
||||
The function *find_content* will find the class for a piece of content
|
||||
when given the name of that content. For example:
|
||||
``find_content("leather armor")`` will return the
|
||||
*dungeonsheets.armor.LeatherArmor* class.
|
||||
|
||||
The *find_content* function is a shortcut that makes use of the
|
||||
*default_content_registry*, which is a *ContentRegistry* instance that
|
||||
is aware of all the content included in *dungeonsheets*. **New modules
|
||||
should register themselves** with the *default_content_registry*, best
|
||||
achieved by::
|
||||
|
||||
from content_registry import default_content_registry
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
In the case of **homebrew content**, the python file may not be in the
|
||||
python path and so cannot be imported directly. In this case, the
|
||||
*import_homebrew* function will import the python file-name given and
|
||||
then register it with the *default_content_registry*, most often
|
||||
within a character sheet file. For example, if the *PaperSword* weapon
|
||||
class is defined in a separate file "my_homebrew.py", then in the
|
||||
character file::
|
||||
|
||||
from content_registry import import_homebrew
|
||||
import_homebrew("my_homebrew.py")
|
||||
|
||||
weapons = ["paper sword"]
|
||||
|
||||
If homebrew content shares a name with canonical content, then lookup
|
||||
by string will raise an exception. In those situations, the homebrew
|
||||
content can be used directly from *import_homebrew*::
|
||||
|
||||
from content_registry import import_homebrew
|
||||
campaign = import_homebrew("my_homebrew.py")
|
||||
|
||||
weapons = [campaign.PaperSword]
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from functools import lru_cache
|
||||
import importlib.util
|
||||
from typing import Union, List, Optional
|
||||
|
||||
from dungeonsheets import (
|
||||
weapons,
|
||||
monsters,
|
||||
race,
|
||||
background,
|
||||
armor,
|
||||
spells,
|
||||
infusions,
|
||||
magic_items,
|
||||
features,
|
||||
exceptions,
|
||||
)
|
||||
from dungeonsheets import exceptions
|
||||
|
||||
|
||||
class ContentRegistry:
|
||||
@@ -24,6 +55,28 @@ class ContentRegistry:
|
||||
self.modules = []
|
||||
|
||||
def add_module(self, new_module):
|
||||
"""Register a module with this registry.
|
||||
|
||||
Adding the same module multiple times has no effect.
|
||||
|
||||
*new_module* can also be a string, in which case, an attempt
|
||||
will be made to load the module from *sys.modules*. This way,
|
||||
a module can register itself::
|
||||
|
||||
# Define classes, etc
|
||||
...
|
||||
# Register the module
|
||||
registry = ContentRegistry()
|
||||
registry.add_module(__name__)
|
||||
|
||||
"""
|
||||
# Try and look up the module by name
|
||||
try:
|
||||
new_module = sys.modules[new_module]
|
||||
except KeyError:
|
||||
if isinstance(new_module, str):
|
||||
raise exceptions.ContentNotFound(f"Module could not be resolved: {repr(new_module)}")
|
||||
# Add the imported module to the list for later
|
||||
if new_module not in self.modules:
|
||||
self.modules.append(new_module)
|
||||
|
||||
@@ -94,15 +147,6 @@ class ContentRegistry:
|
||||
|
||||
|
||||
default_content_registry = ContentRegistry()
|
||||
default_content_registry.add_module(weapons)
|
||||
default_content_registry.add_module(monsters)
|
||||
default_content_registry.add_module(race)
|
||||
default_content_registry.add_module(background)
|
||||
default_content_registry.add_module(armor)
|
||||
default_content_registry.add_module(spells)
|
||||
default_content_registry.add_module(infusions)
|
||||
default_content_registry.add_module(magic_items)
|
||||
default_content_registry.add_module(features)
|
||||
|
||||
|
||||
def find_content(name: str, valid_classes: Optional[List] = None):
|
||||
|
||||
@@ -16,3 +16,8 @@ from dungeonsheets.features.rogue import *
|
||||
from dungeonsheets.features.sorceror import *
|
||||
from dungeonsheets.features.warlock import *
|
||||
from dungeonsheets.features.wizard import *
|
||||
|
||||
from dungeonsheets.content_registry import default_content_registry
|
||||
|
||||
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
from dungeonsheets.content_registry import default_content_registry
|
||||
|
||||
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
|
||||
class Infusion:
|
||||
name = "Unknown infusion"
|
||||
item = "Item to be infused"
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
from dungeonsheets.content_registry import default_content_registry
|
||||
|
||||
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
|
||||
class MagicItem:
|
||||
"""
|
||||
Generic Magic Item. Add description here.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Convenience module holding base classes for the various kinds of
|
||||
game mechanics."""
|
||||
|
||||
from dungeonsheets.content import Content
|
||||
from dungeonsheets.spells import Spell
|
||||
from dungeonsheets.features import Feature
|
||||
from dungeonsheets.infusions import Infusion
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from dungeonsheets.content_registry import default_content_registry
|
||||
|
||||
from dungeonsheets.monsters.monsters import *
|
||||
from dungeonsheets.monsters.monsters_a import *
|
||||
from dungeonsheets.monsters.monsters_b import *
|
||||
@@ -25,3 +27,6 @@ from dungeonsheets.monsters.monsters_w import *
|
||||
from dungeonsheets.monsters.monsters_x import *
|
||||
from dungeonsheets.monsters.monsters_y import *
|
||||
from dungeonsheets.monsters.monsters_z import *
|
||||
|
||||
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
@@ -2,6 +2,10 @@ from collections import defaultdict
|
||||
|
||||
from dungeonsheets import features as feats
|
||||
from dungeonsheets import spells, weapons
|
||||
from dungeonsheets.content_registry import default_content_registry
|
||||
|
||||
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
|
||||
class Race:
|
||||
|
||||
@@ -26,3 +26,8 @@ from dungeonsheets.spells.spells_w import *
|
||||
from dungeonsheets.spells.spells_x import *
|
||||
from dungeonsheets.spells.spells_y import *
|
||||
from dungeonsheets.spells.spells_z import *
|
||||
|
||||
from dungeonsheets.content_registry import default_content_registry
|
||||
|
||||
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
from dungeonsheets.content_registry import default_content_registry
|
||||
|
||||
|
||||
default_content_registry.add_module(__name__)
|
||||
|
||||
|
||||
class Weapon:
|
||||
name = ""
|
||||
cost = "0 gp"
|
||||
|
||||
@@ -5,6 +5,9 @@ monsters, etc.
|
||||
|
||||
"""
|
||||
|
||||
from dungeonsheets import mechanics
|
||||
|
||||
|
||||
# This line (or one like it) is required in order for dungeonsheets to
|
||||
# recognize the file.
|
||||
dungeonsheets_version = "0.15.0"
|
||||
@@ -36,14 +39,15 @@ monsters = ["aboleth", "wolf", "giant eagle", "Vashta Nerada", "priest"]
|
||||
# make up the body
|
||||
|
||||
|
||||
class BBEGMotivation():
|
||||
class BBEGMotivation(mechanics.Content):
|
||||
"""Hans Gruber is after the $640 in bearer bonds stored in *Nakatomi
|
||||
plaza*.
|
||||
|
||||
"""
|
||||
name = "Big-Bad-Evil-Guy Motivation"
|
||||
|
||||
class BarFight():
|
||||
|
||||
class BarFight(mechanics.Content):
|
||||
"""If the characters decide to go to the *Alliance Friendly Bar*,
|
||||
they will probably have to fight their way out against 5 enemies
|
||||
(3 Veteran, 2 Soldier).
|
||||
|
||||
@@ -14,6 +14,17 @@ class TestContentRegistry(TestCase):
|
||||
creg.add_module(monsters)
|
||||
self.assertEqual(len(creg.modules), 1)
|
||||
|
||||
def test_add_module_by_name(self):
|
||||
# Check that a module gets converted to a module instance
|
||||
creg = ContentRegistry()
|
||||
creg.add_module("dungeonsheets.monsters")
|
||||
self.assertEqual(len(creg.modules), 1)
|
||||
self.assertFalse(isinstance(creg.modules[0], str),
|
||||
"String not converted to module.")
|
||||
# Check if is indempotent
|
||||
creg.add_module("dungeonsheets.monsters")
|
||||
self.assertEqual(len(creg.modules), 1)
|
||||
|
||||
def test_findattr(self):
|
||||
"""Check if the function can find attributes."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user