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 CHANGED
@@ -1,5 +1,10 @@
1
1
  from pyview.live_view import LiveView
2
- from pyview.live_socket import LiveViewSocket
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
  ]
@@ -1,4 +1,4 @@
1
- from typing import TypeVar, Any, Generic, Optional, Callable
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: Callable[..., Base]
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.__fields__.keys()
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: Callable[..., Base]) -> ChangeSet[Base]:
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 Any, TypeVar, Generic, TYPE_CHECKING, Optional
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 LiveViewSocket(Generic[T]):
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 as e:
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, UnconnectedSocket
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: AnySocket, session: Session):
25
+ async def mount(self, socket: LiveViewSocket[T], session: Session):
27
26
  pass
28
27
 
29
- async def handle_event(self, event, payload, socket: LiveViewSocket[T]):
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: LiveViewSocket[T]):
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: AnySocket):
34
+ async def handle_params(self, url: URL, params, socket: LiveViewSocket[T]):
36
35
  pass
37
36
 
38
- async def disconnect(self, socket: LiveViewSocket[T]):
37
+ async def disconnect(self, socket: ConnectedLiveViewSocket[T]):
39
38
  pass
40
39
 
41
40
  async def render(self, assigns: T) -> RenderedContent:
@@ -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 DictConvertable(Protocol):
9
- def asdict(self) -> dict[str, Any]:
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
- Assigns = Union[dict[str, Any], DictConvertable]
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 = LiveViewSocket(websocket, topic, lv)
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: LiveViewSocket, prev_rendered: dict[str, Any]
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: LiveViewSocket):
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.25
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.9,<3.12
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.7.1,<3.0.0)
40
- Requires-Dist: starlette (==0.37.2)
41
- Requires-Dist: uvicorn (==0.20.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=Pk-RgSkIG6fkLH3CwrF5tptoq7n72w0O73k2vAW4CAQ,374
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=B1q1nXwI2iuZZQpE3P2T0PpwI21PHjqcsuIQmkKPCvI,1747
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=6SLEkEBzK-zIUNh_5j_OG5t6IHGTDNCpGXk7D7SMNJ4,4370
15
- pyview/live_view.py,sha256=A0vCCvHUy39_eEhRzDbYMEDzgpRqseZPjCnBAMjogxw,1406
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=wSKyBw7ejpUY5qXUZdE36Jeeix8Of0CUq8eZdQwxXyg,1864
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=Vi5aIoxz_Z9qOEcA5fgxsSoJtLz7n_ytbJ8vZiWDvGc,8473
43
- pyview_web-0.0.25.dist-info/LICENSE,sha256=M_bADaBm9_MV9llX3lCicksLhwk3eZUjA2srE0uUWr0,1071
44
- pyview_web-0.0.25.dist-info/METADATA,sha256=-UhlfnbHQaDx-Elu2KFZRxrV2brUwuJ2GAwFfrRWRGU,5276
45
- pyview_web-0.0.25.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
46
- pyview_web-0.0.25.dist-info/RECORD,,
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,,