1717from .spec .utilities import get_spec
1818from .spec .td import ThingDescription
1919from .decorators import tag
20- from .sockets import Sockets , SocketSubscriber , socket_handler_loop
20+ from .sockets import Sockets
2121
2222from .default_views .extensions import ExtensionList
2323from .default_views .tasks import TaskList , TaskView
2424from .default_views .docs import docs_blueprint , SwaggerUIView
25+ from .default_views .root import RootView
26+ from .default_views .sockets import socket_handler
2527
2628from ..core .utilities import get_docstring
2729
30+ import weakref
2831import logging
2932
3033
@@ -72,7 +75,7 @@ def __init__(
7275
7376 # Logging handler
7477 # TODO: Add cleanup code
75- self .log_handler = LabThingLogger (self )
78+ self .log_handler = LabThingLogger ()
7679 logging .getLogger ().addHandler (self .log_handler )
7780
7881 self .spec = APISpec (
@@ -97,14 +100,23 @@ def description(self, description: str):
97100 self .spec .description = description
98101
99102 @property
100- def title (self , ):
103+ def title (self ):
101104 return self ._title
102105
103106 @title .setter
104107 def title (self , title : str ):
105108 self ._title = title
106109 self .spec .title = title
107110
111+ @property
112+ def safe_title (self ):
113+ title = self .title
114+ if not title :
115+ title = "unknown"
116+ title = title .replace (" " , "" )
117+ title = title .lower ()
118+ return title
119+
108120 @property
109121 def version (self ,):
110122 return str (self ._version )
@@ -119,11 +131,9 @@ def version(self, version: str):
119131 def init_app (self , app ):
120132 self .app = app
121133
122- app .teardown_appcontext (self .teardown )
123-
124134 # Register Flask extension
125135 app .extensions = getattr (app , "extensions" , {})
126- app .extensions [EXTENSION_NAME ] = self
136+ app .extensions [EXTENSION_NAME ] = weakref . ref ( self )
127137
128138 # Flask error formatter
129139 if self .format_flask_exceptions :
@@ -145,12 +155,9 @@ def init_app(self, app):
145155 self .sockets = Sockets (app )
146156 self ._create_base_sockets ()
147157
148- def teardown (self , exception ):
149- pass
150-
151158 def _create_base_routes (self ):
152159 # Add root representation
153- self .app . add_url_rule ( self . _complete_url ( "/" , "" ), "root" , self . root )
160+ self .add_view ( RootView , "/" , endpoint = "root" )
154161 # Add thing descriptions
155162 self .app .register_blueprint (
156163 docs_blueprint , url_prefix = f"{ self .url_prefix } /docs"
@@ -166,19 +173,7 @@ def _create_base_routes(self):
166173 self .add_view (TaskView , "/tasks/<task_id>" , endpoint = TASK_ENDPOINT )
167174
168175 def _create_base_sockets (self ):
169- self .sockets .add_url_rule (self ._complete_url ("" , "" ), self ._socket_handler )
170-
171- def _socket_handler (self , ws ):
172- # Create a socket subscriber
173- wssub = SocketSubscriber (ws )
174- self .subscribers .add (wssub )
175- logging .info (f"Added subscriber { wssub } " )
176- # Start the socket connection handler loop
177- socket_handler_loop (ws )
178- # Remove the subscriber once the loop returns
179- self .subscribers .remove (wssub )
180- logging .info (f"Removed subscriber { wssub } " )
181- logging .debug (list (self .subscribers ))
176+ self .sockets .add_view (self ._complete_url ("" , "" ), socket_handler )
182177
183178 # Device stuff
184179
@@ -300,17 +295,19 @@ def _register_view(self, app, view, *urls, endpoint=None, **kwargs):
300295
301296 # There might be a better way to do this than _rules_by_endpoint,
302297 # but I can't find one so this will do for now. Skipping PYL-W0212
298+ # FIXME: There is a MASSIVE memory leak or something going on in APISpec!
299+ # This is grinding tests to a halt, and is really annoying... Should be fixed.
303300 flask_rules = app .url_map ._rules_by_endpoint .get (endpoint ) # skipcq: PYL-W0212
304301 for flask_rule in flask_rules :
305302 self .spec .path (** rule_to_apispec_path (flask_rule , view , self .spec ))
306303
307304 # Handle resource groups listed in API spec
308305 view_spec = get_spec (view )
309- view_groups = view_spec .get ("_groups " , [] )
310- if "actions" in view_groups :
306+ view_tags = view_spec .get ("tags " , set () )
307+ if "actions" in view_tags :
311308 self .thing_description .action (flask_rules , view )
312309 self ._action_views [view .endpoint ] = view
313- if "properties" in view_groups :
310+ if "properties" in view_tags :
314311 self .thing_description .property (flask_rules , view )
315312 self ._property_views [view .endpoint ] = view
316313
@@ -336,8 +333,3 @@ def add_root_link(self, view, rel, kwargs=None, params=None):
336333 if params is None :
337334 params = {}
338335 self .thing_description .add_link (view , rel , kwargs = kwargs , params = params )
339-
340- # Description
341- def root (self ):
342- """Root representation"""
343- return self .thing_description .to_dict ()
0 commit comments