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
|
+
# Generated by Django 4.2.3 on 2023-08-04 05:49
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
("reactpy_django", "0003_componentsession_delete_componentparams"),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
operations = [
|
|
12
|
+
migrations.CreateModel(
|
|
13
|
+
name="Config",
|
|
14
|
+
fields=[
|
|
15
|
+
(
|
|
16
|
+
"id",
|
|
17
|
+
models.BigAutoField(
|
|
18
|
+
auto_created=True,
|
|
19
|
+
primary_key=True,
|
|
20
|
+
serialize=False,
|
|
21
|
+
verbose_name="ID",
|
|
22
|
+
),
|
|
23
|
+
),
|
|
24
|
+
("cleaned_at", models.DateTimeField(auto_now_add=True)),
|
|
25
|
+
],
|
|
26
|
+
),
|
|
27
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Generated by Django 4.2.3 on 2023-08-23 19:56
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
("reactpy_django", "0004_config"),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
operations = [
|
|
12
|
+
migrations.AlterField(
|
|
13
|
+
model_name="componentsession",
|
|
14
|
+
name="last_accessed",
|
|
15
|
+
field=models.DateTimeField(auto_now=True),
|
|
16
|
+
),
|
|
17
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Generated by Django 4.2.5 on 2023-10-09 01:02
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
("reactpy_django", "0005_alter_componentsession_last_accessed"),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
operations = [
|
|
12
|
+
migrations.CreateModel(
|
|
13
|
+
name="UserDataModel",
|
|
14
|
+
fields=[
|
|
15
|
+
(
|
|
16
|
+
"id",
|
|
17
|
+
models.BigAutoField(
|
|
18
|
+
auto_created=True,
|
|
19
|
+
primary_key=True,
|
|
20
|
+
serialize=False,
|
|
21
|
+
verbose_name="ID",
|
|
22
|
+
),
|
|
23
|
+
),
|
|
24
|
+
("user_pk", models.CharField(max_length=255, unique=True)),
|
|
25
|
+
("data", models.BinaryField(blank=True, null=True)),
|
|
26
|
+
],
|
|
27
|
+
),
|
|
28
|
+
]
|
|
File without changes
|
reactpy_django/models.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from django.contrib.auth import get_user_model
|
|
2
|
+
from django.db import models
|
|
3
|
+
from django.db.models.signals import pre_delete
|
|
4
|
+
from django.dispatch import receiver
|
|
5
|
+
|
|
6
|
+
from reactpy_django.utils import get_pk
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ComponentSession(models.Model):
|
|
10
|
+
"""A model for storing component sessions."""
|
|
11
|
+
|
|
12
|
+
uuid = models.UUIDField(primary_key=True, editable=False, unique=True) # type: ignore
|
|
13
|
+
params = models.BinaryField(editable=False) # type: ignore
|
|
14
|
+
last_accessed = models.DateTimeField(auto_now=True) # type: ignore
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Config(models.Model):
|
|
18
|
+
"""A singleton model for storing ReactPy configuration."""
|
|
19
|
+
|
|
20
|
+
cleaned_at = models.DateTimeField(auto_now_add=True) # type: ignore
|
|
21
|
+
|
|
22
|
+
def save(self, *args, **kwargs):
|
|
23
|
+
"""Singleton save method."""
|
|
24
|
+
self.pk = 1
|
|
25
|
+
super().save(*args, **kwargs)
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def load(cls):
|
|
29
|
+
obj, _ = cls.objects.get_or_create(pk=1)
|
|
30
|
+
return obj
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class UserDataModel(models.Model):
|
|
34
|
+
"""A model for storing `user_state` data."""
|
|
35
|
+
|
|
36
|
+
# We can't store User as a ForeignKey/OneToOneField because it may not be in the same database
|
|
37
|
+
# and Django does not allow cross-database relations. Also, since we can't know the type of the UserModel PK,
|
|
38
|
+
# we store it as a string to normalize.
|
|
39
|
+
user_pk = models.CharField(max_length=255, unique=True) # type: ignore
|
|
40
|
+
data = models.BinaryField(null=True, blank=True) # type: ignore
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@receiver(pre_delete, sender=get_user_model(), dispatch_uid="reactpy_delete_user_data")
|
|
44
|
+
def delete_user_data(sender, instance, **kwargs):
|
|
45
|
+
"""Delete ReactPy's `UserDataModel` when a Django `User` is deleted."""
|
|
46
|
+
pk = get_pk(instance)
|
|
47
|
+
|
|
48
|
+
UserDataModel.objects.filter(user_pk=pk).delete()
|
reactpy_django/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Marker file for PEP 561
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
import asyncio
|
|
5
|
+
|
|
6
|
+
from reactpy_django.pyscript.layout_handler import ReactPyLayoutHandler
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# User component is inserted below by regex replacement
|
|
10
|
+
def user_workspace_UUID():
|
|
11
|
+
"""Encapsulate the user's code with a completely unique function (workspace)
|
|
12
|
+
to prevent overlapping imports and variable names between different components.
|
|
13
|
+
|
|
14
|
+
This code is designed to be run directly by PyScript, and is not intended to be run
|
|
15
|
+
in a normal Python environment.
|
|
16
|
+
|
|
17
|
+
ReactPy-Django performs string substitutions to turn this file into valid PyScript.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def root(): ...
|
|
21
|
+
|
|
22
|
+
return root()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Create a task to run the user's component workspace
|
|
26
|
+
task_UUID = asyncio.create_task(ReactPyLayoutHandler("UUID").run(user_workspace_UUID))
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# mypy: disable-error-code=attr-defined
|
|
2
|
+
import asyncio
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ReactPyLayoutHandler:
|
|
6
|
+
"""Encapsulate the entire layout handler with a class to prevent overlapping
|
|
7
|
+
variable names between user code.
|
|
8
|
+
|
|
9
|
+
This code is designed to be run directly by PyScript, and is not intended to be run
|
|
10
|
+
in a normal Python environment.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, uuid):
|
|
14
|
+
self.uuid = uuid
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def update_model(update, root_model):
|
|
18
|
+
"""Apply an update ReactPy's internal DOM model."""
|
|
19
|
+
from jsonpointer import set_pointer
|
|
20
|
+
|
|
21
|
+
if update["path"]:
|
|
22
|
+
set_pointer(root_model, update["path"], update["model"])
|
|
23
|
+
else:
|
|
24
|
+
root_model.update(update["model"])
|
|
25
|
+
|
|
26
|
+
def render_html(self, layout, model):
|
|
27
|
+
"""Submit ReactPy's internal DOM model into the HTML DOM."""
|
|
28
|
+
from pyscript.js_modules import morphdom
|
|
29
|
+
|
|
30
|
+
import js
|
|
31
|
+
|
|
32
|
+
# Create a new container to render the layout into
|
|
33
|
+
container = js.document.getElementById(f"pyscript-{self.uuid}")
|
|
34
|
+
temp_root_container = container.cloneNode(False)
|
|
35
|
+
self.build_element_tree(layout, temp_root_container, model)
|
|
36
|
+
|
|
37
|
+
# Use morphdom to update the DOM
|
|
38
|
+
morphdom.default(container, temp_root_container)
|
|
39
|
+
|
|
40
|
+
# Remove the cloned container to prevent memory leaks
|
|
41
|
+
temp_root_container.remove()
|
|
42
|
+
|
|
43
|
+
def build_element_tree(self, layout, parent, model):
|
|
44
|
+
"""Recursively build an element tree, starting from the root component."""
|
|
45
|
+
import js
|
|
46
|
+
|
|
47
|
+
if isinstance(model, str):
|
|
48
|
+
parent.appendChild(js.document.createTextNode(model))
|
|
49
|
+
elif isinstance(model, dict):
|
|
50
|
+
if not model["tagName"]:
|
|
51
|
+
for child in model.get("children", []):
|
|
52
|
+
self.build_element_tree(layout, parent, child)
|
|
53
|
+
return
|
|
54
|
+
tag = model["tagName"]
|
|
55
|
+
attributes = model.get("attributes", {})
|
|
56
|
+
children = model.get("children", [])
|
|
57
|
+
element = js.document.createElement(tag)
|
|
58
|
+
for key, value in attributes.items():
|
|
59
|
+
if key == "style":
|
|
60
|
+
for style_key, style_value in value.items():
|
|
61
|
+
setattr(element.style, style_key, style_value)
|
|
62
|
+
elif key == "className":
|
|
63
|
+
element.className = value
|
|
64
|
+
else:
|
|
65
|
+
element.setAttribute(key, value)
|
|
66
|
+
for event_name, event_handler_model in model.get(
|
|
67
|
+
"eventHandlers", {}
|
|
68
|
+
).items():
|
|
69
|
+
self.create_event_handler(
|
|
70
|
+
layout, element, event_name, event_handler_model
|
|
71
|
+
)
|
|
72
|
+
for child in children:
|
|
73
|
+
self.build_element_tree(layout, element, child)
|
|
74
|
+
parent.appendChild(element)
|
|
75
|
+
else:
|
|
76
|
+
raise ValueError(f"Unknown model type: {type(model)}")
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def create_event_handler(layout, element, event_name, event_handler_model):
|
|
80
|
+
"""Create an event handler for an element. This function is used as an
|
|
81
|
+
adapter between ReactPy and browser events."""
|
|
82
|
+
from pyodide.ffi.wrappers import add_event_listener
|
|
83
|
+
|
|
84
|
+
target = event_handler_model["target"]
|
|
85
|
+
|
|
86
|
+
def event_handler(*args):
|
|
87
|
+
asyncio.create_task(
|
|
88
|
+
layout.deliver({"type": "layout-event", "target": target, "data": args})
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
event_name = event_name.lstrip("on_").lower().replace("_", "")
|
|
92
|
+
add_event_listener(element, event_name, event_handler)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def delete_old_workspaces():
|
|
96
|
+
"""To prevent memory leaks, we must delete all user generated Python code when
|
|
97
|
+
it is no longer in use (removed from the page). To do this, we compare what
|
|
98
|
+
UUIDs exist on the DOM, versus what UUIDs exist within the PyScript global
|
|
99
|
+
interpreter."""
|
|
100
|
+
import js
|
|
101
|
+
|
|
102
|
+
dom_workspaces = js.document.querySelectorAll(".pyscript")
|
|
103
|
+
dom_uuids = {element.dataset.uuid for element in dom_workspaces}
|
|
104
|
+
python_uuids = {
|
|
105
|
+
value.split("_")[-1]
|
|
106
|
+
for value in globals()
|
|
107
|
+
if value.startswith("user_workspace_")
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Delete the workspace if it exists at the moment when we check
|
|
111
|
+
for uuid in python_uuids - dom_uuids:
|
|
112
|
+
task_name = f"task_{uuid}"
|
|
113
|
+
if task_name in globals():
|
|
114
|
+
task: asyncio.Task = globals()[task_name]
|
|
115
|
+
task.cancel()
|
|
116
|
+
del globals()[task_name]
|
|
117
|
+
else:
|
|
118
|
+
print(f"Warning: Could not auto delete PyScript task {task_name}")
|
|
119
|
+
|
|
120
|
+
workspace_name = f"user_workspace_{uuid}"
|
|
121
|
+
if workspace_name in globals():
|
|
122
|
+
del globals()[workspace_name]
|
|
123
|
+
else:
|
|
124
|
+
print(
|
|
125
|
+
f"Warning: Could not auto delete PyScript workspace {workspace_name}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
async def run(self, workspace_function):
|
|
129
|
+
"""Run the layout handler. This function is main executor for all user generated code."""
|
|
130
|
+
from reactpy.core.layout import Layout
|
|
131
|
+
|
|
132
|
+
self.delete_old_workspaces()
|
|
133
|
+
root_model: dict = {}
|
|
134
|
+
|
|
135
|
+
async with Layout(workspace_function()) as root_layout:
|
|
136
|
+
while True:
|
|
137
|
+
update = await root_layout.render()
|
|
138
|
+
self.update_model(update, root_model)
|
|
139
|
+
self.render_html(root_layout, root_model)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from django.urls.converters import get_converters
|
|
2
|
+
from reactpy_router.types import ConversionInfo
|
|
3
|
+
|
|
4
|
+
CONVERTERS: dict[str, ConversionInfo] = {
|
|
5
|
+
name: {"regex": converter.regex, "func": converter.to_python}
|
|
6
|
+
for name, converter in get_converters().items()
|
|
7
|
+
}
|
|
8
|
+
CONVERTERS["any"] = {"regex": r".*", "func": str}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from reactpy_router.resolvers import StarletteResolver
|
|
4
|
+
from reactpy_router.types import ConversionInfo, Route
|
|
5
|
+
|
|
6
|
+
from reactpy_django.router.converters import CONVERTERS
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DjangoResolver(StarletteResolver):
|
|
10
|
+
"""A simple route resolver that uses regex to match paths"""
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
route: Route,
|
|
15
|
+
param_pattern=r"<(?P<type>\w+:)?(?P<name>\w+)>",
|
|
16
|
+
converters: dict[str, ConversionInfo] | None = None,
|
|
17
|
+
) -> None:
|
|
18
|
+
super().__init__(
|
|
19
|
+
route=route,
|
|
20
|
+
param_pattern=param_pattern,
|
|
21
|
+
converters=converters or CONVERTERS,
|
|
22
|
+
)
|