instaui 0.1.15__py2.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.
- instaui/__init__.py +9 -0
- instaui/_helper/observable_helper.py +45 -0
- instaui/arco/__init__.py +191 -0
- instaui/arco/_settings.py +25 -0
- instaui/arco/_use_tools/locale.py +50 -0
- instaui/arco/component_types.py +1019 -0
- instaui/arco/components/_utils.py +22 -0
- instaui/arco/components/affix.py +29 -0
- instaui/arco/components/alert.py +42 -0
- instaui/arco/components/anchor.py +42 -0
- instaui/arco/components/auto_complete.py +96 -0
- instaui/arco/components/avatar.py +55 -0
- instaui/arco/components/back_top.py +14 -0
- instaui/arco/components/badge.py +14 -0
- instaui/arco/components/breadcrumb.py +14 -0
- instaui/arco/components/button.py +43 -0
- instaui/arco/components/calendar.py +47 -0
- instaui/arco/components/card.py +14 -0
- instaui/arco/components/carousel.py +33 -0
- instaui/arco/components/cascader.py +111 -0
- instaui/arco/components/checkbox.py +32 -0
- instaui/arco/components/collapse.py +31 -0
- instaui/arco/components/color_picker.py +45 -0
- instaui/arco/components/comment.py +14 -0
- instaui/arco/components/config_provider.py +13 -0
- instaui/arco/components/date_picker.py +111 -0
- instaui/arco/components/descriptions.py +14 -0
- instaui/arco/components/divider.py +13 -0
- instaui/arco/components/drawer.py +98 -0
- instaui/arco/components/dropdown.py +45 -0
- instaui/arco/components/empty.py +14 -0
- instaui/arco/components/form.py +55 -0
- instaui/arco/components/icon.py +17 -0
- instaui/arco/components/image.py +33 -0
- instaui/arco/components/input.py +102 -0
- instaui/arco/components/input_number.py +97 -0
- instaui/arco/components/input_password.py +38 -0
- instaui/arco/components/input_search.py +37 -0
- instaui/arco/components/input_tag.py +110 -0
- instaui/arco/components/layout.py +13 -0
- instaui/arco/components/layout_content.py +6 -0
- instaui/arco/components/layout_footer.py +6 -0
- instaui/arco/components/layout_header.py +6 -0
- instaui/arco/components/layout_sider.py +53 -0
- instaui/arco/components/link.py +36 -0
- instaui/arco/components/list.py +68 -0
- instaui/arco/components/mention.py +97 -0
- instaui/arco/components/menu.py +88 -0
- instaui/arco/components/modal.py +97 -0
- instaui/arco/components/overflow_list.py +29 -0
- instaui/arco/components/page_header.py +29 -0
- instaui/arco/components/pagination.py +45 -0
- instaui/arco/components/pop_confirm.py +58 -0
- instaui/arco/components/popover.py +32 -0
- instaui/arco/components/progress.py +14 -0
- instaui/arco/components/radio.py +40 -0
- instaui/arco/components/radio_group.py +42 -0
- instaui/arco/components/rate.py +45 -0
- instaui/arco/components/resize_box.py +62 -0
- instaui/arco/components/result.py +14 -0
- instaui/arco/components/select.py +182 -0
- instaui/arco/components/skeleton.py +14 -0
- instaui/arco/components/slider.py +38 -0
- instaui/arco/components/space.py +14 -0
- instaui/arco/components/spin.py +14 -0
- instaui/arco/components/split.py +76 -0
- instaui/arco/components/statistic.py +14 -0
- instaui/arco/components/steps.py +32 -0
- instaui/arco/components/switch.py +57 -0
- instaui/arco/components/tab_pane.py +12 -0
- instaui/arco/components/table.py +276 -0
- instaui/arco/components/tabs.py +101 -0
- instaui/arco/components/tag.py +42 -0
- instaui/arco/components/textarea.py +84 -0
- instaui/arco/components/time_picker.py +76 -0
- instaui/arco/components/timeline.py +14 -0
- instaui/arco/components/tooltip.py +29 -0
- instaui/arco/components/transfer.py +58 -0
- instaui/arco/components/tree.py +120 -0
- instaui/arco/components/tree_select.py +86 -0
- instaui/arco/components/trigger.py +58 -0
- instaui/arco/components/typography.py +142 -0
- instaui/arco/components/upload.py +71 -0
- instaui/arco/components/verification_code.py +58 -0
- instaui/arco/components/watermark.py +14 -0
- instaui/arco/locales/__init__.py +4 -0
- instaui/arco/locales/_index.py +31 -0
- instaui/arco/locales/en_us.py +227 -0
- instaui/arco/locales/zh_cn.py +224 -0
- instaui/arco/setup.py +36 -0
- instaui/arco/static/instaui-arco.css +1 -0
- instaui/arco/static/instaui-arco.js +55771 -0
- instaui/arco/types.py +24 -0
- instaui/boot_info.py +43 -0
- instaui/common/jsonable.py +37 -0
- instaui/components/__init__.py +0 -0
- instaui/components/column.py +26 -0
- instaui/components/component.py +47 -0
- instaui/components/content.py +34 -0
- instaui/components/directive.py +55 -0
- instaui/components/element.py +573 -0
- instaui/components/grid.py +213 -0
- instaui/components/html/__init__.py +49 -0
- instaui/components/html/_mixins.py +34 -0
- instaui/components/html/_preset.py +4 -0
- instaui/components/html/button.py +38 -0
- instaui/components/html/checkbox.py +35 -0
- instaui/components/html/date.py +28 -0
- instaui/components/html/div.py +7 -0
- instaui/components/html/form.py +7 -0
- instaui/components/html/heading.py +51 -0
- instaui/components/html/input.py +28 -0
- instaui/components/html/label.py +21 -0
- instaui/components/html/li.py +17 -0
- instaui/components/html/link.py +31 -0
- instaui/components/html/number.py +34 -0
- instaui/components/html/paragraph.py +29 -0
- instaui/components/html/range.py +48 -0
- instaui/components/html/select.py +69 -0
- instaui/components/html/span.py +19 -0
- instaui/components/html/table.py +36 -0
- instaui/components/html/textarea.py +28 -0
- instaui/components/html/ul.py +20 -0
- instaui/components/label.py +5 -0
- instaui/components/markdown/markdown.js +33 -0
- instaui/components/markdown/markdown.py +41 -0
- instaui/components/markdown/static/github-markdown.css +12 -0
- instaui/components/markdown/static/marked.esm.js +2579 -0
- instaui/components/match.py +108 -0
- instaui/components/row.py +17 -0
- instaui/components/shiki_code/shiki_code.js +126 -0
- instaui/components/shiki_code/shiki_code.py +99 -0
- instaui/components/shiki_code/static/langs/css.mjs +5 -0
- instaui/components/shiki_code/static/langs/markdown.mjs +5 -0
- instaui/components/shiki_code/static/langs/python.mjs +5 -0
- instaui/components/shiki_code/static/langs/shell.mjs +2 -0
- instaui/components/shiki_code/static/langs/shellscript.mjs +5 -0
- instaui/components/shiki_code/static/shiki-core.js +5784 -0
- instaui/components/shiki_code/static/shiki-style.css +179 -0
- instaui/components/shiki_code/static/shiki-transformers.js +461 -0
- instaui/components/shiki_code/static/themes/vitesse-dark.mjs +2 -0
- instaui/components/shiki_code/static/themes/vitesse-light.mjs +2 -0
- instaui/components/slot.py +81 -0
- instaui/components/transition_group.py +9 -0
- instaui/components/value_element.py +52 -0
- instaui/components/vfor.py +142 -0
- instaui/components/vif.py +42 -0
- instaui/consts.py +23 -0
- instaui/dependencies/component_dependency.py +22 -0
- instaui/dependencies/plugin_dependency.py +28 -0
- instaui/event/event_mixin.py +12 -0
- instaui/event/js_event.py +82 -0
- instaui/event/vue_event.py +66 -0
- instaui/event/web_event.py +123 -0
- instaui/experimental/__init__.py +3 -0
- instaui/experimental/debug.py +48 -0
- instaui/extra_libs/_echarts.py +3 -0
- instaui/extra_libs/_import_error.py +9 -0
- instaui/extra_libs/_mermaid.py +3 -0
- instaui/extra_libs/_shiki_code.py +3 -0
- instaui/fastapi_server/_utils.py +42 -0
- instaui/fastapi_server/_uvicorn.py +37 -0
- instaui/fastapi_server/debug_mode_router.py +60 -0
- instaui/fastapi_server/dependency_router.py +28 -0
- instaui/fastapi_server/event_router.py +58 -0
- instaui/fastapi_server/middlewares.py +19 -0
- instaui/fastapi_server/request_context.py +19 -0
- instaui/fastapi_server/resource.py +30 -0
- instaui/fastapi_server/server.py +308 -0
- instaui/fastapi_server/watch_router.py +53 -0
- instaui/handlers/_utils.py +88 -0
- instaui/handlers/event_handler.py +60 -0
- instaui/handlers/watch_handler.py +61 -0
- instaui/html_tools.py +94 -0
- instaui/inject.py +33 -0
- instaui/js/__init__.py +4 -0
- instaui/js/js_output.py +15 -0
- instaui/js/lambda_func.py +35 -0
- instaui/launch_collector.py +52 -0
- instaui/page_info.py +13 -0
- instaui/runtime/__init__.py +29 -0
- instaui/runtime/_app.py +234 -0
- instaui/runtime/_inner_helper.py +9 -0
- instaui/runtime/_link_manager.py +89 -0
- instaui/runtime/context.py +47 -0
- instaui/runtime/dataclass.py +30 -0
- instaui/runtime/resource.py +65 -0
- instaui/runtime/scope.py +133 -0
- instaui/runtime/ui_state_scope.py +15 -0
- instaui/settings/__init__.py +4 -0
- instaui/settings/__settings.py +13 -0
- instaui/shadcn_classless/_index.py +42 -0
- instaui/shadcn_classless/static/shadcn-classless.css +403 -0
- instaui/skip.py +12 -0
- instaui/spa_router/__init__.py +26 -0
- instaui/spa_router/_components.py +35 -0
- instaui/spa_router/_file_base_utils.py +273 -0
- instaui/spa_router/_functions.py +122 -0
- instaui/spa_router/_install.py +11 -0
- instaui/spa_router/_route_model.py +117 -0
- instaui/spa_router/_router_box.py +40 -0
- instaui/spa_router/_router_output.py +22 -0
- instaui/spa_router/_router_param_var.py +51 -0
- instaui/spa_router/_types.py +4 -0
- instaui/spa_router/templates/page_routes +60 -0
- instaui/static/insta-ui.css +1 -0
- instaui/static/insta-ui.esm-browser.prod.js +3717 -0
- instaui/static/insta-ui.ico +0 -0
- instaui/static/insta-ui.js.map +1 -0
- instaui/static/instaui-tools-browser.js +511 -0
- instaui/static/templates/debug/sse.html +117 -0
- instaui/static/templates/web.html +74 -0
- instaui/static/templates/webview.html +78 -0
- instaui/static/templates/zero.html +71 -0
- instaui/static/vue.esm-browser.prod.js +9 -0
- instaui/static/vue.global.prod.js +9 -0
- instaui/static/vue.runtime.esm-browser.prod.js +5 -0
- instaui/systems/file_system.py +6 -0
- instaui/systems/func_system.py +119 -0
- instaui/systems/js_system.py +22 -0
- instaui/systems/module_system.py +46 -0
- instaui/systems/pydantic_system.py +27 -0
- instaui/systems/string_system.py +10 -0
- instaui/tailwind/__init__.py +6 -0
- instaui/tailwind/_index.py +24 -0
- instaui/tailwind/static/tailwindcss-v3.min.js +62 -0
- instaui/tailwind/static/tailwindcss-v4.min.js +8 -0
- instaui/template/__init__.py +4 -0
- instaui/template/_utils.py +23 -0
- instaui/template/env.py +7 -0
- instaui/template/web_template.py +49 -0
- instaui/template/webview_template.py +48 -0
- instaui/template/zero_template.py +105 -0
- instaui/ui/__init__.py +144 -0
- instaui/ui/__init__.pyi +149 -0
- instaui/ui/events.py +25 -0
- instaui/ui_functions/input_slient_data.py +16 -0
- instaui/ui_functions/server.py +15 -0
- instaui/ui_functions/str_format.py +36 -0
- instaui/ui_functions/ui_page.py +16 -0
- instaui/ui_functions/ui_types.py +13 -0
- instaui/ui_functions/url_location.py +33 -0
- instaui/vars/_types.py +8 -0
- instaui/vars/data.py +68 -0
- instaui/vars/element_ref.py +40 -0
- instaui/vars/event_context.py +49 -0
- instaui/vars/event_extend.py +0 -0
- instaui/vars/js_computed.py +117 -0
- instaui/vars/mixin_types/common_type.py +5 -0
- instaui/vars/mixin_types/element_binding.py +16 -0
- instaui/vars/mixin_types/observable.py +7 -0
- instaui/vars/mixin_types/pathable.py +14 -0
- instaui/vars/mixin_types/py_binding.py +13 -0
- instaui/vars/mixin_types/str_format_binding.py +8 -0
- instaui/vars/mixin_types/var_type.py +5 -0
- instaui/vars/path_var.py +90 -0
- instaui/vars/ref.py +103 -0
- instaui/vars/slot_prop.py +46 -0
- instaui/vars/state.py +97 -0
- instaui/vars/types.py +24 -0
- instaui/vars/vfor_item.py +204 -0
- instaui/vars/vue_computed.py +81 -0
- instaui/vars/web_computed.py +209 -0
- instaui/vars/web_view_computed.py +1 -0
- instaui/version.py +3 -0
- instaui/watch/_types.py +4 -0
- instaui/watch/_utils.py +3 -0
- instaui/watch/js_watch.py +110 -0
- instaui/watch/vue_watch.py +77 -0
- instaui/watch/web_watch.py +181 -0
- instaui/webview/__init__.py +2 -0
- instaui/webview/_utils.py +8 -0
- instaui/webview/api.py +72 -0
- instaui/webview/func.py +114 -0
- instaui/webview/index.py +161 -0
- instaui/webview/resource.py +172 -0
- instaui/zero/__init__.py +3 -0
- instaui/zero/func.py +123 -0
- instaui/zero/scope.py +109 -0
- instaui-0.1.15.dist-info/METADATA +152 -0
- instaui-0.1.15.dist-info/RECORD +283 -0
- instaui-0.1.15.dist-info/WHEEL +5 -0
- instaui-0.1.15.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,308 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from contextlib import contextmanager
|
3
|
+
import inspect
|
4
|
+
import os
|
5
|
+
import multiprocessing
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Any, Optional, Set, Union
|
8
|
+
import __main__
|
9
|
+
|
10
|
+
from fastapi import FastAPI
|
11
|
+
from fastapi import Request
|
12
|
+
from fastapi.middleware.gzip import GZipMiddleware
|
13
|
+
from fastapi.staticfiles import StaticFiles
|
14
|
+
from fastapi.responses import HTMLResponse
|
15
|
+
import uvicorn
|
16
|
+
from uvicorn.supervisors import ChangeReload
|
17
|
+
import itertools
|
18
|
+
|
19
|
+
from instaui.html_tools import to_config_data
|
20
|
+
from instaui.launch_collector import get_launch_collector
|
21
|
+
from instaui.page_info import PageInfo
|
22
|
+
|
23
|
+
from instaui import consts
|
24
|
+
from instaui.runtime._app import get_app_slot, get_default_app_slot
|
25
|
+
from instaui.runtime.dataclass import JsLink, VueAppComponent
|
26
|
+
from instaui.template import web_template
|
27
|
+
|
28
|
+
|
29
|
+
from . import dependency_router
|
30
|
+
from . import event_router
|
31
|
+
from . import watch_router
|
32
|
+
from . import debug_mode_router
|
33
|
+
from .middlewares import RequestContextMiddleware
|
34
|
+
from ._uvicorn import UvicornServer
|
35
|
+
from . import resource
|
36
|
+
from instaui.version import __version__ as _INSTA_VERSION
|
37
|
+
|
38
|
+
APP_IMPORT_STRING = "instaui.fastapi_server.server:Server._instance.app"
|
39
|
+
|
40
|
+
|
41
|
+
INSTAUI_STATIC_URL = f"/_instaui_{_INSTA_VERSION}/static"
|
42
|
+
|
43
|
+
VUE_JS_HASH_LINK = f"{INSTAUI_STATIC_URL}/{consts.VUE_ES_JS_PATH.name}"
|
44
|
+
INSTAUI_JS_HASH_LINK = f"{INSTAUI_STATIC_URL}/{consts.APP_ES_JS_PATH.name}"
|
45
|
+
APP_CSS_LINK = f"{INSTAUI_STATIC_URL}/{consts.APP_CSS_PATH.name}"
|
46
|
+
FAVICON_LINK = f"{INSTAUI_STATIC_URL}/{consts.FAVICON_PATH.name}"
|
47
|
+
|
48
|
+
|
49
|
+
class Server:
|
50
|
+
_instance: Optional[Server] = None
|
51
|
+
|
52
|
+
def __init__(
|
53
|
+
self,
|
54
|
+
use_gzip: Union[int, bool] = True,
|
55
|
+
):
|
56
|
+
self.app = FastAPI()
|
57
|
+
self.app.add_middleware(RequestContextMiddleware)
|
58
|
+
|
59
|
+
if use_gzip:
|
60
|
+
self.app.add_middleware(
|
61
|
+
GZipMiddleware,
|
62
|
+
minimum_size=use_gzip if isinstance(use_gzip, int) else 500,
|
63
|
+
)
|
64
|
+
|
65
|
+
dependency_router.create_router(self.app)
|
66
|
+
event_router.create_router(self.app)
|
67
|
+
watch_router.create_router(self.app)
|
68
|
+
debug_mode_router.create_router(self.app)
|
69
|
+
|
70
|
+
self.add_instaui_static(self.app)
|
71
|
+
|
72
|
+
for page_info in get_launch_collector()._page_router.values():
|
73
|
+
self.register_page(page_info)
|
74
|
+
|
75
|
+
self._registered_static_routes: Set[str] = set()
|
76
|
+
|
77
|
+
Server._instance = self
|
78
|
+
|
79
|
+
def register_page(self, info: PageInfo):
|
80
|
+
is_async = inspect.iscoroutinefunction(info.func)
|
81
|
+
|
82
|
+
self._remove_route(info.path)
|
83
|
+
|
84
|
+
if is_async:
|
85
|
+
|
86
|
+
@self.app.get(info.path)
|
87
|
+
async def _(request: Request):
|
88
|
+
self._update_page_info(request, info)
|
89
|
+
with _execute_request_lifespans():
|
90
|
+
await info.func()
|
91
|
+
html = self._to_web_html(
|
92
|
+
page_info=info,
|
93
|
+
request=request,
|
94
|
+
)
|
95
|
+
|
96
|
+
return HTMLResponse(html)
|
97
|
+
|
98
|
+
else:
|
99
|
+
|
100
|
+
@self.app.get(info.path)
|
101
|
+
def _(request: Request):
|
102
|
+
self._update_page_info(request, info)
|
103
|
+
with _execute_request_lifespans():
|
104
|
+
info.func()
|
105
|
+
html = self._to_web_html(
|
106
|
+
page_info=info,
|
107
|
+
request=request,
|
108
|
+
)
|
109
|
+
|
110
|
+
return HTMLResponse(html)
|
111
|
+
|
112
|
+
def _to_web_html(
|
113
|
+
self,
|
114
|
+
*,
|
115
|
+
page_info: PageInfo,
|
116
|
+
request: Request,
|
117
|
+
):
|
118
|
+
config_data = to_config_data()
|
119
|
+
|
120
|
+
system_slot = get_app_slot()
|
121
|
+
default_app_slot = get_default_app_slot()
|
122
|
+
html_resource = system_slot._html_resource
|
123
|
+
default_html_resource = default_app_slot._html_resource
|
124
|
+
|
125
|
+
favicon_url = FAVICON_LINK
|
126
|
+
if html_resource.favicon:
|
127
|
+
favicon_url = resource.record_resource(html_resource.favicon)
|
128
|
+
else:
|
129
|
+
if default_html_resource.favicon:
|
130
|
+
favicon_url = resource.record_resource(default_html_resource.favicon)
|
131
|
+
|
132
|
+
model = web_template.WebTemplateModel(
|
133
|
+
vue_js_link=VUE_JS_HASH_LINK,
|
134
|
+
instaui_js_link=INSTAUI_JS_HASH_LINK,
|
135
|
+
css_links=[
|
136
|
+
APP_CSS_LINK,
|
137
|
+
],
|
138
|
+
config_dict=config_data,
|
139
|
+
favicon_url=favicon_url,
|
140
|
+
title=html_resource.title
|
141
|
+
or default_html_resource.title
|
142
|
+
or consts.PAGE_TITLE,
|
143
|
+
)
|
144
|
+
|
145
|
+
# register custom components
|
146
|
+
for component in system_slot._component_dependencies:
|
147
|
+
if not component.esm:
|
148
|
+
continue
|
149
|
+
|
150
|
+
model.vue_app_component.append(
|
151
|
+
VueAppComponent(
|
152
|
+
name=component.tag_name,
|
153
|
+
url=resource.record_resource(component.esm),
|
154
|
+
)
|
155
|
+
)
|
156
|
+
|
157
|
+
if component.css:
|
158
|
+
for css_link in component.css:
|
159
|
+
css_resource = resource.record_resource(css_link)
|
160
|
+
if css_link.is_file():
|
161
|
+
model.css_links.append(css_resource)
|
162
|
+
|
163
|
+
if component.externals:
|
164
|
+
for name, url in component.externals.items():
|
165
|
+
model.add_extra_import_map(name, resource.record_resource(url))
|
166
|
+
|
167
|
+
# register custom plugins
|
168
|
+
for plugin in set(
|
169
|
+
itertools.chain(
|
170
|
+
system_slot._plugin_dependencies, default_app_slot._plugin_dependencies
|
171
|
+
)
|
172
|
+
):
|
173
|
+
if not plugin.esm:
|
174
|
+
continue
|
175
|
+
|
176
|
+
model.vue_app_use.append(plugin.name)
|
177
|
+
|
178
|
+
model.add_extra_import_map(
|
179
|
+
plugin.name, resource.record_resource(plugin.esm)
|
180
|
+
)
|
181
|
+
|
182
|
+
if plugin.css:
|
183
|
+
for css_link in plugin.css:
|
184
|
+
model.css_links.append(resource.record_resource(css_link))
|
185
|
+
|
186
|
+
# css file link to web static link
|
187
|
+
for link in html_resource.get_valid_css_links(
|
188
|
+
default_html_resource._css_links_manager
|
189
|
+
):
|
190
|
+
if isinstance(link, Path):
|
191
|
+
model.css_links.append(resource.record_resource(link))
|
192
|
+
else:
|
193
|
+
model.css_links.append(link)
|
194
|
+
|
195
|
+
# js file link to web static link
|
196
|
+
for info in html_resource.get_valid_js_links(
|
197
|
+
default_html_resource._js_links_manager
|
198
|
+
):
|
199
|
+
link = (
|
200
|
+
resource.record_resource(info.link)
|
201
|
+
if isinstance(info.link, Path)
|
202
|
+
else info.link
|
203
|
+
)
|
204
|
+
model.js_links.append(JsLink(link))
|
205
|
+
|
206
|
+
for js_code in itertools.chain(
|
207
|
+
html_resource._script_tags, default_html_resource._script_tags
|
208
|
+
):
|
209
|
+
model.script_tags.append(js_code)
|
210
|
+
|
211
|
+
for sylte_code in itertools.chain(
|
212
|
+
html_resource._style_tags, default_html_resource._style_tags
|
213
|
+
):
|
214
|
+
model.style_tags.append(sylte_code)
|
215
|
+
|
216
|
+
model.prefix = request.headers.get(
|
217
|
+
"X-Forwarded-Prefix", request.scope.get("root_path", "")
|
218
|
+
)
|
219
|
+
|
220
|
+
return web_template.render_web_html(model)
|
221
|
+
|
222
|
+
def _update_page_info(self, request: Request, page_info: PageInfo):
|
223
|
+
app = get_app_slot()
|
224
|
+
|
225
|
+
app._page_path = page_info.path
|
226
|
+
app._page_params = request.path_params
|
227
|
+
app._query_params = dict(request.query_params)
|
228
|
+
|
229
|
+
def _remove_route(self, path: str) -> None:
|
230
|
+
self.app.routes[:] = [
|
231
|
+
r for r in self.app.routes if getattr(r, "path", None) != path
|
232
|
+
]
|
233
|
+
|
234
|
+
def try_close_server(self):
|
235
|
+
UvicornServer.instance.should_exit = True
|
236
|
+
|
237
|
+
def run(
|
238
|
+
self,
|
239
|
+
host="0.0.0.0",
|
240
|
+
port=8080,
|
241
|
+
reload: bool = True,
|
242
|
+
reload_dirs: str = ".",
|
243
|
+
reload_includes: str = "*.py",
|
244
|
+
reload_excludes: str = ".*, .py[cod], .sw.*, ~*",
|
245
|
+
log_level="info",
|
246
|
+
workers: int | None = None,
|
247
|
+
uds: str | None = None,
|
248
|
+
**kwargs: Any,
|
249
|
+
):
|
250
|
+
if multiprocessing.current_process().name != "MainProcess":
|
251
|
+
return
|
252
|
+
|
253
|
+
if reload and not hasattr(__main__, "__file__"):
|
254
|
+
reload = False
|
255
|
+
|
256
|
+
config = uvicorn.Config(
|
257
|
+
APP_IMPORT_STRING if reload else self.app,
|
258
|
+
host=host,
|
259
|
+
port=port,
|
260
|
+
reload=reload,
|
261
|
+
log_level=log_level,
|
262
|
+
workers=workers,
|
263
|
+
uds=uds,
|
264
|
+
reload_includes=_split_args(reload_includes) if reload else None,
|
265
|
+
reload_excludes=_split_args(reload_excludes) if reload else None,
|
266
|
+
reload_dirs=_split_args(reload_dirs) if reload else None,
|
267
|
+
**kwargs,
|
268
|
+
)
|
269
|
+
|
270
|
+
UvicornServer.create_singleton(config, [debug_mode_router.when_server_reload])
|
271
|
+
|
272
|
+
if config.should_reload:
|
273
|
+
ChangeReload(config, target=UvicornServer.instance.run, sockets=[]).run()
|
274
|
+
else:
|
275
|
+
UvicornServer.instance.run()
|
276
|
+
|
277
|
+
if config.uds:
|
278
|
+
os.remove(config.uds) # pragma: py-win32
|
279
|
+
|
280
|
+
def run_with(self, app):
|
281
|
+
assert isinstance(app, FastAPI), "app must be a FastAPI instance"
|
282
|
+
|
283
|
+
@staticmethod
|
284
|
+
def add_instaui_static(app: FastAPI):
|
285
|
+
app.mount(
|
286
|
+
INSTAUI_STATIC_URL,
|
287
|
+
StaticFiles(directory=consts._STATIC_DIR),
|
288
|
+
name=INSTAUI_STATIC_URL,
|
289
|
+
)
|
290
|
+
|
291
|
+
|
292
|
+
def _split_args(args: str):
|
293
|
+
return [a.strip() for a in args.split(",")]
|
294
|
+
|
295
|
+
|
296
|
+
@contextmanager
|
297
|
+
def _execute_request_lifespans():
|
298
|
+
events = [iter(event()) for event in get_launch_collector().page_request_lifespans]
|
299
|
+
for event in events:
|
300
|
+
next(event)
|
301
|
+
|
302
|
+
yield
|
303
|
+
|
304
|
+
for event in events:
|
305
|
+
try:
|
306
|
+
next(event)
|
307
|
+
except StopIteration:
|
308
|
+
pass
|
@@ -0,0 +1,53 @@
|
|
1
|
+
from typing import Dict
|
2
|
+
from fastapi import FastAPI
|
3
|
+
from instaui.handlers import watch_handler
|
4
|
+
from instaui.runtime.context import get_context
|
5
|
+
|
6
|
+
from . import _utils
|
7
|
+
|
8
|
+
|
9
|
+
def create_router(app: FastAPI):
|
10
|
+
_async_handler(app)
|
11
|
+
_sync_handler(app)
|
12
|
+
|
13
|
+
|
14
|
+
def _async_handler(app: FastAPI):
|
15
|
+
@app.post(watch_handler.ASYNC_URL)
|
16
|
+
async def _(data: Dict):
|
17
|
+
hkey = data.pop("key")
|
18
|
+
handler_info = watch_handler.get_handler_info(hkey)
|
19
|
+
if handler_info is None:
|
20
|
+
return {"error": "watch handler not found"}
|
21
|
+
|
22
|
+
_utils.update_app_page_info(data)
|
23
|
+
|
24
|
+
result = await handler_info.fn(
|
25
|
+
*handler_info.get_handler_args(_get_binds_from_data(data))
|
26
|
+
)
|
27
|
+
return _utils.response_data(handler_info.outputs_binding_count, result)
|
28
|
+
|
29
|
+
|
30
|
+
def _sync_handler(app: FastAPI):
|
31
|
+
@app.post(watch_handler.SYNC_URL)
|
32
|
+
def _(data: Dict):
|
33
|
+
hkey = data.pop("key")
|
34
|
+
handler_info = watch_handler.get_handler_info(hkey)
|
35
|
+
if handler_info is None:
|
36
|
+
return {"error": "watch handler not found"}
|
37
|
+
|
38
|
+
_utils.update_app_page_info(data)
|
39
|
+
|
40
|
+
result = handler_info.fn(
|
41
|
+
*handler_info.get_handler_args(_get_binds_from_data(data))
|
42
|
+
)
|
43
|
+
return _utils.response_data(handler_info.outputs_binding_count, result)
|
44
|
+
|
45
|
+
if get_context().debug_mode:
|
46
|
+
|
47
|
+
@app.get("/instaui/watch-infos", tags=["instaui-debug"])
|
48
|
+
def watch_infos():
|
49
|
+
return watch_handler.get_statistics_info()
|
50
|
+
|
51
|
+
|
52
|
+
def _get_binds_from_data(data: Dict):
|
53
|
+
return data.get("input", [])
|
@@ -0,0 +1,88 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from typing import Callable, List, Mapping, Optional
|
3
|
+
|
4
|
+
import pydantic_core
|
5
|
+
from instaui.systems import func_system, pydantic_system
|
6
|
+
from instaui.runtime.context import get_context
|
7
|
+
|
8
|
+
|
9
|
+
def create_handler_key(
|
10
|
+
page_path: str,
|
11
|
+
handler: Callable,
|
12
|
+
):
|
13
|
+
_, lineno, _ = func_system.get_function_location_info(handler)
|
14
|
+
|
15
|
+
if get_context().debug_mode:
|
16
|
+
return f"page:{page_path}|line:{lineno}"
|
17
|
+
|
18
|
+
return f"{page_path}|{lineno}"
|
19
|
+
|
20
|
+
|
21
|
+
@dataclass
|
22
|
+
class HandlerInfo:
|
23
|
+
fn: Callable
|
24
|
+
fn_location_info: str
|
25
|
+
outputs_binding_count: int = 0
|
26
|
+
handler_param_converters: List[pydantic_system.TypeAdapterProtocol] = field(
|
27
|
+
default_factory=list
|
28
|
+
)
|
29
|
+
is_last_param_args: bool = False
|
30
|
+
|
31
|
+
def get_handler_args(self, input_values: List):
|
32
|
+
real_param_converters = _try_expand_params_converters(
|
33
|
+
self.handler_param_converters, input_values, self.is_last_param_args
|
34
|
+
)
|
35
|
+
|
36
|
+
try:
|
37
|
+
return [
|
38
|
+
param_converter.to_python_value(value)
|
39
|
+
for param_converter, value in zip(real_param_converters, input_values)
|
40
|
+
]
|
41
|
+
except pydantic_core._pydantic_core.ValidationError as e:
|
42
|
+
raise ValueError(f"invalid input[{self.fn_location_info}]: {e}") from None
|
43
|
+
|
44
|
+
@classmethod
|
45
|
+
def from_handler(
|
46
|
+
cls,
|
47
|
+
handler: Callable,
|
48
|
+
outputs_binding_count: int,
|
49
|
+
custom_type_adapter_map: Optional[
|
50
|
+
Mapping[int, pydantic_system.TypeAdapterProtocol]
|
51
|
+
] = None,
|
52
|
+
):
|
53
|
+
custom_type_adapter_map = custom_type_adapter_map or {}
|
54
|
+
params_infos = func_system.get_fn_params_infos(handler)
|
55
|
+
is_last_param_args = func_system.is_last_param_args(handler)
|
56
|
+
param_converters = [
|
57
|
+
custom_type_adapter_map.get(
|
58
|
+
idx, pydantic_system.create_type_adapter(param_type)
|
59
|
+
)
|
60
|
+
for idx, (_, param_type) in enumerate(params_infos)
|
61
|
+
]
|
62
|
+
|
63
|
+
file, lineno, _ = func_system.get_function_location_info(handler)
|
64
|
+
|
65
|
+
return cls(
|
66
|
+
handler,
|
67
|
+
f'File "{file}", line {lineno}',
|
68
|
+
outputs_binding_count,
|
69
|
+
handler_param_converters=param_converters,
|
70
|
+
is_last_param_args=is_last_param_args,
|
71
|
+
)
|
72
|
+
|
73
|
+
|
74
|
+
def _try_expand_params_converters(
|
75
|
+
old_param_converters: List[pydantic_system.TypeAdapterProtocol],
|
76
|
+
input_values: List,
|
77
|
+
is_last_param_args: bool,
|
78
|
+
):
|
79
|
+
if not is_last_param_args:
|
80
|
+
return old_param_converters
|
81
|
+
|
82
|
+
diff = len(input_values) - len(old_param_converters)
|
83
|
+
if diff == 0:
|
84
|
+
return old_param_converters
|
85
|
+
|
86
|
+
arg_param_converters = [old_param_converters[-1]] * diff
|
87
|
+
|
88
|
+
return [*old_param_converters[:], *arg_param_converters]
|
@@ -0,0 +1,60 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import threading
|
3
|
+
from typing import Callable, Dict, Optional, Sequence, TYPE_CHECKING
|
4
|
+
import ast
|
5
|
+
from . import _utils
|
6
|
+
from instaui.vars.event_context import DatasetEventContext
|
7
|
+
from instaui.runtime._app import update_web_server_info
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from instaui.vars.mixin_types.py_binding import CanOutputMixin, CanInputMixin
|
11
|
+
|
12
|
+
ASYNC_URL = "/instaui/event/async"
|
13
|
+
SYNC_URL = "/instaui/event/sync"
|
14
|
+
update_web_server_info(event_url=SYNC_URL, event_async_url=ASYNC_URL)
|
15
|
+
_event_handlers: Dict[str, _utils.HandlerInfo] = {}
|
16
|
+
dict_lock = threading.Lock()
|
17
|
+
|
18
|
+
|
19
|
+
class EventDataSetTypeAdapter:
|
20
|
+
def to_python_value(self, value, *args, **kwargs):
|
21
|
+
return ast.literal_eval(value)
|
22
|
+
|
23
|
+
|
24
|
+
def register_event_handler(
|
25
|
+
key: str,
|
26
|
+
handler: Callable,
|
27
|
+
outputs_binding: Optional[Sequence[CanOutputMixin]],
|
28
|
+
inputs_binding: Optional[Sequence[CanInputMixin]],
|
29
|
+
):
|
30
|
+
if key in _event_handlers:
|
31
|
+
return
|
32
|
+
|
33
|
+
custom_type_adapter_map = {
|
34
|
+
i: EventDataSetTypeAdapter()
|
35
|
+
for i, binding in enumerate(inputs_binding or [])
|
36
|
+
if isinstance(binding, DatasetEventContext)
|
37
|
+
}
|
38
|
+
|
39
|
+
handler_info = _utils.HandlerInfo.from_handler(
|
40
|
+
handler,
|
41
|
+
len(list(outputs_binding)) if outputs_binding else 0,
|
42
|
+
custom_type_adapter_map=custom_type_adapter_map,
|
43
|
+
)
|
44
|
+
|
45
|
+
with dict_lock:
|
46
|
+
_event_handlers[key] = handler_info
|
47
|
+
|
48
|
+
|
49
|
+
def get_handler(key: str) -> Optional[_utils.HandlerInfo]:
|
50
|
+
return _event_handlers.get(key)
|
51
|
+
|
52
|
+
|
53
|
+
def get_statistics_info():
|
54
|
+
return {
|
55
|
+
"_event_handlers count": len(_event_handlers),
|
56
|
+
"_event_handlers keys": list(_event_handlers.keys()),
|
57
|
+
}
|
58
|
+
|
59
|
+
|
60
|
+
create_handler_key = _utils.create_handler_key
|
@@ -0,0 +1,61 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import threading
|
3
|
+
from typing import (
|
4
|
+
Callable,
|
5
|
+
Dict,
|
6
|
+
Generic,
|
7
|
+
TypeVar,
|
8
|
+
)
|
9
|
+
from dataclasses import dataclass
|
10
|
+
|
11
|
+
from instaui.runtime.context import get_context
|
12
|
+
from instaui.runtime._app import update_web_server_info
|
13
|
+
from instaui.systems import func_system
|
14
|
+
from . import _utils
|
15
|
+
|
16
|
+
|
17
|
+
ASYNC_URL = "/instaui/watch/async"
|
18
|
+
SYNC_URL = "/instaui/watch/sync"
|
19
|
+
|
20
|
+
update_web_server_info(watch_url=SYNC_URL, watch_async_url=ASYNC_URL)
|
21
|
+
|
22
|
+
_watch_handlers: Dict[str, _utils.HandlerInfo] = {}
|
23
|
+
dict_lock = threading.Lock()
|
24
|
+
|
25
|
+
|
26
|
+
def register_handler(key: str, handler: Callable, outputs_binding_count: int):
|
27
|
+
if key in _watch_handlers:
|
28
|
+
return
|
29
|
+
with dict_lock:
|
30
|
+
_watch_handlers[key] = _utils.HandlerInfo.from_handler(
|
31
|
+
handler, outputs_binding_count
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
def get_handler_info(key: str) -> _utils.HandlerInfo:
|
36
|
+
return _watch_handlers.get(key) # type: ignore
|
37
|
+
|
38
|
+
|
39
|
+
def get_statistics_info():
|
40
|
+
return {
|
41
|
+
"_watch_handlers count": len(_watch_handlers),
|
42
|
+
"_watch_handlers keys": list(_watch_handlers.keys()),
|
43
|
+
}
|
44
|
+
|
45
|
+
|
46
|
+
def create_handler_key(page_path: str, handler: Callable):
|
47
|
+
_, lineno, _ = func_system.get_function_location_info(handler)
|
48
|
+
|
49
|
+
if get_context().debug_mode:
|
50
|
+
return f"path:{page_path}|line:{lineno}"
|
51
|
+
return f"{page_path}|{lineno}"
|
52
|
+
|
53
|
+
|
54
|
+
_TWatchStateValue = TypeVar("_TWatchStateValue")
|
55
|
+
|
56
|
+
|
57
|
+
@dataclass(frozen=True)
|
58
|
+
class WatchState(Generic[_TWatchStateValue]):
|
59
|
+
new_value: _TWatchStateValue
|
60
|
+
old_value: _TWatchStateValue
|
61
|
+
modified: bool
|
instaui/html_tools.py
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Any, Dict, Literal, Optional, Union
|
4
|
+
from instaui.common.jsonable import dumps, dumps2dict
|
5
|
+
from instaui.runtime._app import get_app_slot
|
6
|
+
from instaui.tailwind._index import use_tailwind
|
7
|
+
|
8
|
+
use_tailwind = use_tailwind
|
9
|
+
|
10
|
+
|
11
|
+
def add_css_link(href: Union[str, Path]):
|
12
|
+
"""Add a link to a CSS file to the HTML document.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
href (Union[str, Path]): The path to the CSS file.
|
16
|
+
"""
|
17
|
+
get_app_slot()._html_resource.add_css_link(href)
|
18
|
+
|
19
|
+
|
20
|
+
def remove_css_link(href: Union[str, Path]):
|
21
|
+
"""Remove a link to a CSS file from the HTML document.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
href (Union[str, Path]): The path to the CSS file.
|
25
|
+
"""
|
26
|
+
get_app_slot()._html_resource.remove_css_link(href)
|
27
|
+
|
28
|
+
|
29
|
+
def add_js_link(
|
30
|
+
link: Union[str, Path],
|
31
|
+
*,
|
32
|
+
type: Optional[Literal["module"]] = None,
|
33
|
+
):
|
34
|
+
"""Add a link to a JavaScript file to the HTML document.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
link (Union[str, Path]): The path to the JavaScript file.
|
38
|
+
type (Optional[Literal["module"]], optional): The type of the JavaScript file. Defaults to None.
|
39
|
+
"""
|
40
|
+
|
41
|
+
attrs = {
|
42
|
+
"type": type,
|
43
|
+
}
|
44
|
+
|
45
|
+
get_app_slot()._html_resource.add_js_link(link, attrs=attrs)
|
46
|
+
|
47
|
+
|
48
|
+
def add_style(content: str):
|
49
|
+
"""Add a style tag to the HTML document.
|
50
|
+
|
51
|
+
Args:
|
52
|
+
content (str): The content of the style tag.
|
53
|
+
"""
|
54
|
+
get_app_slot()._html_resource.add_style_tag(content)
|
55
|
+
|
56
|
+
|
57
|
+
def use_page_title(title: str):
|
58
|
+
"""Set the title of the HTML document.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
title (str): The title of the HTML document.
|
62
|
+
"""
|
63
|
+
get_app_slot()._html_resource.title = title
|
64
|
+
|
65
|
+
|
66
|
+
def use_favicon(favicon: Path):
|
67
|
+
"""Set the favicon of the HTML document.
|
68
|
+
|
69
|
+
Args:
|
70
|
+
favicon (Path): The path to the favicon.
|
71
|
+
"""
|
72
|
+
get_app_slot()._html_resource.favicon = favicon
|
73
|
+
|
74
|
+
|
75
|
+
def add_js_code(code: str, *, script_attrs: Optional[Dict[str, Any]] = None):
|
76
|
+
"""Add a script tag to the HTML document with the given JavaScript code.
|
77
|
+
|
78
|
+
Args:
|
79
|
+
code (str): The JavaScript code.
|
80
|
+
script_attrs (Optional[Dict[str, Any]], optional): The attributes of the script tag. Defaults to None.
|
81
|
+
"""
|
82
|
+
get_app_slot()._html_resource.add_script_tag(code, script_attrs=script_attrs)
|
83
|
+
|
84
|
+
|
85
|
+
def add_vue_app_use(name: str):
|
86
|
+
get_app_slot()._html_resource.add_vue_app_use(name)
|
87
|
+
|
88
|
+
|
89
|
+
def to_config_data() -> Dict:
|
90
|
+
return dumps2dict(get_app_slot())
|
91
|
+
|
92
|
+
|
93
|
+
def to_json(indent=False):
|
94
|
+
return dumps(get_app_slot(), indent=indent)
|
instaui/inject.py
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
import typing
|
2
|
+
from instaui.runtime import ui_state_scope
|
3
|
+
|
4
|
+
|
5
|
+
_T = typing.TypeVar("_T")
|
6
|
+
|
7
|
+
|
8
|
+
def injection_key(key: str) -> _T:
|
9
|
+
"""creates a key for injection
|
10
|
+
|
11
|
+
Args:
|
12
|
+
key (str): key to use for injection
|
13
|
+
|
14
|
+
Examples:
|
15
|
+
.. code-block:: python
|
16
|
+
text_key :str = injection_key("text")
|
17
|
+
|
18
|
+
# state model
|
19
|
+
class Person(ui.StateModel):
|
20
|
+
name:str
|
21
|
+
|
22
|
+
person_key :Person = injection_key(Person(name='foo'))
|
23
|
+
"""
|
24
|
+
return key # type: ignore
|
25
|
+
|
26
|
+
|
27
|
+
def inject_state(key: _T) -> _T:
|
28
|
+
return typing.cast(_T, ui_state_scope.load_state(key)) # type: ignore
|
29
|
+
|
30
|
+
|
31
|
+
def provide_state(key: _T, obj: _T) -> _T:
|
32
|
+
ui_state_scope.save_state(key, obj)
|
33
|
+
return obj
|
instaui/js/__init__.py
ADDED