reactpy_django 5.0.0__py3-none-any.whl
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.
- js/node_modules/@pyscript/core/src/stdlib/pyscript/__init__.py +61 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/display.py +177 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/event_handling.py +76 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/fetch.py +87 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/ffi.py +18 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/flatted.py +148 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/magic_js.py +79 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/storage.py +60 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/util.py +33 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/web.py +1176 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/websocket.py +71 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/workers.py +43 -0
- js/node_modules/flatted/python/flatted.py +149 -0
- js/node_modules/flatted/python/test.py +63 -0
- js/node_modules/polyscript/python/mip.py +288 -0
- reactpy_django/__init__.py +32 -0
- reactpy_django/apps.py +11 -0
- reactpy_django/checks.py +558 -0
- reactpy_django/clean.py +141 -0
- reactpy_django/components.py +284 -0
- reactpy_django/config.py +128 -0
- reactpy_django/database.py +31 -0
- reactpy_django/decorators.py +65 -0
- reactpy_django/exceptions.py +34 -0
- reactpy_django/hooks.py +469 -0
- reactpy_django/html.py +3 -0
- reactpy_django/http/__init__.py +0 -0
- reactpy_django/http/urls.py +18 -0
- reactpy_django/http/views.py +62 -0
- reactpy_django/management/__init__.py +0 -0
- reactpy_django/management/commands/__init__.py +0 -0
- reactpy_django/management/commands/clean_reactpy.py +37 -0
- reactpy_django/migrations/0001_initial.py +25 -0
- reactpy_django/migrations/0002_rename_created_at_componentparams_last_accessed.py +17 -0
- reactpy_django/migrations/0003_componentsession_delete_componentparams.py +28 -0
- reactpy_django/migrations/0004_config.py +27 -0
- reactpy_django/migrations/0005_alter_componentsession_last_accessed.py +17 -0
- reactpy_django/migrations/0006_userdatamodel.py +28 -0
- reactpy_django/migrations/__init__.py +0 -0
- reactpy_django/models.py +48 -0
- reactpy_django/py.typed +1 -0
- reactpy_django/pyscript/__init__.py +0 -0
- reactpy_django/pyscript/component_template.py +26 -0
- reactpy_django/pyscript/layout_handler.py +139 -0
- reactpy_django/router/__init__.py +5 -0
- reactpy_django/router/converters.py +8 -0
- reactpy_django/router/resolvers.py +22 -0
- reactpy_django/static/reactpy_django/client.js +1 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom-esm.js +769 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom-factory.js +705 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom-umd.js +777 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom-umd.min.js +1 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom.js +771 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror-BEtcgaoQ.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror-BEtcgaoQ.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_commands-DDxffOmd.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_commands-DDxffOmd.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_lang-python-CnWnFqxD.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_lang-python-CnWnFqxD.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_language-CjmvX4ix.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_language-CjmvX4ix.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_state-D1qTXrff.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_state-D1qTXrff.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_view-DVb8uYMr.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_view-DVb8uYMr.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/core-CjO3FOKB.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/core-CjO3FOKB.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/core.css +1 -0
- reactpy_django/static/reactpy_django/pyscript/core.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/core.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/deprecations-manager-pFtn19mE.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/deprecations-manager-pFtn19mE.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/error-tq-z48YI.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/error-tq-z48YI.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/index-S1Do43bx.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/index-S1Do43bx.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/mpy-DovD7Qjy.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/mpy-DovD7Qjy.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/py-BUsUWVJg.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/py-BUsUWVJg.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/py-editor-CeySmmer.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/py-editor-CeySmmer.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/py-terminal-CH_wV7wQ.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/py-terminal-CH_wV7wQ.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/storage.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/storage.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/toml-CvAfdf9_.js +3 -0
- reactpy_django/static/reactpy_django/pyscript/toml-CvAfdf9_.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/toml-DiUM0_qs.js +3 -0
- reactpy_django/static/reactpy_django/pyscript/toml-DiUM0_qs.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/xterm-BY7uk_OU.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/xterm-BY7uk_OU.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/xterm-readline-CZfBw7ic.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/xterm-readline-CZfBw7ic.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/xterm.css +7 -0
- reactpy_django/static/reactpy_django/pyscript/xterm_addon-fit--gyF3PcZ.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/xterm_addon-fit--gyF3PcZ.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/xterm_addon-web-links-Cnej-nJ6.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/xterm_addon-web-links-Cnej-nJ6.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/zip-DrwYHuF9.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/zip-DrwYHuF9.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript-custom.css +3 -0
- reactpy_django/static/reactpy_django/pyscript-hide-debug.css +3 -0
- reactpy_django/templates/reactpy/component.html +27 -0
- reactpy_django/templates/reactpy/pyscript_component.html +2 -0
- reactpy_django/templates/reactpy/pyscript_setup.html +8 -0
- reactpy_django/templatetags/__init__.py +0 -0
- reactpy_django/templatetags/reactpy.py +250 -0
- reactpy_django/types.py +81 -0
- reactpy_django/utils.py +560 -0
- reactpy_django/websocket/__init__.py +0 -0
- reactpy_django/websocket/consumer.py +227 -0
- reactpy_django/websocket/paths.py +15 -0
- reactpy_django-5.0.0.dist-info/LICENSE.md +9 -0
- reactpy_django-5.0.0.dist-info/METADATA +152 -0
- reactpy_django-5.0.0.dist-info/RECORD +118 -0
- reactpy_django-5.0.0.dist-info/WHEEL +5 -0
- reactpy_django-5.0.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{% load static %}
|
|
2
|
+
|
|
3
|
+
{% if reactpy_failure and reactpy_debug_mode %}
|
|
4
|
+
<pre>{% firstof reactpy_error "UnknownError" %}: "{% firstof reactpy_dotted_path "UnknownPath" %}"</pre>
|
|
5
|
+
{% endif %}
|
|
6
|
+
|
|
7
|
+
{% if not reactpy_failure %}
|
|
8
|
+
{% if reactpy_class %}<div id="{{reactpy_uuid}}" class="{{reactpy_class}}"></div>{% endif %}
|
|
9
|
+
{% if not reactpy_class %}<div id="{{reactpy_uuid}}"></div>{% endif %}
|
|
10
|
+
{% if reactpy_prerender_html %}<div id="{{reactpy_uuid}}-prerender">{{reactpy_prerender_html|safe}}</div>{% endif %}
|
|
11
|
+
{% if reactpy_offline_html %}<div id="{{reactpy_uuid}}-offline" hidden>{{reactpy_offline_html|safe}}</div>{% endif %}
|
|
12
|
+
<script type="module" crossorigin="anonymous">
|
|
13
|
+
import { mountComponent } from "{% static 'reactpy_django/client.js' %}";
|
|
14
|
+
const mountElement = document.getElementById("{{reactpy_uuid}}");
|
|
15
|
+
mountComponent(
|
|
16
|
+
mountElement,
|
|
17
|
+
"{{reactpy_host}}",
|
|
18
|
+
"{{reactpy_url_prefix}}",
|
|
19
|
+
"{{reactpy_component_path}}",
|
|
20
|
+
"{{reactpy_resolved_web_modules_path}}",
|
|
21
|
+
Number("{{reactpy_reconnect_interval}}"),
|
|
22
|
+
Number("{{reactpy_reconnect_max_interval}}"),
|
|
23
|
+
Number("{{reactpy_reconnect_max_retries}}"),
|
|
24
|
+
Number("{{reactpy_reconnect_backoff_multiplier}}"),
|
|
25
|
+
);
|
|
26
|
+
</script>
|
|
27
|
+
{% endif %}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{% load static %}
|
|
2
|
+
<link rel="stylesheet" href="{% static 'reactpy_django/pyscript/core.css' %}" />
|
|
3
|
+
<link rel="stylesheet" href="{% static 'reactpy_django/pyscript-custom.css' %}" />
|
|
4
|
+
{% if not reactpy_debug_mode %}
|
|
5
|
+
<link rel="stylesheet" href="{% static 'reactpy_django/pyscript-hide-debug.css' %}" />
|
|
6
|
+
{% endif %}
|
|
7
|
+
<script type="module" async crossorigin="anonymous" src="{% static 'reactpy_django/pyscript/core.js' %}"></script>
|
|
8
|
+
<py-script async config='{{pyscript_config}}'>{{pyscript_layout_handler}}</py-script>
|
|
File without changes
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from logging import getLogger
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
|
|
6
|
+
from django import template
|
|
7
|
+
from django.http import HttpRequest
|
|
8
|
+
from django.urls import NoReverseMatch, reverse
|
|
9
|
+
from reactpy.core.types import ComponentConstructor, ComponentType, VdomDict
|
|
10
|
+
|
|
11
|
+
from reactpy_django import config as reactpy_config
|
|
12
|
+
from reactpy_django.exceptions import (
|
|
13
|
+
ComponentCarrierError,
|
|
14
|
+
ComponentDoesNotExistError,
|
|
15
|
+
ComponentParamError,
|
|
16
|
+
InvalidHostError,
|
|
17
|
+
OfflineComponentMissing,
|
|
18
|
+
)
|
|
19
|
+
from reactpy_django.utils import (
|
|
20
|
+
PYSCRIPT_LAYOUT_HANDLER,
|
|
21
|
+
extend_pyscript_config,
|
|
22
|
+
prerender_component,
|
|
23
|
+
render_pyscript_template,
|
|
24
|
+
save_component_params,
|
|
25
|
+
strtobool,
|
|
26
|
+
validate_component_args,
|
|
27
|
+
validate_host,
|
|
28
|
+
vdom_or_component_to_string,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
RESOLVED_WEB_MODULES_PATH = reverse("reactpy:web_modules", args=["/"]).strip("/")
|
|
33
|
+
except NoReverseMatch:
|
|
34
|
+
RESOLVED_WEB_MODULES_PATH = ""
|
|
35
|
+
register = template.Library()
|
|
36
|
+
_logger = getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@register.inclusion_tag("reactpy/component.html", takes_context=True)
|
|
40
|
+
def component(
|
|
41
|
+
context: template.RequestContext,
|
|
42
|
+
dotted_path: str,
|
|
43
|
+
*args,
|
|
44
|
+
host: str | None = None,
|
|
45
|
+
prerender: str = str(reactpy_config.REACTPY_PRERENDER),
|
|
46
|
+
offline: str = "",
|
|
47
|
+
**kwargs,
|
|
48
|
+
):
|
|
49
|
+
"""This tag is used to embed an existing ReactPy component into your HTML template.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
dotted_path: The dotted path to the component to render.
|
|
53
|
+
*args: The positional arguments to provide to the component.
|
|
54
|
+
|
|
55
|
+
Keyword Args:
|
|
56
|
+
class: The HTML class to apply to the top-level component div.
|
|
57
|
+
key: Force the component's root node to use a specific key value. Using \
|
|
58
|
+
key within a template tag is effectively useless.
|
|
59
|
+
host: The host to use for ReactPy connections. If set to `None`, \
|
|
60
|
+
the host will be automatically configured. \
|
|
61
|
+
Example values include: `localhost:8000`, `example.com`, `example.com/subdir`
|
|
62
|
+
prerender: Configures whether to pre-render this component, which \
|
|
63
|
+
enables SEO compatibility and reduces perceived latency.
|
|
64
|
+
offline: The dotted path to the component to render when the client is offline.
|
|
65
|
+
**kwargs: The keyword arguments to provide to the component.
|
|
66
|
+
|
|
67
|
+
Example ::
|
|
68
|
+
|
|
69
|
+
{% load reactpy %}
|
|
70
|
+
<!DOCTYPE html>
|
|
71
|
+
<html>
|
|
72
|
+
<body>
|
|
73
|
+
{% component "example_project.my_app.components.hello_world" recipient="World" %}
|
|
74
|
+
</body>
|
|
75
|
+
</html>
|
|
76
|
+
"""
|
|
77
|
+
request: HttpRequest | None = context.get("request")
|
|
78
|
+
perceived_host = (request.get_host() if request else "").strip("/")
|
|
79
|
+
host = (
|
|
80
|
+
host
|
|
81
|
+
or (
|
|
82
|
+
next(reactpy_config.REACTPY_DEFAULT_HOSTS)
|
|
83
|
+
if reactpy_config.REACTPY_DEFAULT_HOSTS
|
|
84
|
+
else ""
|
|
85
|
+
)
|
|
86
|
+
).strip("/")
|
|
87
|
+
is_local = not host or host.startswith(perceived_host)
|
|
88
|
+
uuid = str(uuid4())
|
|
89
|
+
class_ = kwargs.pop("class", "")
|
|
90
|
+
has_args = bool(args or kwargs)
|
|
91
|
+
user_component: ComponentConstructor | None = None
|
|
92
|
+
_prerender_html = ""
|
|
93
|
+
_offline_html = ""
|
|
94
|
+
|
|
95
|
+
# Validate the host
|
|
96
|
+
if host and reactpy_config.REACTPY_DEBUG_MODE:
|
|
97
|
+
try:
|
|
98
|
+
validate_host(host)
|
|
99
|
+
except InvalidHostError as e:
|
|
100
|
+
return failure_context(dotted_path, e)
|
|
101
|
+
|
|
102
|
+
# Fetch the component
|
|
103
|
+
if is_local:
|
|
104
|
+
user_component = reactpy_config.REACTPY_REGISTERED_COMPONENTS.get(dotted_path)
|
|
105
|
+
if not user_component:
|
|
106
|
+
msg = f"Component '{dotted_path}' is not registered as a root component. "
|
|
107
|
+
_logger.error(msg)
|
|
108
|
+
return failure_context(dotted_path, ComponentDoesNotExistError(msg))
|
|
109
|
+
|
|
110
|
+
# Validate the component args & kwargs
|
|
111
|
+
if is_local and reactpy_config.REACTPY_DEBUG_MODE:
|
|
112
|
+
try:
|
|
113
|
+
validate_component_args(user_component, *args, **kwargs)
|
|
114
|
+
except ComponentParamError as e:
|
|
115
|
+
_logger.error(str(e))
|
|
116
|
+
return failure_context(dotted_path, e)
|
|
117
|
+
|
|
118
|
+
# Store args & kwargs in the database (fetched by our websocket later)
|
|
119
|
+
if has_args:
|
|
120
|
+
try:
|
|
121
|
+
save_component_params(args, kwargs, uuid)
|
|
122
|
+
except Exception as e:
|
|
123
|
+
_logger.exception(
|
|
124
|
+
"An unknown error has occurred while saving component params for '%s'.",
|
|
125
|
+
dotted_path,
|
|
126
|
+
)
|
|
127
|
+
return failure_context(dotted_path, e)
|
|
128
|
+
|
|
129
|
+
# Pre-render the component, if requested
|
|
130
|
+
if strtobool(prerender):
|
|
131
|
+
if not is_local:
|
|
132
|
+
msg = "Cannot pre-render non-local components."
|
|
133
|
+
_logger.error(msg)
|
|
134
|
+
return failure_context(dotted_path, ComponentDoesNotExistError(msg))
|
|
135
|
+
if not user_component:
|
|
136
|
+
msg = "Cannot pre-render component that is not registered."
|
|
137
|
+
_logger.error(msg)
|
|
138
|
+
return failure_context(dotted_path, ComponentDoesNotExistError(msg))
|
|
139
|
+
if not request:
|
|
140
|
+
msg = (
|
|
141
|
+
"Cannot pre-render component without a HTTP request. Are you missing the "
|
|
142
|
+
"request context processor in settings.py:TEMPLATES['OPTIONS']['context_processors']?"
|
|
143
|
+
)
|
|
144
|
+
_logger.error(msg)
|
|
145
|
+
return failure_context(dotted_path, ComponentCarrierError(msg))
|
|
146
|
+
_prerender_html = prerender_component(
|
|
147
|
+
user_component, args, kwargs, uuid, request
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Fetch the offline component's HTML, if requested
|
|
151
|
+
if offline:
|
|
152
|
+
offline_component = reactpy_config.REACTPY_REGISTERED_COMPONENTS.get(offline)
|
|
153
|
+
if not offline_component:
|
|
154
|
+
msg = f"Cannot render offline component '{offline}'. It is not registered as a component."
|
|
155
|
+
_logger.error(msg)
|
|
156
|
+
return failure_context(dotted_path, OfflineComponentMissing(msg))
|
|
157
|
+
if not request:
|
|
158
|
+
msg = (
|
|
159
|
+
"Cannot render an offline component without a HTTP request. Are you missing the "
|
|
160
|
+
"request context processor in settings.py:TEMPLATES['OPTIONS']['context_processors']?"
|
|
161
|
+
)
|
|
162
|
+
_logger.error(msg)
|
|
163
|
+
return failure_context(dotted_path, ComponentCarrierError(msg))
|
|
164
|
+
_offline_html = prerender_component(offline_component, [], {}, uuid, request)
|
|
165
|
+
|
|
166
|
+
# Return the template rendering context
|
|
167
|
+
return {
|
|
168
|
+
"reactpy_class": class_,
|
|
169
|
+
"reactpy_uuid": uuid,
|
|
170
|
+
"reactpy_host": host or perceived_host,
|
|
171
|
+
"reactpy_url_prefix": reactpy_config.REACTPY_URL_PREFIX,
|
|
172
|
+
"reactpy_component_path": f"{dotted_path}/{uuid}/{int(has_args)}/",
|
|
173
|
+
"reactpy_resolved_web_modules_path": RESOLVED_WEB_MODULES_PATH,
|
|
174
|
+
"reactpy_reconnect_interval": reactpy_config.REACTPY_RECONNECT_INTERVAL,
|
|
175
|
+
"reactpy_reconnect_max_interval": reactpy_config.REACTPY_RECONNECT_MAX_INTERVAL,
|
|
176
|
+
"reactpy_reconnect_backoff_multiplier": reactpy_config.REACTPY_RECONNECT_BACKOFF_MULTIPLIER,
|
|
177
|
+
"reactpy_reconnect_max_retries": reactpy_config.REACTPY_RECONNECT_MAX_RETRIES,
|
|
178
|
+
"reactpy_prerender_html": _prerender_html,
|
|
179
|
+
"reactpy_offline_html": _offline_html,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@register.inclusion_tag("reactpy/pyscript_component.html", takes_context=True)
|
|
184
|
+
def pyscript_component(
|
|
185
|
+
context: template.RequestContext,
|
|
186
|
+
*file_paths: str,
|
|
187
|
+
initial: str | VdomDict | ComponentType = "",
|
|
188
|
+
root: str = "root",
|
|
189
|
+
):
|
|
190
|
+
"""
|
|
191
|
+
Args:
|
|
192
|
+
file_paths: File path to your client-side component. If multiple paths are \
|
|
193
|
+
provided, the contents are automatically merged.
|
|
194
|
+
|
|
195
|
+
Kwargs:
|
|
196
|
+
initial: The initial HTML that is displayed prior to the PyScript component \
|
|
197
|
+
loads. This can either be a string containing raw HTML, a \
|
|
198
|
+
`#!python reactpy.html` snippet, or a non-interactive component.
|
|
199
|
+
root: The name of the root component function.
|
|
200
|
+
"""
|
|
201
|
+
if not file_paths:
|
|
202
|
+
raise ValueError(
|
|
203
|
+
"At least one file path must be provided to the 'pyscript_component' tag."
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
uuid = uuid4().hex
|
|
207
|
+
request: HttpRequest | None = context.get("request")
|
|
208
|
+
initial = vdom_or_component_to_string(initial, request=request, uuid=uuid)
|
|
209
|
+
executor = render_pyscript_template(file_paths, uuid, root)
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
"pyscript_executor": executor,
|
|
213
|
+
"pyscript_uuid": uuid,
|
|
214
|
+
"pyscript_initial_html": initial,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@register.inclusion_tag("reactpy/pyscript_setup.html")
|
|
219
|
+
def pyscript_setup(
|
|
220
|
+
*extra_py: str,
|
|
221
|
+
extra_js: str | dict = "",
|
|
222
|
+
config: str | dict = "",
|
|
223
|
+
):
|
|
224
|
+
"""
|
|
225
|
+
Args:
|
|
226
|
+
extra_py: Dependencies that need to be loaded on the page for \
|
|
227
|
+
your PyScript components. Each dependency must be contained \
|
|
228
|
+
within it's own string and written in Python requirements file syntax.
|
|
229
|
+
|
|
230
|
+
Kwargs:
|
|
231
|
+
extra_js: A JSON string or Python dictionary containing a vanilla \
|
|
232
|
+
JavaScript module URL and the `name: str` to access it within \
|
|
233
|
+
`pyscript.js_modules.*`.
|
|
234
|
+
config: A JSON string or Python dictionary containing PyScript \
|
|
235
|
+
configuration values.
|
|
236
|
+
"""
|
|
237
|
+
return {
|
|
238
|
+
"pyscript_config": extend_pyscript_config(extra_py, extra_js, config),
|
|
239
|
+
"pyscript_layout_handler": PYSCRIPT_LAYOUT_HANDLER,
|
|
240
|
+
"reactpy_debug_mode": reactpy_config.REACTPY_DEBUG_MODE,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def failure_context(dotted_path: str, error: Exception):
|
|
245
|
+
return {
|
|
246
|
+
"reactpy_failure": True,
|
|
247
|
+
"reactpy_debug_mode": reactpy_config.REACTPY_DEBUG_MODE,
|
|
248
|
+
"reactpy_dotted_path": dotted_path,
|
|
249
|
+
"reactpy_error": type(error).__name__,
|
|
250
|
+
}
|
reactpy_django/types.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import (
|
|
5
|
+
TYPE_CHECKING,
|
|
6
|
+
Any,
|
|
7
|
+
Callable,
|
|
8
|
+
Generic,
|
|
9
|
+
MutableMapping,
|
|
10
|
+
NamedTuple,
|
|
11
|
+
Protocol,
|
|
12
|
+
Sequence,
|
|
13
|
+
TypeVar,
|
|
14
|
+
Union,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from django.http import HttpRequest
|
|
18
|
+
from reactpy.types import Connection
|
|
19
|
+
from typing_extensions import ParamSpec
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from reactpy_django.websocket.consumer import ReactpyAsyncWebsocketConsumer
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
FuncParams = ParamSpec("FuncParams")
|
|
26
|
+
Inferred = TypeVar("Inferred")
|
|
27
|
+
ConnectionType = Connection[Union["ReactpyAsyncWebsocketConsumer", HttpRequest]]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class Query(Generic[Inferred]):
|
|
32
|
+
"""Queries generated by the `use_query` hook."""
|
|
33
|
+
|
|
34
|
+
data: Inferred
|
|
35
|
+
loading: bool
|
|
36
|
+
error: Exception | None
|
|
37
|
+
refetch: Callable[[], None]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class Mutation(Generic[FuncParams]):
|
|
42
|
+
"""Mutations generated by the `use_mutation` hook."""
|
|
43
|
+
|
|
44
|
+
execute: Callable[FuncParams, None]
|
|
45
|
+
loading: bool
|
|
46
|
+
error: Exception | None
|
|
47
|
+
reset: Callable[[], None]
|
|
48
|
+
|
|
49
|
+
def __call__(self, *args: FuncParams.args, **kwargs: FuncParams.kwargs) -> None:
|
|
50
|
+
"""Execute the mutation."""
|
|
51
|
+
self.execute(*args, **kwargs)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class AsyncPostprocessor(Protocol):
|
|
55
|
+
async def __call__(self, data: Any) -> Any: ...
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class SyncPostprocessor(Protocol):
|
|
59
|
+
def __call__(self, data: Any) -> Any: ...
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class ComponentParams:
|
|
64
|
+
"""Container used for serializing component parameters.
|
|
65
|
+
This dataclass is pickled & stored in the database, then unpickled when needed."""
|
|
66
|
+
|
|
67
|
+
args: Sequence
|
|
68
|
+
kwargs: MutableMapping[str, Any]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class UserData(NamedTuple):
|
|
72
|
+
query: Query[dict | None]
|
|
73
|
+
mutation: Mutation[dict]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AsyncMessageReceiver(Protocol):
|
|
77
|
+
async def __call__(self, message: dict) -> None: ...
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class AsyncMessageSender(Protocol):
|
|
81
|
+
async def __call__(self, message: dict) -> None: ...
|