Skip to content

Commit f76f868

Browse files
authored
Merge pull request #162 from labthings/new-extension-manifest
New extension manifest
2 parents db5677a + 0165bc7 commit f76f868

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

src/labthings/extensions.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
import os
44
import sys
55
import traceback
6+
import inspect
67
from importlib import util
78
from typing import Callable, Dict, List, Union
89

910
from flask import url_for
1011

1112
from .utilities import camel_to_snake, get_docstring, snake_to_spine
13+
from .views import View as BaseView
1214
from .views.builder import static_from
1315

1416

@@ -109,6 +111,10 @@ def add_view(self, view_class, *urls, endpoint=None, **kwargs):
109111
for url in cleaned_urls:
110112
self._rules[url] = self._views[endpoint]
111113

114+
# Store this extension name as the View owner
115+
if issubclass(view_class, BaseView):
116+
view_class.set_extension(self.name)
117+
112118
def on_register(self, function, args=None, kwargs=None):
113119
"""
114120
@@ -262,11 +268,37 @@ def find_extensions_in_file(extension_path: str, module_name="extensions") -> li
262268
)
263269
return []
264270
else:
265-
if hasattr(mod, "__extensions__"):
271+
# TODO: Add documentation links to warnings
272+
if hasattr(mod, "LABTHINGS_EXTENSIONS"):
273+
ext_objects = []
274+
for ext_element in getattr(mod, "LABTHINGS_EXTENSIONS"):
275+
if inspect.isclass(ext_element) and issubclass(
276+
ext_element, BaseExtension
277+
):
278+
ext_objects.append(ext_element())
279+
elif isinstance(ext_element, BaseExtension):
280+
logging.warning(
281+
"%s: Extension instance passed instead of class. LABTHINGS_EXTENSIONS should contain classes, not instances.",
282+
ext_element,
283+
)
284+
ext_objects.append(ext_element)
285+
else:
286+
logging.error(
287+
"Unsupported extension type %s. Skipping.", type(ext_element)
288+
)
289+
return ext_objects
290+
elif hasattr(mod, "__extensions__"):
291+
logging.warning(
292+
"Explicit extension list using the __extensions__ global is deprecated. Please use LABTHINGS_EXTENSIONS instead."
293+
)
266294
return [
267295
getattr(mod, ext_name) for ext_name in getattr(mod, "__extensions__")
268296
]
269297
else:
298+
logging.warning(
299+
"No LTX_MANIFEST found for %s. Searching for implicit extension objects.",
300+
extension_path,
301+
)
270302
return find_instances_in_module(mod, BaseExtension)
271303

272304

src/labthings/views/__init__.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from ..actions.pool import Pool
1010
from ..deque import Deque
11-
from ..find import current_labthing
11+
from ..find import current_labthing, find_extension
1212
from ..marshalling import marshal_with, use_args
1313
from ..representations import DEFAULT_REPRESENTATIONS
1414
from ..schema import ActionSchema, EventSchema, Schema, build_action_schema
@@ -40,6 +40,11 @@ class View(MethodView):
4040
_cls_tags: Set[str] = set() # Class tags that shouldn't be removed
4141
_opmap: Dict[str, str] = {} # Mapping of Thing Description ops to class methods
4242

43+
# Name of parent extension, if one exists.
44+
# This is only used for extension development where Views are added to the extension.
45+
# We store the name instead of the object itself to prevent circular references.
46+
_parent_extension_name: Optional[str] = None
47+
4348
def __init__(self, *args, **kwargs):
4449
MethodView.__init__(self, *args, **kwargs)
4550

@@ -50,6 +55,16 @@ def __init__(self, *args, **kwargs):
5055
else DEFAULT_REPRESENTATIONS
5156
)
5257

58+
@property
59+
def extension(self):
60+
if self._parent_extension_name:
61+
return find_extension(self._parent_extension_name)
62+
return None
63+
64+
@classmethod
65+
def set_extension(cls, extension_name: str):
66+
cls._parent_extension_name = extension_name
67+
5368
@classmethod
5469
def get_tags(cls):
5570
""" """
@@ -257,7 +272,7 @@ class EventView(View):
257272

258273
# Internal
259274
_opmap = {
260-
"subscribeevent": "get",
275+
"subscribeevent": "get"
261276
} # Mapping of Thing Description ops to class methods
262277
_cls_tags = {"events"}
263278
_deque = Deque() # Action queue

0 commit comments

Comments
 (0)