reacton 1.10.1__tar.gz → 1.10.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {reacton-1.10.1 → reacton-1.10.2}/PKG-INFO +1 -1
- reacton-1.10.2/reacton/_version.py +1 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/core.py +33 -23
- {reacton-1.10.1 → reacton-1.10.2}/reacton/core_test.py +60 -0
- reacton-1.10.1/reacton/_version.py +0 -1
- {reacton-1.10.1 → reacton-1.10.2}/.gitignore +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/LICENSE +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/pyproject.toml +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/react_ipywidgets/__init__.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/react_ipywidgets/core.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/__init__.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/bqplot.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/deprecated/__init__.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/deprecated/ipyvuetify.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/find.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/find_test.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/generate.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/generate_test.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/ipycanvas.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/ipyvue.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/ipyvue_test.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/ipyvuetify.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/ipywidgets.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/logging.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/patch.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/patch_display.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/py.typed +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/rx.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/test.vue +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/test_rx.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/utils.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/utils_test.py +0 -0
- {reacton-1.10.1 → reacton-1.10.2}/reacton/work.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.10.2"
|
|
@@ -1322,29 +1322,33 @@ class _RenderContext:
|
|
|
1322
1322
|
self._remove_element(self.element, default_key="/", parent_key=ROOT_KEY)
|
|
1323
1323
|
logger.info("Removing elements done.")
|
|
1324
1324
|
assert self.context is self.context_root
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1325
|
+
# everything below used to run outside the lock: a render() that was
|
|
1326
|
+
# blocked on the lock could then interleave with this teardown and
|
|
1327
|
+
# find self.context None mid-render (AssertionError in
|
|
1328
|
+
# Element.__exit__, seen in production behind a prompt kernel close)
|
|
1329
|
+
if self.container:
|
|
1330
|
+
self.container.close()
|
|
1331
|
+
if isinstance(self.container, widgets.DOMWidget) and self.container.layout is not None:
|
|
1332
|
+
self.container.layout.close()
|
|
1333
|
+
if self._shared_elements:
|
|
1334
|
+
raise RuntimeError(f"Element not cleaned up: {self._shared_elements}")
|
|
1335
|
+
if self._orphans:
|
|
1336
|
+
orphan_widgets = set([_get_widgets_dict()[k] for k in self._orphans])
|
|
1337
|
+
raise RuntimeError(f"Orphan widgets not cleaned up for widgets: {orphan_widgets}")
|
|
1338
|
+
exceptions = [*self.context.exceptions_children, *self.context_root.exceptions_self]
|
|
1339
|
+
# break the reference cycles through the tree (see _teardown_component_context);
|
|
1340
|
+
# _closing stays True, making stray setters and event handlers no-ops
|
|
1341
|
+
for context in all_contexts:
|
|
1342
|
+
_teardown_component_context(context)
|
|
1343
|
+
self.context = None
|
|
1344
|
+
self.context_root = None # type: ignore
|
|
1345
|
+
# the root element, container and root widget keep the widget tree alive, and
|
|
1346
|
+
# widgets reference their elements, whose kwargs hold user callbacks - which
|
|
1347
|
+
# capture use_state setters and therefore this render context
|
|
1348
|
+
self.element = None # type: ignore
|
|
1349
|
+
self.container = None
|
|
1350
|
+
self.last_root_widget = None
|
|
1351
|
+
self._old_element_ids.clear()
|
|
1348
1352
|
if exceptions:
|
|
1349
1353
|
raise exceptions[0]
|
|
1350
1354
|
|
|
@@ -1547,6 +1551,12 @@ class _RenderContext:
|
|
|
1547
1551
|
self._lock_thread = threading.current_thread()
|
|
1548
1552
|
if was_locked:
|
|
1549
1553
|
logger.info("Mutex released, continuing render phase")
|
|
1554
|
+
if self._closing or self.context is None:
|
|
1555
|
+
# close() won the race for the lock (a disconnect can close the
|
|
1556
|
+
# kernel while an update was waiting to render): the tree is
|
|
1557
|
+
# torn down, there is nothing to render into anymore
|
|
1558
|
+
logger.info("Render requested on a closing/closed render context, ignoring")
|
|
1559
|
+
return container
|
|
1550
1560
|
prev_rc = getattr(local, "rc", None)
|
|
1551
1561
|
try:
|
|
1552
1562
|
local.rc = self
|
|
@@ -2963,6 +2963,66 @@ def test_close_when_overridden():
|
|
|
2963
2963
|
rc.close()
|
|
2964
2964
|
|
|
2965
2965
|
|
|
2966
|
+
def test_render_after_close_is_noop():
|
|
2967
|
+
# A disconnect can close the kernel while an update is waiting on the render
|
|
2968
|
+
# lock: the late render used to proceed into the torn-down tree and hit
|
|
2969
|
+
# "assert rc.context is not None" in Element.__exit__ (seen in production).
|
|
2970
|
+
# A render on a closed/closing context must be a no-op instead.
|
|
2971
|
+
@react.component
|
|
2972
|
+
def App():
|
|
2973
|
+
value, _set_value = react.use_state(1)
|
|
2974
|
+
return w.Label(value=str(value))
|
|
2975
|
+
|
|
2976
|
+
box, rc = react.render(App(), handle_error=False)
|
|
2977
|
+
rc.close()
|
|
2978
|
+
rc.render(App(), box) # must not raise, must not render
|
|
2979
|
+
|
|
2980
|
+
|
|
2981
|
+
def test_close_render_race():
|
|
2982
|
+
# close() in one thread while renders queue behind the lock in another:
|
|
2983
|
+
# neither thread may raise (the close teardown must not interleave with a
|
|
2984
|
+
# render that was blocked on the lock).
|
|
2985
|
+
import threading
|
|
2986
|
+
|
|
2987
|
+
for _ in range(20):
|
|
2988
|
+
|
|
2989
|
+
@react.component
|
|
2990
|
+
def App():
|
|
2991
|
+
value, _set_value = react.use_state(1)
|
|
2992
|
+
return w.Label(value=str(value))
|
|
2993
|
+
|
|
2994
|
+
box, rc = react.render(App(), handle_error=False)
|
|
2995
|
+
el = App()
|
|
2996
|
+
errors: List[BaseException] = []
|
|
2997
|
+
barrier = threading.Barrier(2)
|
|
2998
|
+
|
|
2999
|
+
def do_render(rc=rc, el=el, box=box, errors=errors, barrier=barrier):
|
|
3000
|
+
try:
|
|
3001
|
+
barrier.wait()
|
|
3002
|
+
rc.render(el, box)
|
|
3003
|
+
except BaseException as e: # noqa
|
|
3004
|
+
errors.append(e)
|
|
3005
|
+
|
|
3006
|
+
def do_close(rc=rc, errors=errors, barrier=barrier):
|
|
3007
|
+
try:
|
|
3008
|
+
barrier.wait()
|
|
3009
|
+
rc.close()
|
|
3010
|
+
except BaseException as e: # noqa
|
|
3011
|
+
errors.append(e)
|
|
3012
|
+
|
|
3013
|
+
t1 = threading.Thread(target=do_render)
|
|
3014
|
+
t2 = threading.Thread(target=do_close)
|
|
3015
|
+
t1.start()
|
|
3016
|
+
t2.start()
|
|
3017
|
+
t1.join()
|
|
3018
|
+
t2.join()
|
|
3019
|
+
# whichever won the lock, the loser must not blow up; if render won,
|
|
3020
|
+
# close ran after and the context must still close cleanly
|
|
3021
|
+
if not rc._closing:
|
|
3022
|
+
rc.close()
|
|
3023
|
+
assert not errors, errors
|
|
3024
|
+
|
|
3025
|
+
|
|
2966
3026
|
def test_render_perf_child_only():
|
|
2967
3027
|
render_child = unittest.mock.Mock()
|
|
2968
3028
|
render_main = unittest.mock.Mock()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.10.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|