home · contact · privacy
Minor code style/comment/type hinting improvements.
authorChristian Heller <c.heller@plomlompom.de>
Tue, 16 Jul 2024 22:48:44 +0000 (00:48 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Tue, 16 Jul 2024 22:48:44 +0000 (00:48 +0200)
plomtask/db.py
plomtask/http.py
tests/todos.py

index 27dc117273f23c44cd4f9af7934f769810b0d3de..67a7fc766ce607095520e174c944f957667011d7 100644 (file)
@@ -281,10 +281,11 @@ class BaseModel(Generic[BaseModelId]):
         return list(cls.versioned_defaults.keys())
 
     @property
-    def as_dict_and_refs(self) -> tuple[dict[str, object], list[Any]]:
+    def as_dict_and_refs(self) -> tuple[dict[str, object],
+                                        list[BaseModel[int] | BaseModel[str]]]:
         """Return self as json.dumps-ready dict, list of referenced objects."""
         d: dict[str, object] = {'id': self.id_}
-        refs: list[Any] = []
+        refs: list[BaseModel[int] | BaseModel[str]] = []
         for to_save in self.to_save_simples:
             d[to_save] = getattr(self, to_save)
         if len(self.to_save_versioned()) > 0:
index 31646626b9016178cc300d2eea59e217ce5a9dbb..28770689742dde22e1a861d08af595b472f99d15 100644 (file)
@@ -133,12 +133,14 @@ class TaskHandler(BaseHTTPRequestHandler):
     _form_data: InputsParser
     _params: InputsParser
 
-    def _send_page(self,
-                   ctx: dict[str, Any],
-                   tmpl_name: str,
-                   code: int = 200
-                   ) -> None:
-        """Send ctx as proper HTTP response."""
+    def _send_page(
+            self, ctx: dict[str, Any], tmpl_name: str, code: int = 200
+            ) -> None:
+        """HTTP-send ctx as HTML or JSON, as defined by .server.render_mode.
+
+        The differentiation by .server.render_mode serves to allow easily
+        comparable JSON responses for automatic testing.
+        """
         body: str
         headers: list[tuple[str, str]] = []
         if 'html' == self.server.render_mode:
@@ -154,37 +156,42 @@ class TaskHandler(BaseHTTPRequestHandler):
         self.wfile.write(bytes(body, 'utf-8'))
 
     def _ctx_to_json(self, ctx: dict[str, object]) -> str:
-        """Render ctx into JSON string."""
+        """Render ctx into JSON string.
+
+        Flattens any objects that json.dumps might not want to serialize, and
+        turns occurrences of BaseModel objects into listings of their .id_, to
+        be resolved to a full dict inside a top-level '_library' dictionary,
+        to avoid endless and circular nesting.
+        """
 
-        def flatten(node: object, library: dict[str, dict[str | int, object]]
-                    ) -> Any:
+        def flatten(node: object) -> object:
 
-            def update_library_with(item: Any,
-                                    library: dict[str, dict[str | int, object]]
-                                    ) -> None:
+            def update_library_with(
+                    item: BaseModel[int] | BaseModel[str]) -> None:
                 cls_name = item.__class__.__name__
                 if cls_name not in library:
                     library[cls_name] = {}
                 if item.id_ not in library[cls_name]:
                     d, refs = item.as_dict_and_refs
-                    library[cls_name][item.id_] = d
+                    id_key = '?' if item.id_ is None else item.id_
+                    library[cls_name][id_key] = d
                     for ref in refs:
-                        update_library_with(ref, library)
+                        update_library_with(ref)
 
             if isinstance(node, BaseModel):
-                update_library_with(node, library)
+                update_library_with(node)
                 return node.id_
             if isinstance(node, DictableNode):
                 d, refs = node.as_dict_and_refs
                 for ref in refs:
-                    update_library_with(ref, library)
+                    update_library_with(ref)
                 return d
             if isinstance(node, (list, tuple)):
-                return [flatten(item, library) for item in node]
+                return [flatten(item) for item in node]
             if isinstance(node, dict):
                 d = {}
                 for k, v in node.items():
-                    d[k] = flatten(v, library)
+                    d[k] = flatten(v)
                 return d
             if isinstance(node, HandledException):
                 return str(node)
@@ -192,7 +199,7 @@ class TaskHandler(BaseHTTPRequestHandler):
 
         library: dict[str, dict[str | int, object]] = {}
         for k, v in ctx.items():
-            ctx[k] = flatten(v, library)
+            ctx[k] = flatten(v)
         ctx['_library'] = library
         return json_dumps(ctx)
 
index 01297d479700b483c56bc1c1cfd60f41f3f74928..0851bb5de73a9e34a8af8bc7e2305d51552eed9e 100644 (file)
@@ -322,11 +322,8 @@ class TestsWithServer(TestCaseWithServer):
         expected['_library']['Todo']['2']['parents'] = [1]
         expected['_library']['Todo']['1']['children'] = [2]
         expected['steps_todo_to_process'] = [{
-            'children': [],
-            'fillable': False,
-            'node_id': 1,
-            'process': None,
-            'todo': 2}]
+            'children': [], 'fillable': False,
+            'node_id': 1, 'process': None, 'todo': 2}]
         self.check_post({'adopt': 2}, '/todo?id=1')
         self.check_json_get('/todo?id=1', expected)
         # # test todo1 cannot be set done with todo2 not done yet