Skip to content

Commit 5bdcb33

Browse files
committed
monkey path mime bundle method
1 parent c7eaad1 commit 5bdcb33

File tree

6 files changed

+200
-126
lines changed

6 files changed

+200
-126
lines changed

notebooks/introduction.ipynb

Lines changed: 128 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,7 @@
88
"\n",
99
"---\n",
1010
"\n",
11-
"[ReactPy](https://reactpy.dev/) is a library for building user interfaces in Python without Javascript. ReactPy interfaces are made from components which look and behave similarly to those found in [ReactJS](https://reactjs.org/). Designed with simplicity in mind, ReactPy can be used by those without web development experience while also being powerful enough to grow with your ambitions.\n",
12-
"\n",
13-
"\n",
14-
"\n",
15-
"# Getting Started\n",
16-
"\n",
17-
"Then, before anything else, do one of the following:\n",
18-
"\n",
19-
"1. At the top of your notebook run\n",
20-
"\n",
21-
" ```python\n",
22-
" import reactpy_jupyter\n",
23-
" ```\n",
24-
"\n",
25-
"2. Register `reactpy_jupyter` as a permanant IPython extension in [your config file](https://ipython.readthedocs.io/en/stable/config/intro.html#introduction-to-ipython-configuration):\n",
26-
"\n",
27-
" ```python\n",
28-
" c.InteractiveShellApp.extensions = ['reactpy_jupyter']\n",
29-
" ```\n",
30-
"\n",
31-
"For the purposes of this tutorial, you'll want to do the first:"
32-
]
33-
},
34-
{
35-
"cell_type": "code",
36-
"execution_count": null,
37-
"metadata": {
38-
"tags": []
39-
},
40-
"outputs": [],
41-
"source": [
42-
"import reactpy_jupyter"
11+
"[ReactPy](https://reactpy.dev/) is a library for building user interfaces in Python without Javascript. ReactPy interfaces are made from components which look and behave similarly to those found in [ReactJS](https://reactjs.org/). Designed with simplicity in mind, ReactPy can be used by those without web development experience while also being powerful enough to grow with your ambitions."
4312
]
4413
},
4514
{
@@ -53,11 +22,27 @@
5322
},
5423
{
5524
"cell_type": "code",
56-
"execution_count": null,
25+
"execution_count": 1,
5726
"metadata": {
5827
"tags": []
5928
},
60-
"outputs": [],
29+
"outputs": [
30+
{
31+
"data": {
32+
"application/vnd.jupyter.widget-view+json": {
33+
"model_id": "e0c3c63d3e1c4d88a71bb7bbc6ae45e0",
34+
"version_major": 2,
35+
"version_minor": 0
36+
},
37+
"text/plain": [
38+
"LayoutWidget(Layout(ContextProvider(<function context at 0x7f7986b10cc0>)))"
39+
]
40+
},
41+
"execution_count": 1,
42+
"metadata": {},
43+
"output_type": "execute_result"
44+
}
45+
],
6146
"source": [
6247
"from reactpy import component, html\n",
6348
"\n",
@@ -83,11 +68,27 @@
8368
},
8469
{
8570
"cell_type": "code",
86-
"execution_count": null,
71+
"execution_count": 2,
8772
"metadata": {
8873
"tags": []
8974
},
90-
"outputs": [],
75+
"outputs": [
76+
{
77+
"data": {
78+
"application/vnd.jupyter.widget-view+json": {
79+
"model_id": "a87baeb77dcc44e1a29ab154df1dd8a7",
80+
"version_major": 2,
81+
"version_minor": 0
82+
},
83+
"text/plain": [
84+
"LayoutWidget(Layout(ContextProvider(<function context at 0x7f7986b10cc0>)))"
85+
]
86+
},
87+
"execution_count": 2,
88+
"metadata": {},
89+
"output_type": "execute_result"
90+
}
91+
],
9192
"source": [
9293
"from reactpy import component, html\n",
9394
"\n",
@@ -136,11 +137,27 @@
136137
},
137138
{
138139
"cell_type": "code",
139-
"execution_count": null,
140+
"execution_count": 3,
140141
"metadata": {
141142
"tags": []
142143
},
143-
"outputs": [],
144+
"outputs": [
145+
{
146+
"data": {
147+
"application/vnd.jupyter.widget-view+json": {
148+
"model_id": "920e5f89aba644528257e20deae25f2e",
149+
"version_major": 2,
150+
"version_minor": 0
151+
},
152+
"text/plain": [
153+
"LayoutWidget(Layout(ContextProvider(<function context at 0x7f7986b10cc0>)))"
154+
]
155+
},
156+
"execution_count": 3,
157+
"metadata": {},
158+
"output_type": "execute_result"
159+
}
160+
],
144161
"source": [
145162
"import json\n",
146163
"from pathlib import Path\n",
@@ -190,9 +207,25 @@
190207
},
191208
{
192209
"cell_type": "code",
193-
"execution_count": null,
210+
"execution_count": 4,
194211
"metadata": {},
195-
"outputs": [],
212+
"outputs": [
213+
{
214+
"data": {
215+
"application/vnd.jupyter.widget-view+json": {
216+
"model_id": "67cb81abf4ce4c9e991a9771607b9d5e",
217+
"version_major": 2,
218+
"version_minor": 0
219+
},
220+
"text/plain": [
221+
"LayoutWidget(Layout(ContextProvider(<function context at 0x7f7986b10cc0>)))"
222+
]
223+
},
224+
"execution_count": 4,
225+
"metadata": {},
226+
"output_type": "execute_result"
227+
}
228+
],
196229
"source": [
197230
"from reactpy_jupyter import from_widget\n",
198231
"from ipywidgets import IntSlider\n",
@@ -212,7 +245,7 @@
212245
},
213246
{
214247
"cell_type": "code",
215-
"execution_count": null,
248+
"execution_count": 5,
216249
"metadata": {
217250
"tags": []
218251
},
@@ -249,11 +282,27 @@
249282
},
250283
{
251284
"cell_type": "code",
252-
"execution_count": null,
285+
"execution_count": 6,
253286
"metadata": {
254287
"tags": []
255288
},
256-
"outputs": [],
289+
"outputs": [
290+
{
291+
"data": {
292+
"application/vnd.jupyter.widget-view+json": {
293+
"model_id": "c586a0f0fe0b44a3bab173ccb9fa52d8",
294+
"version_major": 2,
295+
"version_minor": 0
296+
},
297+
"text/plain": [
298+
"LayoutWidget(Layout(ContextProvider(<function context at 0x7f7986b10cc0>)))"
299+
]
300+
},
301+
"execution_count": 6,
302+
"metadata": {},
303+
"output_type": "execute_result"
304+
}
305+
],
257306
"source": [
258307
"from ipywidgets import IntSlider\n",
259308
"\n",
@@ -269,11 +318,27 @@
269318
},
270319
{
271320
"cell_type": "code",
272-
"execution_count": null,
321+
"execution_count": 8,
273322
"metadata": {
274323
"tags": []
275324
},
276-
"outputs": [],
325+
"outputs": [
326+
{
327+
"data": {
328+
"application/vnd.jupyter.widget-view+json": {
329+
"model_id": "a0180ea4781b4dda9f77d1be75fbcad3",
330+
"version_major": 2,
331+
"version_minor": 0
332+
},
333+
"text/plain": [
334+
"Box(children=(IntSlider(value=0, readout=False), LayoutWidget(Layout(ContextProvider(<function context at 0x7f…"
335+
]
336+
},
337+
"execution_count": 8,
338+
"metadata": {},
339+
"output_type": "execute_result"
340+
}
341+
],
277342
"source": [
278343
"from ipywidgets import Box\n",
279344
"from reactpy_jupyter import to_widget\n",
@@ -293,9 +358,25 @@
293358
},
294359
{
295360
"cell_type": "code",
296-
"execution_count": null,
361+
"execution_count": 9,
297362
"metadata": {},
298-
"outputs": [],
363+
"outputs": [
364+
{
365+
"data": {
366+
"application/vnd.jupyter.widget-view+json": {
367+
"model_id": "abeb4c3af2164e00927b317e5dfb6569",
368+
"version_major": 2,
369+
"version_minor": 0
370+
},
371+
"text/plain": [
372+
"Box(children=(LayoutWidget(Layout(ContextProvider(<function context at 0x7f7986b10cc0>))), LayoutWidget(Layout…"
373+
]
374+
},
375+
"execution_count": 9,
376+
"metadata": {},
377+
"output_type": "execute_result"
378+
}
379+
],
299380
"source": [
300381
"slider = IntSlider(readout=False)\n",
301382
"slider_observer_constructor = to_widget(SliderObserver)\n",

reactpy_jupyter.pth

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import reactpy_jupyter

reactpy_jupyter/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
# Distributed under the terms of the Modified BSD License.
66

77
from . import jupyter_server_extension
8+
from .monkey_patch import execute_patch
89
from .widget_component import from_widget
910
from .import_resources import setup_import_resources
10-
from .ipython_extension import load_ipython_extension, unload_ipython_extension
1111
from .layout_widget import to_widget, run, set_import_source_base_url
1212

1313
__version__ = "0.8.1" # DO NOT MODIFY
@@ -24,3 +24,4 @@
2424

2525

2626
setup_import_resources()
27+
execute_patch()

reactpy_jupyter/ipython_extension.py

Lines changed: 0 additions & 57 deletions
This file was deleted.

reactpy_jupyter/monkey_patch.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from typing import Any
2+
from weakref import finalize
3+
from reactpy_jupyter.layout_widget import to_widget
4+
from reactpy.core.component import Component
5+
6+
# we can't track the widgets by adding them as a hidden attribute to the component
7+
# because Component has __slots__ defined
8+
LIVE_WIDGETS: dict[int, Any] = {}
9+
10+
11+
def execute_patch() -> None:
12+
"""Monkey patch ReactPy's Component class to display as a Jupyter widget"""
13+
14+
def _repr_mimebundle_(self: Component, *a, **kw) -> None:
15+
self_id = id(self)
16+
if self_id not in LIVE_WIDGETS:
17+
widget = LIVE_WIDGETS[self_id] = to_widget(self)
18+
finalize(self, lambda: LIVE_WIDGETS.pop(self_id, None))
19+
else:
20+
widget = LIVE_WIDGETS[self_id]
21+
return widget._repr_mimebundle_(*a, **kw)
22+
23+
Component._repr_mimebundle_ = _repr_mimebundle_

0 commit comments

Comments
 (0)