Browse Source

maint: refine converting between scope name and dom id

wangweimin 4 years ago
parent
commit
855d08d468
4 changed files with 40 additions and 37 deletions
  1. 2 0
      docs/spec.rst
  2. 25 7
      pywebio/io_ctrl.py
  3. 11 27
      pywebio/output.py
  4. 2 3
      webiojs/src/models/output.ts

+ 2 - 0
docs/spec.rst

@@ -191,6 +191,8 @@ The ``spec`` fields of ``output`` commands:
 * position: int, see :ref:`scope - User manual <scope_param>`
 * position: int, see :ref:`scope - User manual <scope_param>`
 * Other attributes of different types
 * Other attributes of different types
 
 
+``container_selector`` and ``container_dom_id`` is used to implement output context manager.
+
 Unique attributes of different types:
 Unique attributes of different types:
 
 
 * type: markdown
 * type: markdown

+ 25 - 7
pywebio/io_ctrl.py

@@ -13,6 +13,24 @@ from .utils import random_str
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
 
 
+def scope2dom(name, no_css_selector=False):
+    """Get the CSS selector/element name actually used in the front-end html page
+
+    :param str/tuple name: When it is str, it is regarded as the Dom ID name;
+       when tuple, the format is (css selector, element name)
+    """
+    selector = '#'
+    if isinstance(name, tuple):
+        selector, name = name
+
+    name = name.replace(' ', '-')
+
+    if no_css_selector:
+        selector = ''
+
+    return '%spywebio-scope-%s' % (selector, name)
+
+
 class Output:
 class Output:
     """ ``put_xxx()`` 类函数的返回值
     """ ``put_xxx()`` 类函数的返回值
 
 
@@ -73,12 +91,12 @@ class Output:
     def __enter__(self):
     def __enter__(self):
         if not self.enabled_context_manager:
         if not self.enabled_context_manager:
             raise RuntimeError("This output function can't be used as context manager!")
             raise RuntimeError("This output function can't be used as context manager!")
-        r = self.custom_enter(self) if self.custom_enter else None
-        if r is not None:
-            return r
+        if self.custom_enter:
+            return self.custom_enter(self)
+
         self.container_dom_id = self.container_dom_id or random_str(10)
         self.container_dom_id = self.container_dom_id or random_str(10)
         self.spec['container_selector'] = self.container_selector
         self.spec['container_selector'] = self.container_selector
-        self.spec['container_dom_id'] = self.container_dom_id
+        self.spec['container_dom_id'] = scope2dom(self.container_dom_id, no_css_selector=True)
         self.send()
         self.send()
         get_current_session().push_scope(self.container_dom_id)
         get_current_session().push_scope(self.container_dom_id)
         return self.container_dom_id
         return self.container_dom_id
@@ -89,9 +107,9 @@ class Output:
         it means that the context manager can handle the exception,
         it means that the context manager can handle the exception,
         so that the with statement terminates the propagation of the exception
         so that the with statement terminates the propagation of the exception
         """
         """
-        r = self.custom_exit(self, exc_type=exc_type, exc_val=exc_val, exc_tb=exc_tb) if self.custom_exit else None
-        if r is not None:
-            return r
+        if self.custom_exit:
+            return self.custom_exit(self, exc_type=exc_type, exc_val=exc_val, exc_tb=exc_tb)
+
         get_current_session().pop_scope()
         get_current_session().pop_scope()
         return False  # Propagate Exception
         return False  # Propagate Exception
 
 

+ 11 - 27
pywebio/output.py

@@ -159,7 +159,7 @@ from collections.abc import Mapping, Sequence
 from functools import wraps
 from functools import wraps
 from typing import Union
 from typing import Union
 
 
-from .io_ctrl import output_register_callback, send_msg, Output, safely_destruct_output_when_exp, OutputList
+from .io_ctrl import output_register_callback, send_msg, Output, safely_destruct_output_when_exp, OutputList, scope2dom
 from .session import get_current_session, download
 from .session import get_current_session, download
 from .utils import random_str, iscoroutinefunction, is_html_safe_value
 from .utils import random_str, iscoroutinefunction, is_html_safe_value
 
 
@@ -206,22 +206,6 @@ class Scope:
 _scope_name_allowed_chars = set(string.ascii_letters + string.digits + '_-')
 _scope_name_allowed_chars = set(string.ascii_letters + string.digits + '_-')
 
 
 
 
-def _parse_scope(name, no_css_selector=False):
-    """Get the CSS selector/element name actually used in the front-end html page
-
-    :param str/tuple name: When it is str, it is regarded as the Dom ID name;
-       when tuple, the format is (css selector, element name)
-    """
-    selector = '#'
-    if isinstance(name, tuple):
-        selector, name = name
-
-    name = name.replace(' ', '-')
-
-    if no_css_selector:
-        selector = ''
-
-    return '%spywebio-scope-%s' % (selector, name)
 
 
 
 
 def set_scope(name, container_scope=Scope.Current, position=OutputPosition.BOTTOM, if_exist=None):
 def set_scope(name, container_scope=Scope.Current, position=OutputPosition.BOTTOM, if_exist=None):
@@ -244,8 +228,8 @@ def set_scope(name, container_scope=Scope.Current, position=OutputPosition.BOTTO
         container_scope = get_current_session().get_scope_name(container_scope)
         container_scope = get_current_session().get_scope_name(container_scope)
 
 
     assert is_html_safe_value(name), "Scope name only allow letter/digit/'_'/'-' char."
     assert is_html_safe_value(name), "Scope name only allow letter/digit/'_'/'-' char."
-    send_msg('output_ctl', dict(set_scope=_parse_scope(name, no_css_selector=True),
-                                container=_parse_scope(container_scope),
+    send_msg('output_ctl', dict(set_scope=scope2dom(name, no_css_selector=True),
+                                container=scope2dom(container_scope),
                                 position=position, if_exist=if_exist))
                                 position=position, if_exist=if_exist))
 
 
 
 
@@ -272,7 +256,7 @@ def clear(scope=Scope.Current):
     """
     """
     if isinstance(scope, int):
     if isinstance(scope, int):
         scope = get_current_session().get_scope_name(scope)
         scope = get_current_session().get_scope_name(scope)
-    send_msg('output_ctl', dict(clear=_parse_scope(scope)))
+    send_msg('output_ctl', dict(clear=scope2dom(scope)))
 
 
 
 
 def remove(scope=Scope.Current):
 def remove(scope=Scope.Current):
@@ -283,7 +267,7 @@ def remove(scope=Scope.Current):
     if isinstance(scope, int):
     if isinstance(scope, int):
         scope = get_current_session().get_scope_name(scope)
         scope = get_current_session().get_scope_name(scope)
     assert scope != 'ROOT', "Can not remove `ROOT` scope."
     assert scope != 'ROOT', "Can not remove `ROOT` scope."
-    send_msg('output_ctl', dict(remove=_parse_scope(scope)))
+    send_msg('output_ctl', dict(remove=scope2dom(scope)))
 
 
 
 
 def scroll_to(scope=Scope.Current, position=Position.TOP):
 def scroll_to(scope=Scope.Current, position=Position.TOP):
@@ -299,7 +283,7 @@ def scroll_to(scope=Scope.Current, position=Position.TOP):
     """
     """
     if isinstance(scope, int):
     if isinstance(scope, int):
         scope = get_current_session().get_scope_name(scope)
         scope = get_current_session().get_scope_name(scope)
-    send_msg('output_ctl', dict(scroll_to=_parse_scope(scope), position=position))
+    send_msg('output_ctl', dict(scroll_to=scope2dom(scope), position=position))
 
 
 
 
 def _get_output_spec(type, scope, position, **other_spec):
 def _get_output_spec(type, scope, position, **other_spec):
@@ -323,7 +307,7 @@ def _get_output_spec(type, scope, position, **other_spec):
     else:
     else:
         scope_name = scope
         scope_name = scope
 
 
-    spec['scope'] = _parse_scope(scope_name)
+    spec['scope'] = scope2dom(scope_name)
     spec['position'] = position
     spec['position'] = position
 
 
     return spec
     return spec
@@ -1379,7 +1363,7 @@ def output(*contents):
             for o in outputs:
             for o in outputs:
                 if not isinstance(o, Output):
                 if not isinstance(o, Output):
                     o = put_text(o)
                     o = put_text(o)
-                o.spec['scope'] = _parse_scope(self.scope)
+                o.spec['scope'] = scope2dom(self.scope)
                 o.spec['position'] = OutputPosition.BOTTOM
                 o.spec['position'] = OutputPosition.BOTTOM
                 o.send()
                 o.send()
 
 
@@ -1393,7 +1377,7 @@ def output(*contents):
             for acc, o in enumerate(outputs):
             for acc, o in enumerate(outputs):
                 if not isinstance(o, Output):
                 if not isinstance(o, Output):
                     o = put_text(o)
                     o = put_text(o)
-                o.spec['scope'] = _parse_scope(self.scope)
+                o.spec['scope'] = scope2dom(self.scope)
                 o.spec['position'] = idx + direction * acc
                 o.spec['position'] = idx + direction * acc
                 o.send()
                 o.send()
 
 
@@ -1408,7 +1392,7 @@ def output(*contents):
             {{/contents}}
             {{/contents}}
         </div>"""
         </div>"""
     out_spec = put_widget(template=tpl,
     out_spec = put_widget(template=tpl,
-                          data=dict(contents=contents, dom_class_name=_parse_scope(dom_name, no_css_selector=True)))
+                          data=dict(contents=contents, dom_class_name=scope2dom(dom_name, no_css_selector=True)))
     return OutputHandler(Output.dump_dict(out_spec), ('.', dom_name))
     return OutputHandler(Output.dump_dict(out_spec), ('.', dom_name))
 
 
 
 
@@ -1542,7 +1526,7 @@ def popup(title, content=None, size=PopupSize.NORMAL, implicit_close=True, closa
 
 
     send_msg(cmd='popup', spec=dict(content=Output.dump_dict(content), title=title, size=size,
     send_msg(cmd='popup', spec=dict(content=Output.dump_dict(content), title=title, size=size,
                                     implicit_close=implicit_close, closable=closable,
                                     implicit_close=implicit_close, closable=closable,
-                                    dom_id=_parse_scope(dom_id, no_css_selector=True)))
+                                    dom_id=scope2dom(dom_id, no_css_selector=True)))
 
 
     return use_scope_(dom_id)
     return use_scope_(dom_id)
 
 

+ 2 - 3
webiojs/src/models/output.ts

@@ -224,11 +224,10 @@ export function getWidgetElement(spec: any) {
         elem.attr({"style": old_style + spec.style});
         elem.attr({"style": old_style + spec.style});
     }
     }
     if (spec.container_dom_id) {
     if (spec.container_dom_id) {
-        let dom_id = 'pywebio-scope-' + spec.container_dom_id;
         if (spec.container_selector)
         if (spec.container_selector)
-            elem.find(spec.container_selector).attr('id', dom_id);
+            elem.find(spec.container_selector).attr('id', spec.container_dom_id);
         else
         else
-            elem.attr('id', dom_id);
+            elem.attr('id', spec.container_dom_id);
     }
     }
     return elem;
     return elem;
 }
 }