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.
Files changed (283) hide show
  1. instaui/__init__.py +9 -0
  2. instaui/_helper/observable_helper.py +45 -0
  3. instaui/arco/__init__.py +191 -0
  4. instaui/arco/_settings.py +25 -0
  5. instaui/arco/_use_tools/locale.py +50 -0
  6. instaui/arco/component_types.py +1019 -0
  7. instaui/arco/components/_utils.py +22 -0
  8. instaui/arco/components/affix.py +29 -0
  9. instaui/arco/components/alert.py +42 -0
  10. instaui/arco/components/anchor.py +42 -0
  11. instaui/arco/components/auto_complete.py +96 -0
  12. instaui/arco/components/avatar.py +55 -0
  13. instaui/arco/components/back_top.py +14 -0
  14. instaui/arco/components/badge.py +14 -0
  15. instaui/arco/components/breadcrumb.py +14 -0
  16. instaui/arco/components/button.py +43 -0
  17. instaui/arco/components/calendar.py +47 -0
  18. instaui/arco/components/card.py +14 -0
  19. instaui/arco/components/carousel.py +33 -0
  20. instaui/arco/components/cascader.py +111 -0
  21. instaui/arco/components/checkbox.py +32 -0
  22. instaui/arco/components/collapse.py +31 -0
  23. instaui/arco/components/color_picker.py +45 -0
  24. instaui/arco/components/comment.py +14 -0
  25. instaui/arco/components/config_provider.py +13 -0
  26. instaui/arco/components/date_picker.py +111 -0
  27. instaui/arco/components/descriptions.py +14 -0
  28. instaui/arco/components/divider.py +13 -0
  29. instaui/arco/components/drawer.py +98 -0
  30. instaui/arco/components/dropdown.py +45 -0
  31. instaui/arco/components/empty.py +14 -0
  32. instaui/arco/components/form.py +55 -0
  33. instaui/arco/components/icon.py +17 -0
  34. instaui/arco/components/image.py +33 -0
  35. instaui/arco/components/input.py +102 -0
  36. instaui/arco/components/input_number.py +97 -0
  37. instaui/arco/components/input_password.py +38 -0
  38. instaui/arco/components/input_search.py +37 -0
  39. instaui/arco/components/input_tag.py +110 -0
  40. instaui/arco/components/layout.py +13 -0
  41. instaui/arco/components/layout_content.py +6 -0
  42. instaui/arco/components/layout_footer.py +6 -0
  43. instaui/arco/components/layout_header.py +6 -0
  44. instaui/arco/components/layout_sider.py +53 -0
  45. instaui/arco/components/link.py +36 -0
  46. instaui/arco/components/list.py +68 -0
  47. instaui/arco/components/mention.py +97 -0
  48. instaui/arco/components/menu.py +88 -0
  49. instaui/arco/components/modal.py +97 -0
  50. instaui/arco/components/overflow_list.py +29 -0
  51. instaui/arco/components/page_header.py +29 -0
  52. instaui/arco/components/pagination.py +45 -0
  53. instaui/arco/components/pop_confirm.py +58 -0
  54. instaui/arco/components/popover.py +32 -0
  55. instaui/arco/components/progress.py +14 -0
  56. instaui/arco/components/radio.py +40 -0
  57. instaui/arco/components/radio_group.py +42 -0
  58. instaui/arco/components/rate.py +45 -0
  59. instaui/arco/components/resize_box.py +62 -0
  60. instaui/arco/components/result.py +14 -0
  61. instaui/arco/components/select.py +182 -0
  62. instaui/arco/components/skeleton.py +14 -0
  63. instaui/arco/components/slider.py +38 -0
  64. instaui/arco/components/space.py +14 -0
  65. instaui/arco/components/spin.py +14 -0
  66. instaui/arco/components/split.py +76 -0
  67. instaui/arco/components/statistic.py +14 -0
  68. instaui/arco/components/steps.py +32 -0
  69. instaui/arco/components/switch.py +57 -0
  70. instaui/arco/components/tab_pane.py +12 -0
  71. instaui/arco/components/table.py +276 -0
  72. instaui/arco/components/tabs.py +101 -0
  73. instaui/arco/components/tag.py +42 -0
  74. instaui/arco/components/textarea.py +84 -0
  75. instaui/arco/components/time_picker.py +76 -0
  76. instaui/arco/components/timeline.py +14 -0
  77. instaui/arco/components/tooltip.py +29 -0
  78. instaui/arco/components/transfer.py +58 -0
  79. instaui/arco/components/tree.py +120 -0
  80. instaui/arco/components/tree_select.py +86 -0
  81. instaui/arco/components/trigger.py +58 -0
  82. instaui/arco/components/typography.py +142 -0
  83. instaui/arco/components/upload.py +71 -0
  84. instaui/arco/components/verification_code.py +58 -0
  85. instaui/arco/components/watermark.py +14 -0
  86. instaui/arco/locales/__init__.py +4 -0
  87. instaui/arco/locales/_index.py +31 -0
  88. instaui/arco/locales/en_us.py +227 -0
  89. instaui/arco/locales/zh_cn.py +224 -0
  90. instaui/arco/setup.py +36 -0
  91. instaui/arco/static/instaui-arco.css +1 -0
  92. instaui/arco/static/instaui-arco.js +55771 -0
  93. instaui/arco/types.py +24 -0
  94. instaui/boot_info.py +43 -0
  95. instaui/common/jsonable.py +37 -0
  96. instaui/components/__init__.py +0 -0
  97. instaui/components/column.py +26 -0
  98. instaui/components/component.py +47 -0
  99. instaui/components/content.py +34 -0
  100. instaui/components/directive.py +55 -0
  101. instaui/components/element.py +573 -0
  102. instaui/components/grid.py +213 -0
  103. instaui/components/html/__init__.py +49 -0
  104. instaui/components/html/_mixins.py +34 -0
  105. instaui/components/html/_preset.py +4 -0
  106. instaui/components/html/button.py +38 -0
  107. instaui/components/html/checkbox.py +35 -0
  108. instaui/components/html/date.py +28 -0
  109. instaui/components/html/div.py +7 -0
  110. instaui/components/html/form.py +7 -0
  111. instaui/components/html/heading.py +51 -0
  112. instaui/components/html/input.py +28 -0
  113. instaui/components/html/label.py +21 -0
  114. instaui/components/html/li.py +17 -0
  115. instaui/components/html/link.py +31 -0
  116. instaui/components/html/number.py +34 -0
  117. instaui/components/html/paragraph.py +29 -0
  118. instaui/components/html/range.py +48 -0
  119. instaui/components/html/select.py +69 -0
  120. instaui/components/html/span.py +19 -0
  121. instaui/components/html/table.py +36 -0
  122. instaui/components/html/textarea.py +28 -0
  123. instaui/components/html/ul.py +20 -0
  124. instaui/components/label.py +5 -0
  125. instaui/components/markdown/markdown.js +33 -0
  126. instaui/components/markdown/markdown.py +41 -0
  127. instaui/components/markdown/static/github-markdown.css +12 -0
  128. instaui/components/markdown/static/marked.esm.js +2579 -0
  129. instaui/components/match.py +108 -0
  130. instaui/components/row.py +17 -0
  131. instaui/components/shiki_code/shiki_code.js +126 -0
  132. instaui/components/shiki_code/shiki_code.py +99 -0
  133. instaui/components/shiki_code/static/langs/css.mjs +5 -0
  134. instaui/components/shiki_code/static/langs/markdown.mjs +5 -0
  135. instaui/components/shiki_code/static/langs/python.mjs +5 -0
  136. instaui/components/shiki_code/static/langs/shell.mjs +2 -0
  137. instaui/components/shiki_code/static/langs/shellscript.mjs +5 -0
  138. instaui/components/shiki_code/static/shiki-core.js +5784 -0
  139. instaui/components/shiki_code/static/shiki-style.css +179 -0
  140. instaui/components/shiki_code/static/shiki-transformers.js +461 -0
  141. instaui/components/shiki_code/static/themes/vitesse-dark.mjs +2 -0
  142. instaui/components/shiki_code/static/themes/vitesse-light.mjs +2 -0
  143. instaui/components/slot.py +81 -0
  144. instaui/components/transition_group.py +9 -0
  145. instaui/components/value_element.py +52 -0
  146. instaui/components/vfor.py +142 -0
  147. instaui/components/vif.py +42 -0
  148. instaui/consts.py +23 -0
  149. instaui/dependencies/component_dependency.py +22 -0
  150. instaui/dependencies/plugin_dependency.py +28 -0
  151. instaui/event/event_mixin.py +12 -0
  152. instaui/event/js_event.py +82 -0
  153. instaui/event/vue_event.py +66 -0
  154. instaui/event/web_event.py +123 -0
  155. instaui/experimental/__init__.py +3 -0
  156. instaui/experimental/debug.py +48 -0
  157. instaui/extra_libs/_echarts.py +3 -0
  158. instaui/extra_libs/_import_error.py +9 -0
  159. instaui/extra_libs/_mermaid.py +3 -0
  160. instaui/extra_libs/_shiki_code.py +3 -0
  161. instaui/fastapi_server/_utils.py +42 -0
  162. instaui/fastapi_server/_uvicorn.py +37 -0
  163. instaui/fastapi_server/debug_mode_router.py +60 -0
  164. instaui/fastapi_server/dependency_router.py +28 -0
  165. instaui/fastapi_server/event_router.py +58 -0
  166. instaui/fastapi_server/middlewares.py +19 -0
  167. instaui/fastapi_server/request_context.py +19 -0
  168. instaui/fastapi_server/resource.py +30 -0
  169. instaui/fastapi_server/server.py +308 -0
  170. instaui/fastapi_server/watch_router.py +53 -0
  171. instaui/handlers/_utils.py +88 -0
  172. instaui/handlers/event_handler.py +60 -0
  173. instaui/handlers/watch_handler.py +61 -0
  174. instaui/html_tools.py +94 -0
  175. instaui/inject.py +33 -0
  176. instaui/js/__init__.py +4 -0
  177. instaui/js/js_output.py +15 -0
  178. instaui/js/lambda_func.py +35 -0
  179. instaui/launch_collector.py +52 -0
  180. instaui/page_info.py +13 -0
  181. instaui/runtime/__init__.py +29 -0
  182. instaui/runtime/_app.py +234 -0
  183. instaui/runtime/_inner_helper.py +9 -0
  184. instaui/runtime/_link_manager.py +89 -0
  185. instaui/runtime/context.py +47 -0
  186. instaui/runtime/dataclass.py +30 -0
  187. instaui/runtime/resource.py +65 -0
  188. instaui/runtime/scope.py +133 -0
  189. instaui/runtime/ui_state_scope.py +15 -0
  190. instaui/settings/__init__.py +4 -0
  191. instaui/settings/__settings.py +13 -0
  192. instaui/shadcn_classless/_index.py +42 -0
  193. instaui/shadcn_classless/static/shadcn-classless.css +403 -0
  194. instaui/skip.py +12 -0
  195. instaui/spa_router/__init__.py +26 -0
  196. instaui/spa_router/_components.py +35 -0
  197. instaui/spa_router/_file_base_utils.py +273 -0
  198. instaui/spa_router/_functions.py +122 -0
  199. instaui/spa_router/_install.py +11 -0
  200. instaui/spa_router/_route_model.py +117 -0
  201. instaui/spa_router/_router_box.py +40 -0
  202. instaui/spa_router/_router_output.py +22 -0
  203. instaui/spa_router/_router_param_var.py +51 -0
  204. instaui/spa_router/_types.py +4 -0
  205. instaui/spa_router/templates/page_routes +60 -0
  206. instaui/static/insta-ui.css +1 -0
  207. instaui/static/insta-ui.esm-browser.prod.js +3717 -0
  208. instaui/static/insta-ui.ico +0 -0
  209. instaui/static/insta-ui.js.map +1 -0
  210. instaui/static/instaui-tools-browser.js +511 -0
  211. instaui/static/templates/debug/sse.html +117 -0
  212. instaui/static/templates/web.html +74 -0
  213. instaui/static/templates/webview.html +78 -0
  214. instaui/static/templates/zero.html +71 -0
  215. instaui/static/vue.esm-browser.prod.js +9 -0
  216. instaui/static/vue.global.prod.js +9 -0
  217. instaui/static/vue.runtime.esm-browser.prod.js +5 -0
  218. instaui/systems/file_system.py +6 -0
  219. instaui/systems/func_system.py +119 -0
  220. instaui/systems/js_system.py +22 -0
  221. instaui/systems/module_system.py +46 -0
  222. instaui/systems/pydantic_system.py +27 -0
  223. instaui/systems/string_system.py +10 -0
  224. instaui/tailwind/__init__.py +6 -0
  225. instaui/tailwind/_index.py +24 -0
  226. instaui/tailwind/static/tailwindcss-v3.min.js +62 -0
  227. instaui/tailwind/static/tailwindcss-v4.min.js +8 -0
  228. instaui/template/__init__.py +4 -0
  229. instaui/template/_utils.py +23 -0
  230. instaui/template/env.py +7 -0
  231. instaui/template/web_template.py +49 -0
  232. instaui/template/webview_template.py +48 -0
  233. instaui/template/zero_template.py +105 -0
  234. instaui/ui/__init__.py +144 -0
  235. instaui/ui/__init__.pyi +149 -0
  236. instaui/ui/events.py +25 -0
  237. instaui/ui_functions/input_slient_data.py +16 -0
  238. instaui/ui_functions/server.py +15 -0
  239. instaui/ui_functions/str_format.py +36 -0
  240. instaui/ui_functions/ui_page.py +16 -0
  241. instaui/ui_functions/ui_types.py +13 -0
  242. instaui/ui_functions/url_location.py +33 -0
  243. instaui/vars/_types.py +8 -0
  244. instaui/vars/data.py +68 -0
  245. instaui/vars/element_ref.py +40 -0
  246. instaui/vars/event_context.py +49 -0
  247. instaui/vars/event_extend.py +0 -0
  248. instaui/vars/js_computed.py +117 -0
  249. instaui/vars/mixin_types/common_type.py +5 -0
  250. instaui/vars/mixin_types/element_binding.py +16 -0
  251. instaui/vars/mixin_types/observable.py +7 -0
  252. instaui/vars/mixin_types/pathable.py +14 -0
  253. instaui/vars/mixin_types/py_binding.py +13 -0
  254. instaui/vars/mixin_types/str_format_binding.py +8 -0
  255. instaui/vars/mixin_types/var_type.py +5 -0
  256. instaui/vars/path_var.py +90 -0
  257. instaui/vars/ref.py +103 -0
  258. instaui/vars/slot_prop.py +46 -0
  259. instaui/vars/state.py +97 -0
  260. instaui/vars/types.py +24 -0
  261. instaui/vars/vfor_item.py +204 -0
  262. instaui/vars/vue_computed.py +81 -0
  263. instaui/vars/web_computed.py +209 -0
  264. instaui/vars/web_view_computed.py +1 -0
  265. instaui/version.py +3 -0
  266. instaui/watch/_types.py +4 -0
  267. instaui/watch/_utils.py +3 -0
  268. instaui/watch/js_watch.py +110 -0
  269. instaui/watch/vue_watch.py +77 -0
  270. instaui/watch/web_watch.py +181 -0
  271. instaui/webview/__init__.py +2 -0
  272. instaui/webview/_utils.py +8 -0
  273. instaui/webview/api.py +72 -0
  274. instaui/webview/func.py +114 -0
  275. instaui/webview/index.py +161 -0
  276. instaui/webview/resource.py +172 -0
  277. instaui/zero/__init__.py +3 -0
  278. instaui/zero/func.py +123 -0
  279. instaui/zero/scope.py +109 -0
  280. instaui-0.1.15.dist-info/METADATA +152 -0
  281. instaui-0.1.15.dist-info/RECORD +283 -0
  282. instaui-0.1.15.dist-info/WHEEL +5 -0
  283. 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
@@ -0,0 +1,4 @@
1
+ from .lambda_func import LambdaFunc as func
2
+
3
+
4
+ __all__ = ["func"]