pyview-web 0.0.25__py3-none-any.whl → 0.1.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.
Potentially problematic release.
This version of pyview-web might be problematic. Click here for more details.
- pyview/__init__.py +8 -1
- pyview/changesets/changesets.py +4 -4
- pyview/live_socket.py +20 -4
- pyview/live_view.py +6 -7
- pyview/template/live_template.py +13 -10
- pyview/ws_handler.py +4 -4
- {pyview_web-0.0.25.dist-info → pyview_web-0.1.0.dist-info}/METADATA +6 -7
- {pyview_web-0.0.25.dist-info → pyview_web-0.1.0.dist-info}/RECORD +10 -10
- {pyview_web-0.0.25.dist-info → pyview_web-0.1.0.dist-info}/LICENSE +0 -0
- {pyview_web-0.0.25.dist-info → pyview_web-0.1.0.dist-info}/WHEEL +0 -0
pyview/__init__.py
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
from pyview.live_view import LiveView
|
|
2
|
-
from pyview.live_socket import
|
|
2
|
+
from pyview.live_socket import (
|
|
3
|
+
LiveViewSocket,
|
|
4
|
+
is_connected,
|
|
5
|
+
ConnectedLiveViewSocket,
|
|
6
|
+
UnconnectedSocket,
|
|
7
|
+
)
|
|
3
8
|
from pyview.pyview import PyView, defaultRootTemplate
|
|
4
9
|
from pyview.js import js
|
|
5
10
|
from pyview.pyview import RootTemplateContext, RootTemplate
|
|
@@ -12,4 +17,6 @@ __all__ = [
|
|
|
12
17
|
"js",
|
|
13
18
|
"RootTemplateContext",
|
|
14
19
|
"RootTemplate",
|
|
20
|
+
"is_connected",
|
|
21
|
+
"ConnectedLiveViewSocket",
|
|
15
22
|
]
|
pyview/changesets/changesets.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import TypeVar, Any, Generic, Optional
|
|
1
|
+
from typing import TypeVar, Any, Generic, Optional
|
|
2
2
|
from pydantic import BaseModel, ValidationError
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from types import SimpleNamespace
|
|
@@ -8,7 +8,7 @@ Base = TypeVar("Base", bound=BaseModel)
|
|
|
8
8
|
|
|
9
9
|
@dataclass
|
|
10
10
|
class ChangeSet(Generic[Base]):
|
|
11
|
-
cls:
|
|
11
|
+
cls: type[Base]
|
|
12
12
|
changes: dict[str, Any]
|
|
13
13
|
errors: dict[str, Any]
|
|
14
14
|
valid: bool
|
|
@@ -29,7 +29,7 @@ class ChangeSet(Generic[Base]):
|
|
|
29
29
|
|
|
30
30
|
@property
|
|
31
31
|
def fields(self) -> list[str]:
|
|
32
|
-
return self.cls.
|
|
32
|
+
return list(self.cls.model_fields)
|
|
33
33
|
|
|
34
34
|
def save(self, payload: dict[str, Any]) -> Optional[Base]:
|
|
35
35
|
self.errors = {}
|
|
@@ -58,5 +58,5 @@ class ChangeSet(Generic[Base]):
|
|
|
58
58
|
self.valid = False
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
def change_set(cls:
|
|
61
|
+
def change_set(cls: type[Base]) -> ChangeSet[Base]:
|
|
62
62
|
return ChangeSet(cls, {}, {}, False)
|
pyview/live_socket.py
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from starlette.websockets import WebSocket
|
|
3
3
|
import json
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
TypeVar,
|
|
7
|
+
Generic,
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Optional,
|
|
10
|
+
Union,
|
|
11
|
+
TypeAlias,
|
|
12
|
+
TypeGuard,
|
|
13
|
+
)
|
|
5
14
|
from urllib.parse import urlencode
|
|
6
15
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
7
16
|
from pyview.vendor.flet.pubsub import PubSubHub, PubSub
|
|
@@ -21,10 +30,14 @@ pub_sub_hub = PubSubHub()
|
|
|
21
30
|
T = TypeVar("T")
|
|
22
31
|
|
|
23
32
|
|
|
33
|
+
def is_connected(socket: LiveViewSocket[T]) -> TypeGuard["ConnectedLiveViewSocket[T]"]:
|
|
34
|
+
return socket.connected
|
|
35
|
+
|
|
36
|
+
|
|
24
37
|
class UnconnectedSocket(Generic[T]):
|
|
25
38
|
context: T
|
|
26
|
-
connected: bool = False
|
|
27
39
|
live_title: Optional[str] = None
|
|
40
|
+
connected: bool = False
|
|
28
41
|
|
|
29
42
|
def allow_upload(
|
|
30
43
|
self, upload_name: str, constraints: UploadConstraints
|
|
@@ -32,7 +45,7 @@ class UnconnectedSocket(Generic[T]):
|
|
|
32
45
|
return UploadConfig(name=upload_name, constraints=constraints)
|
|
33
46
|
|
|
34
47
|
|
|
35
|
-
class
|
|
48
|
+
class ConnectedLiveViewSocket(Generic[T]):
|
|
36
49
|
context: T
|
|
37
50
|
live_title: Optional[str] = None
|
|
38
51
|
pending_events: list[tuple[str, Any]]
|
|
@@ -86,7 +99,7 @@ class LiveViewSocket(Generic[T]):
|
|
|
86
99
|
|
|
87
100
|
try:
|
|
88
101
|
await self.websocket.send_text(json.dumps(resp))
|
|
89
|
-
except Exception
|
|
102
|
+
except Exception:
|
|
90
103
|
for id in self.scheduled_jobs:
|
|
91
104
|
print("Removing job", id)
|
|
92
105
|
scheduler.remove_job(id)
|
|
@@ -143,3 +156,6 @@ class LiveViewSocket(Generic[T]):
|
|
|
143
156
|
await self.liveview.disconnect(self)
|
|
144
157
|
except Exception:
|
|
145
158
|
pass
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
LiveViewSocket: TypeAlias = Union[ConnectedLiveViewSocket[T], UnconnectedSocket[T]]
|
pyview/live_view.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import TypeVar, Generic, Optional, Union, Any
|
|
2
|
-
from .live_socket import LiveViewSocket,
|
|
2
|
+
from .live_socket import LiveViewSocket, ConnectedLiveViewSocket
|
|
3
3
|
from pyview.template import (
|
|
4
4
|
LiveTemplate,
|
|
5
5
|
template_file,
|
|
@@ -12,7 +12,6 @@ from urllib.parse import ParseResult
|
|
|
12
12
|
|
|
13
13
|
T = TypeVar("T")
|
|
14
14
|
|
|
15
|
-
AnySocket = Union[LiveViewSocket[T], UnconnectedSocket[T]]
|
|
16
15
|
Session = dict[str, Any]
|
|
17
16
|
|
|
18
17
|
# TODO: ideally this would always be a ParseResult, but we need to update push_patch
|
|
@@ -23,19 +22,19 @@ class LiveView(Generic[T]):
|
|
|
23
22
|
def __init__(self):
|
|
24
23
|
pass
|
|
25
24
|
|
|
26
|
-
async def mount(self, socket:
|
|
25
|
+
async def mount(self, socket: LiveViewSocket[T], session: Session):
|
|
27
26
|
pass
|
|
28
27
|
|
|
29
|
-
async def handle_event(self, event, payload, socket:
|
|
28
|
+
async def handle_event(self, event, payload, socket: ConnectedLiveViewSocket[T]):
|
|
30
29
|
pass
|
|
31
30
|
|
|
32
|
-
async def handle_info(self, event: InfoEvent, socket:
|
|
31
|
+
async def handle_info(self, event: InfoEvent, socket: ConnectedLiveViewSocket[T]):
|
|
33
32
|
pass
|
|
34
33
|
|
|
35
|
-
async def handle_params(self, url: URL, params, socket:
|
|
34
|
+
async def handle_params(self, url: URL, params, socket: LiveViewSocket[T]):
|
|
36
35
|
pass
|
|
37
36
|
|
|
38
|
-
async def disconnect(self, socket:
|
|
37
|
+
async def disconnect(self, socket: ConnectedLiveViewSocket[T]):
|
|
39
38
|
pass
|
|
40
39
|
|
|
41
40
|
async def render(self, assigns: T) -> RenderedContent:
|
pyview/template/live_template.py
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
from pyview.vendor.ibis import Template
|
|
2
|
-
from typing import Any, Union, Protocol, Optional
|
|
3
|
-
from dataclasses import asdict
|
|
2
|
+
from typing import Any, Union, Protocol, Optional, ClassVar
|
|
3
|
+
from dataclasses import asdict, Field
|
|
4
4
|
from .serializer import serialize
|
|
5
5
|
import os.path
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
class DataclassInstance(Protocol):
|
|
9
|
+
__dataclass_fields__: ClassVar[dict[str, Field[Any]]]
|
|
10
|
+
|
|
11
11
|
|
|
12
|
+
Assigns = Union[dict[str, Any], DataclassInstance]
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
# TODO: should we still support this?
|
|
16
|
+
class DictConvertable(Protocol):
|
|
17
|
+
def asdict(self) -> dict[str, Any]: ...
|
|
14
18
|
|
|
15
19
|
|
|
16
20
|
class LiveTemplate:
|
|
@@ -37,11 +41,9 @@ class LiveTemplate:
|
|
|
37
41
|
|
|
38
42
|
|
|
39
43
|
class RenderedContent(Protocol):
|
|
40
|
-
def tree(self) -> dict[str, Any]:
|
|
41
|
-
...
|
|
44
|
+
def tree(self) -> dict[str, Any]: ...
|
|
42
45
|
|
|
43
|
-
def text(self) -> str:
|
|
44
|
-
...
|
|
46
|
+
def text(self) -> str: ...
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
class LiveRender:
|
|
@@ -58,6 +60,7 @@ class LiveRender:
|
|
|
58
60
|
|
|
59
61
|
_cache = {}
|
|
60
62
|
|
|
63
|
+
|
|
61
64
|
def template_file(filename: str) -> Optional[LiveTemplate]:
|
|
62
65
|
"""Renders a template file with the given assigns."""
|
|
63
66
|
if not os.path.isfile(filename):
|
pyview/ws_handler.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import Optional, Any
|
|
|
2
2
|
import json
|
|
3
3
|
from starlette.websockets import WebSocket, WebSocketDisconnect
|
|
4
4
|
from urllib.parse import urlparse, parse_qs
|
|
5
|
-
from pyview.live_socket import LiveViewSocket
|
|
5
|
+
from pyview.live_socket import ConnectedLiveViewSocket, LiveViewSocket
|
|
6
6
|
from pyview.live_routes import LiveViewLookup
|
|
7
7
|
from pyview.csrf import validate_csrf_token
|
|
8
8
|
from pyview.session import deserialize_session
|
|
@@ -44,7 +44,7 @@ class LiveSocketHandler:
|
|
|
44
44
|
url = urlparse(payload["url"])
|
|
45
45
|
lv = self.routes.get(url.path)
|
|
46
46
|
await self.check_auth(websocket, lv)
|
|
47
|
-
socket =
|
|
47
|
+
socket = ConnectedLiveViewSocket(websocket, topic, lv)
|
|
48
48
|
|
|
49
49
|
session = {}
|
|
50
50
|
if "session" in payload:
|
|
@@ -75,7 +75,7 @@ class LiveSocketHandler:
|
|
|
75
75
|
self.sessions -= 1
|
|
76
76
|
|
|
77
77
|
async def handle_connected(
|
|
78
|
-
self, myJoinId, socket:
|
|
78
|
+
self, myJoinId, socket: ConnectedLiveViewSocket, prev_rendered: dict[str, Any]
|
|
79
79
|
):
|
|
80
80
|
while True:
|
|
81
81
|
message = await socket.websocket.receive()
|
|
@@ -235,7 +235,7 @@ class LiveSocketHandler:
|
|
|
235
235
|
)
|
|
236
236
|
|
|
237
237
|
|
|
238
|
-
async def _render(socket:
|
|
238
|
+
async def _render(socket: ConnectedLiveViewSocket):
|
|
239
239
|
rendered = (await socket.liveview.render(socket.context)).tree()
|
|
240
240
|
|
|
241
241
|
if socket.live_title:
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyview-web
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: LiveView in Python
|
|
5
5
|
Home-page: https://pyview.rocks
|
|
6
6
|
License: MIT
|
|
7
7
|
Keywords: web,api,LiveView
|
|
8
8
|
Author: Larry Ogrodnek
|
|
9
9
|
Author-email: ogrodnek@gmail.com
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10,<3.13
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: Environment :: Web Environment
|
|
13
13
|
Classifier: Framework :: AsyncIO
|
|
14
|
-
Classifier: Framework :: FastAPI
|
|
15
14
|
Classifier: Framework :: Pydantic
|
|
16
15
|
Classifier: Intended Audience :: Developers
|
|
17
16
|
Classifier: Intended Audience :: Information Technology
|
|
@@ -20,9 +19,9 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
20
19
|
Classifier: Operating System :: OS Independent
|
|
21
20
|
Classifier: Programming Language :: Python
|
|
22
21
|
Classifier: Programming Language :: Python :: 3
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
24
22
|
Classifier: Programming Language :: Python :: 3.10
|
|
25
23
|
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
25
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
27
26
|
Classifier: Topic :: Internet
|
|
28
27
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
@@ -36,9 +35,9 @@ Requires-Dist: APScheduler (==3.9.1.post1)
|
|
|
36
35
|
Requires-Dist: itsdangerous (>=2.1.2,<3.0.0)
|
|
37
36
|
Requires-Dist: markupsafe (>=2.1.2,<3.0.0)
|
|
38
37
|
Requires-Dist: psutil (>=5.9.4,<6.0.0)
|
|
39
|
-
Requires-Dist: pydantic (>=2.
|
|
40
|
-
Requires-Dist: starlette (==0.
|
|
41
|
-
Requires-Dist: uvicorn (==0.
|
|
38
|
+
Requires-Dist: pydantic (>=2.9.2,<3.0.0)
|
|
39
|
+
Requires-Dist: starlette (==0.38.5)
|
|
40
|
+
Requires-Dist: uvicorn (==0.30.6)
|
|
42
41
|
Requires-Dist: wsproto (==1.2.0)
|
|
43
42
|
Project-URL: Repository, https://github.com/ogrodnek/pyview
|
|
44
43
|
Description-Content-Type: text/markdown
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
pyview/__init__.py,sha256=
|
|
1
|
+
pyview/__init__.py,sha256=PG8coDKOMFTBuG9e3XxHJX1LipT0VDeki8CjDO-ZTG4,504
|
|
2
2
|
pyview/assets/js/app.js,sha256=XuuSgEMY4hx8v0OuEPwaa7trktu_vppL0tc3Bs9Fw7s,2524
|
|
3
3
|
pyview/assets/package-lock.json,sha256=kFCrEUJc3G7VD7EsBQf6__EKQhaKAok-I5rrwiAoX0w,2425
|
|
4
4
|
pyview/assets/package.json,sha256=E6xaX8KMUAektIIedLmI55jGnmlNMSeD2tgKYXWk1vg,151
|
|
@@ -6,20 +6,20 @@ pyview/auth/__init__.py,sha256=vMlirETRhD4va61NOzwg8VY8ep9wVOF96GznJGBmzD0,109
|
|
|
6
6
|
pyview/auth/provider.py,sha256=fwriy2JZcOStutVXD-8VlMPAFXjILCM0l08lhTgmuyE,935
|
|
7
7
|
pyview/auth/required.py,sha256=ZtNmLFth9nK39RxDiJkSzArXwS5Cvr55MUAzfJ1F2e0,1418
|
|
8
8
|
pyview/changesets/__init__.py,sha256=55CLari2JHZtwy4hapHe7CqUyKjcP4dkM_t5d3CY2gU,46
|
|
9
|
-
pyview/changesets/changesets.py,sha256=
|
|
9
|
+
pyview/changesets/changesets.py,sha256=hImmvB_jS6RyLr5Mas5L7DO_0d805jR3c41LKJlnNL4,1720
|
|
10
10
|
pyview/csrf.py,sha256=VIURva9EJqXXYGC7engweh3SwDQCnHlhV2zWdcdnFqc,789
|
|
11
11
|
pyview/events.py,sha256=Zv8G2F1XeXUk1wrnfomeFfxB0OPYmHdjSvxRjQew3No,125
|
|
12
12
|
pyview/js.py,sha256=4OnPEfBfuvmekeQlm9444As4PLR22zLMIyyzQIIkmls,751
|
|
13
13
|
pyview/live_routes.py,sha256=tsKFh2gmH2BWsjsZQZErzRp_-KiAZcn4lFKNLRIN5Nc,498
|
|
14
|
-
pyview/live_socket.py,sha256=
|
|
15
|
-
pyview/live_view.py,sha256=
|
|
14
|
+
pyview/live_socket.py,sha256=BylR8AiMLP0ZF8Lm2NxE1DZq9AX2g1YkcrNuK2a_tLY,4644
|
|
15
|
+
pyview/live_view.py,sha256=3q-n1QoZR-JwPac4Sece_Q3nG3Kj6nEyVQwzxGqVt44,1396
|
|
16
16
|
pyview/phx_message.py,sha256=DUdPfl6tlw9K0FNXJ35ehq03JGgynvwA_JItHQ_dxMQ,2007
|
|
17
17
|
pyview/pyview.py,sha256=xy8on2f-chU4JWVy6zGTDqjP8BW-kOoi16voNIgWRg4,2478
|
|
18
18
|
pyview/secret.py,sha256=HbaNpGAkFs4uxMVAmk9HwE3FIehg7dmwEOlED7C9moM,363
|
|
19
19
|
pyview/session.py,sha256=nC8ExyVwfCgQfx9T-aJGyFhr2C7jsrEY_QFkaXtP28U,432
|
|
20
20
|
pyview/static/assets/app.js,sha256=QoXfdcOCYwVYJftvjsIIVwFye7onaOJMxRpalyYqoMU,200029
|
|
21
21
|
pyview/template/__init__.py,sha256=c5hLRfsF2fDOz8aOsoOgoCeBV6VBzdqN_Ktg3mYPw8A,509
|
|
22
|
-
pyview/template/live_template.py,sha256=
|
|
22
|
+
pyview/template/live_template.py,sha256=JmHHuH6mu4OF-M6pZGTP3vgQWZizp0YS9q1QgtggD9g,1993
|
|
23
23
|
pyview/template/render_diff.py,sha256=v7EVmn8oJdh809N0vnSLK8OiDs1BOpErF36y4VUo9ew,1214
|
|
24
24
|
pyview/template/root_template.py,sha256=zCUs1bt8R7qynhBE0tTSEYfdkGtbeKNmPhwzRiFNdsI,2031
|
|
25
25
|
pyview/template/serializer.py,sha256=WDZfqJr2LMlf36fUW2CmWc2aREc63553_y_GRP2-qYc,826
|
|
@@ -39,8 +39,8 @@ pyview/vendor/ibis/nodes.py,sha256=TgFt4q5MrVW3gC3PVitrs2LyXKllRveooM7XKydNATk,2
|
|
|
39
39
|
pyview/vendor/ibis/template.py,sha256=IX9z-Ig13yJyRnMqtB52eiRLe002qdIxnfa7fYEXLqM,2314
|
|
40
40
|
pyview/vendor/ibis/tree.py,sha256=hg8f-fKHeo6DE8R-QgAhdvEaZ8rKyz7p0nGwPy0CBTs,2509
|
|
41
41
|
pyview/vendor/ibis/utils.py,sha256=nLSaxPR9vMphzV9qinlz_Iurv9c49Ps6Knv8vyNlewU,2768
|
|
42
|
-
pyview/ws_handler.py,sha256=
|
|
43
|
-
pyview_web-0.0.
|
|
44
|
-
pyview_web-0.0.
|
|
45
|
-
pyview_web-0.0.
|
|
46
|
-
pyview_web-0.0.
|
|
42
|
+
pyview/ws_handler.py,sha256=y0LTo9ug33KN3iyBxeaLrv7Vx0kVw1IedVvxO8wm7Tg,8525
|
|
43
|
+
pyview_web-0.1.0.dist-info/LICENSE,sha256=M_bADaBm9_MV9llX3lCicksLhwk3eZUjA2srE0uUWr0,1071
|
|
44
|
+
pyview_web-0.1.0.dist-info/METADATA,sha256=ssECrZ4vS-1qy_XG3oVmRKbYnMlD75bG1vOJXt3C0nA,5244
|
|
45
|
+
pyview_web-0.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
46
|
+
pyview_web-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|