pulse-framework 0.1.64__py3-none-any.whl → 0.1.66a1__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.
- pulse/__init__.py +16 -10
- pulse/app.py +103 -26
- pulse/channel.py +3 -3
- pulse/codegen/templates/layout.py +3 -8
- pulse/{form.py → forms.py} +2 -2
- pulse/helpers.py +9 -212
- pulse/proxy.py +10 -3
- pulse/queries/client.py +5 -1
- pulse/queries/effect.py +2 -1
- pulse/queries/infinite_query.py +54 -17
- pulse/queries/query.py +58 -44
- pulse/queries/store.py +10 -2
- pulse/reactive.py +18 -7
- pulse/render_session.py +135 -12
- pulse/scheduling.py +448 -0
- {pulse_framework-0.1.64.dist-info → pulse_framework-0.1.66a1.dist-info}/METADATA +1 -1
- {pulse_framework-0.1.64.dist-info → pulse_framework-0.1.66a1.dist-info}/RECORD +19 -18
- {pulse_framework-0.1.64.dist-info → pulse_framework-0.1.66a1.dist-info}/WHEEL +0 -0
- {pulse_framework-0.1.64.dist-info → pulse_framework-0.1.66a1.dist-info}/entry_points.txt +0 -0
pulse/scheduling.py
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
from collections.abc import Awaitable, Callable
|
|
4
|
+
from typing import Any, ParamSpec, Protocol, TypeVar, override
|
|
5
|
+
|
|
6
|
+
from anyio import from_thread
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
P = ParamSpec("P")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TimerHandleLike(Protocol):
|
|
13
|
+
def cancel(self) -> None: ...
|
|
14
|
+
def cancelled(self) -> bool: ...
|
|
15
|
+
def when(self) -> float: ...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def is_pytest() -> bool:
|
|
19
|
+
"""Detect if running inside pytest using environment variables."""
|
|
20
|
+
return bool(os.environ.get("PYTEST_CURRENT_TEST")) or (
|
|
21
|
+
"PYTEST_XDIST_TESTRUNUID" in os.environ
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _resolve_registries() -> tuple["TaskRegistry", "TimerRegistry"]:
|
|
26
|
+
from pulse.context import PulseContext
|
|
27
|
+
|
|
28
|
+
ctx = PulseContext.get()
|
|
29
|
+
if ctx.render is not None:
|
|
30
|
+
return ctx.render._tasks, ctx.render._timers # pyright: ignore[reportPrivateUsage]
|
|
31
|
+
return ctx.app._tasks, ctx.app._timers # pyright: ignore[reportPrivateUsage]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def call_soon(
|
|
35
|
+
fn: Callable[P, Any], *args: P.args, **kwargs: P.kwargs
|
|
36
|
+
) -> TimerHandleLike | None:
|
|
37
|
+
"""Schedule a callback to run ASAP on the main event loop from any thread."""
|
|
38
|
+
_, timer_registry = _resolve_registries()
|
|
39
|
+
return timer_registry.call_soon(fn, *args, **kwargs)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def create_task(
|
|
43
|
+
coroutine: Awaitable[T],
|
|
44
|
+
*,
|
|
45
|
+
name: str | None = None,
|
|
46
|
+
on_done: Callable[[asyncio.Task[T]], None] | None = None,
|
|
47
|
+
) -> asyncio.Task[T]:
|
|
48
|
+
"""Create a tracked task on the active session/app registry."""
|
|
49
|
+
task_registry, _ = _resolve_registries()
|
|
50
|
+
return task_registry.create_task(coroutine, name=name, on_done=on_done)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create_future() -> asyncio.Future[Any]:
|
|
54
|
+
"""Create an asyncio Future on the main event loop from any thread."""
|
|
55
|
+
task_registry, _ = _resolve_registries()
|
|
56
|
+
return task_registry.create_future()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def later(
|
|
60
|
+
delay: float, fn: Callable[P, Any], *args: P.args, **kwargs: P.kwargs
|
|
61
|
+
) -> TimerHandleLike:
|
|
62
|
+
"""
|
|
63
|
+
Schedule `fn(*args, **kwargs)` to run after `delay` seconds.
|
|
64
|
+
Works with sync or async functions. Returns a handle; call .cancel() to cancel.
|
|
65
|
+
|
|
66
|
+
The callback runs with no reactive scope to avoid accidentally capturing
|
|
67
|
+
reactive dependencies from the calling context. Other context vars (like
|
|
68
|
+
PulseContext) are preserved normally.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
_, timer_registry = _resolve_registries()
|
|
72
|
+
return timer_registry.later(delay, fn, *args, **kwargs)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class RepeatHandle:
|
|
76
|
+
task: asyncio.Task[None] | None
|
|
77
|
+
cancelled: bool
|
|
78
|
+
|
|
79
|
+
def __init__(self) -> None:
|
|
80
|
+
self.task = None
|
|
81
|
+
self.cancelled = False
|
|
82
|
+
|
|
83
|
+
def cancel(self):
|
|
84
|
+
if self.cancelled:
|
|
85
|
+
return
|
|
86
|
+
self.cancelled = True
|
|
87
|
+
if self.task is not None and not self.task.done():
|
|
88
|
+
self.task.cancel()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def repeat(interval: float, fn: Callable[P, Any], *args: P.args, **kwargs: P.kwargs):
|
|
92
|
+
"""
|
|
93
|
+
Repeatedly run `fn(*args, **kwargs)` every `interval` seconds.
|
|
94
|
+
Works with sync or async functions.
|
|
95
|
+
For async functions, waits for completion before starting the next delay.
|
|
96
|
+
Returns a handle with .cancel() to stop future runs.
|
|
97
|
+
|
|
98
|
+
The callback runs with no reactive scope to avoid accidentally capturing
|
|
99
|
+
reactive dependencies from the calling context. Other context vars (like
|
|
100
|
+
PulseContext) are preserved normally.
|
|
101
|
+
|
|
102
|
+
Optional kwargs:
|
|
103
|
+
- immediate: bool = False # run once immediately before the first interval
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
_, timer_registry = _resolve_registries()
|
|
107
|
+
return timer_registry.repeat(interval, fn, *args, **kwargs)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class TaskRegistry:
|
|
111
|
+
_tasks: set[asyncio.Task[Any]]
|
|
112
|
+
name: str | None
|
|
113
|
+
|
|
114
|
+
def __init__(self, name: str | None = None) -> None:
|
|
115
|
+
self._tasks = set()
|
|
116
|
+
self.name = name
|
|
117
|
+
|
|
118
|
+
def track(self, task: asyncio.Task[T]) -> asyncio.Task[T]:
|
|
119
|
+
self._tasks.add(task)
|
|
120
|
+
task.add_done_callback(self._tasks.discard)
|
|
121
|
+
return task
|
|
122
|
+
|
|
123
|
+
def create_task(
|
|
124
|
+
self,
|
|
125
|
+
coroutine: Awaitable[T],
|
|
126
|
+
*,
|
|
127
|
+
name: str | None = None,
|
|
128
|
+
on_done: Callable[[asyncio.Task[T]], None] | None = None,
|
|
129
|
+
) -> asyncio.Task[T]:
|
|
130
|
+
"""Create and schedule a coroutine task on the main loop from any thread."""
|
|
131
|
+
try:
|
|
132
|
+
asyncio.get_running_loop()
|
|
133
|
+
task = asyncio.ensure_future(coroutine)
|
|
134
|
+
if name is not None:
|
|
135
|
+
task.set_name(name)
|
|
136
|
+
if on_done:
|
|
137
|
+
task.add_done_callback(on_done)
|
|
138
|
+
except RuntimeError:
|
|
139
|
+
|
|
140
|
+
async def _runner():
|
|
141
|
+
asyncio.get_running_loop()
|
|
142
|
+
task = asyncio.ensure_future(coroutine)
|
|
143
|
+
if name is not None:
|
|
144
|
+
task.set_name(name)
|
|
145
|
+
if on_done:
|
|
146
|
+
task.add_done_callback(on_done)
|
|
147
|
+
return task
|
|
148
|
+
|
|
149
|
+
task = from_thread.run(_runner)
|
|
150
|
+
|
|
151
|
+
return self.track(task)
|
|
152
|
+
|
|
153
|
+
def create_future(self) -> asyncio.Future[Any]:
|
|
154
|
+
"""Create an asyncio Future on the main event loop from any thread."""
|
|
155
|
+
try:
|
|
156
|
+
return asyncio.get_running_loop().create_future()
|
|
157
|
+
except RuntimeError:
|
|
158
|
+
|
|
159
|
+
async def _create():
|
|
160
|
+
return asyncio.get_running_loop().create_future()
|
|
161
|
+
|
|
162
|
+
return from_thread.run(_create)
|
|
163
|
+
|
|
164
|
+
def cancel_all(self) -> None:
|
|
165
|
+
for task in list(self._tasks):
|
|
166
|
+
if not task.done():
|
|
167
|
+
task.cancel()
|
|
168
|
+
self._tasks.clear()
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class TimerRegistry:
|
|
172
|
+
_handles: set[TimerHandleLike]
|
|
173
|
+
_tasks: TaskRegistry
|
|
174
|
+
name: str | None
|
|
175
|
+
|
|
176
|
+
def __init__(self, *, tasks: TaskRegistry, name: str | None = None) -> None:
|
|
177
|
+
self._handles = set()
|
|
178
|
+
self._tasks = tasks
|
|
179
|
+
self.name = name
|
|
180
|
+
|
|
181
|
+
def track(self, handle: TimerHandleLike) -> TimerHandleLike:
|
|
182
|
+
self._handles.add(handle)
|
|
183
|
+
return handle
|
|
184
|
+
|
|
185
|
+
def discard(self, handle: TimerHandleLike | None) -> None:
|
|
186
|
+
if handle is None:
|
|
187
|
+
return
|
|
188
|
+
self._handles.discard(handle)
|
|
189
|
+
|
|
190
|
+
def later(
|
|
191
|
+
self,
|
|
192
|
+
delay: float,
|
|
193
|
+
fn: Callable[P, Any],
|
|
194
|
+
*args: P.args,
|
|
195
|
+
**kwargs: P.kwargs,
|
|
196
|
+
) -> TimerHandleLike:
|
|
197
|
+
return self._schedule(delay, fn, args, dict(kwargs), untrack=True)
|
|
198
|
+
|
|
199
|
+
def call_soon(
|
|
200
|
+
self, fn: Callable[P, Any], *args: P.args, **kwargs: P.kwargs
|
|
201
|
+
) -> TimerHandleLike | None:
|
|
202
|
+
def _schedule():
|
|
203
|
+
return self._schedule_soon(fn, args, dict(kwargs))
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
asyncio.get_running_loop()
|
|
207
|
+
return _schedule()
|
|
208
|
+
except RuntimeError:
|
|
209
|
+
|
|
210
|
+
async def _runner():
|
|
211
|
+
return _schedule()
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
return from_thread.run(_runner)
|
|
215
|
+
except RuntimeError:
|
|
216
|
+
if not is_pytest():
|
|
217
|
+
raise
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
def repeat(
|
|
221
|
+
self, interval: float, fn: Callable[P, Any], *args: P.args, **kwargs: P.kwargs
|
|
222
|
+
) -> RepeatHandle:
|
|
223
|
+
from pulse.reactive import Untrack
|
|
224
|
+
|
|
225
|
+
loop = asyncio.get_running_loop()
|
|
226
|
+
handle = RepeatHandle()
|
|
227
|
+
|
|
228
|
+
async def _runner():
|
|
229
|
+
nonlocal handle
|
|
230
|
+
try:
|
|
231
|
+
while not handle.cancelled:
|
|
232
|
+
# Start counting the next interval AFTER the previous execution completes
|
|
233
|
+
await asyncio.sleep(interval)
|
|
234
|
+
if handle.cancelled:
|
|
235
|
+
break
|
|
236
|
+
try:
|
|
237
|
+
with Untrack():
|
|
238
|
+
result = fn(*args, **kwargs)
|
|
239
|
+
if asyncio.iscoroutine(result):
|
|
240
|
+
await result
|
|
241
|
+
except asyncio.CancelledError:
|
|
242
|
+
# Propagate to outer handler to finish cleanly
|
|
243
|
+
raise
|
|
244
|
+
except Exception as exc:
|
|
245
|
+
# Surface exceptions via the loop's exception handler and continue
|
|
246
|
+
loop.call_exception_handler(
|
|
247
|
+
{
|
|
248
|
+
"message": "Unhandled exception in repeat() callback",
|
|
249
|
+
"exception": exc,
|
|
250
|
+
"context": {"callback": fn},
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
except asyncio.CancelledError:
|
|
254
|
+
# Swallow task cancellation to avoid noisy "exception was never retrieved"
|
|
255
|
+
pass
|
|
256
|
+
|
|
257
|
+
handle.task = self._tasks.create_task(_runner())
|
|
258
|
+
return handle
|
|
259
|
+
|
|
260
|
+
def cancel_all(self) -> None:
|
|
261
|
+
for handle in list(self._handles):
|
|
262
|
+
handle.cancel()
|
|
263
|
+
self._handles.clear()
|
|
264
|
+
|
|
265
|
+
def _schedule(
|
|
266
|
+
self,
|
|
267
|
+
delay: float,
|
|
268
|
+
fn: Callable[..., Any],
|
|
269
|
+
args: tuple[Any, ...],
|
|
270
|
+
kwargs: dict[str, Any],
|
|
271
|
+
*,
|
|
272
|
+
untrack: bool,
|
|
273
|
+
) -> TimerHandleLike:
|
|
274
|
+
"""
|
|
275
|
+
Schedule `fn(*args, **kwargs)` to run after `delay` seconds.
|
|
276
|
+
Works with sync or async functions. Returns a TimerHandle; call .cancel() to cancel.
|
|
277
|
+
|
|
278
|
+
The callback can run without a reactive scope to avoid accidentally capturing
|
|
279
|
+
reactive dependencies from the calling context. Other context vars (like
|
|
280
|
+
PulseContext) are preserved normally.
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
loop = asyncio.get_running_loop()
|
|
284
|
+
except RuntimeError:
|
|
285
|
+
try:
|
|
286
|
+
loop = asyncio.get_event_loop()
|
|
287
|
+
except RuntimeError as exc:
|
|
288
|
+
raise RuntimeError("later() requires an event loop") from exc
|
|
289
|
+
|
|
290
|
+
tracked_box: list[TimerHandleLike] = []
|
|
291
|
+
_run = self._prepare_run(loop, tracked_box, fn, args, kwargs, untrack=untrack)
|
|
292
|
+
|
|
293
|
+
handle = loop.call_later(delay, _run)
|
|
294
|
+
tracked = _TrackedTimerHandle(handle, self)
|
|
295
|
+
tracked_box.append(tracked)
|
|
296
|
+
self._handles.add(tracked)
|
|
297
|
+
return tracked
|
|
298
|
+
|
|
299
|
+
def _schedule_soon(
|
|
300
|
+
self,
|
|
301
|
+
fn: Callable[..., Any],
|
|
302
|
+
args: tuple[Any, ...],
|
|
303
|
+
kwargs: dict[str, Any],
|
|
304
|
+
) -> TimerHandleLike:
|
|
305
|
+
try:
|
|
306
|
+
loop = asyncio.get_running_loop()
|
|
307
|
+
except RuntimeError:
|
|
308
|
+
try:
|
|
309
|
+
loop = asyncio.get_event_loop()
|
|
310
|
+
except RuntimeError as exc:
|
|
311
|
+
raise RuntimeError("call_soon() requires an event loop") from exc
|
|
312
|
+
|
|
313
|
+
tracked_box: list[TimerHandleLike] = []
|
|
314
|
+
_run = self._prepare_run(loop, tracked_box, fn, args, kwargs, untrack=False)
|
|
315
|
+
|
|
316
|
+
handle = loop.call_soon(_run)
|
|
317
|
+
tracked = _TrackedHandle(handle, self, when=loop.time())
|
|
318
|
+
tracked_box.append(tracked)
|
|
319
|
+
self._handles.add(tracked)
|
|
320
|
+
return tracked
|
|
321
|
+
|
|
322
|
+
def _prepare_run(
|
|
323
|
+
self,
|
|
324
|
+
loop: asyncio.AbstractEventLoop,
|
|
325
|
+
tracked_box: list[TimerHandleLike],
|
|
326
|
+
fn: Callable[..., Any],
|
|
327
|
+
args: tuple[Any, ...],
|
|
328
|
+
kwargs: dict[str, Any],
|
|
329
|
+
*,
|
|
330
|
+
untrack: bool,
|
|
331
|
+
) -> Callable[[], None]:
|
|
332
|
+
def _run():
|
|
333
|
+
from pulse.reactive import Untrack
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
if untrack:
|
|
337
|
+
with Untrack():
|
|
338
|
+
res = fn(*args, **kwargs)
|
|
339
|
+
else:
|
|
340
|
+
res = fn(*args, **kwargs)
|
|
341
|
+
if asyncio.iscoroutine(res):
|
|
342
|
+
task = self._tasks.create_task(res)
|
|
343
|
+
|
|
344
|
+
def _log_task_exception(t: asyncio.Task[Any]):
|
|
345
|
+
try:
|
|
346
|
+
t.result()
|
|
347
|
+
except asyncio.CancelledError:
|
|
348
|
+
# Normal cancellation path
|
|
349
|
+
pass
|
|
350
|
+
except Exception as exc:
|
|
351
|
+
loop.call_exception_handler(
|
|
352
|
+
{
|
|
353
|
+
"message": "Unhandled exception in later() task",
|
|
354
|
+
"exception": exc,
|
|
355
|
+
"context": {"callback": fn},
|
|
356
|
+
}
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
task.add_done_callback(_log_task_exception)
|
|
360
|
+
except Exception as exc:
|
|
361
|
+
# Surface exceptions via the loop's exception handler and continue
|
|
362
|
+
loop.call_exception_handler(
|
|
363
|
+
{
|
|
364
|
+
"message": "Unhandled exception in later() callback",
|
|
365
|
+
"exception": exc,
|
|
366
|
+
"context": {"callback": fn},
|
|
367
|
+
}
|
|
368
|
+
)
|
|
369
|
+
finally:
|
|
370
|
+
self.discard(tracked_box[0] if tracked_box else None)
|
|
371
|
+
|
|
372
|
+
return _run
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class _TrackedTimerHandle:
|
|
376
|
+
__slots__: tuple[str, ...] = ("_handle", "_registry")
|
|
377
|
+
_handle: asyncio.TimerHandle
|
|
378
|
+
_registry: "TimerRegistry"
|
|
379
|
+
|
|
380
|
+
def __init__(self, handle: asyncio.TimerHandle, registry: "TimerRegistry") -> None:
|
|
381
|
+
self._handle = handle
|
|
382
|
+
self._registry = registry
|
|
383
|
+
|
|
384
|
+
def cancel(self) -> None:
|
|
385
|
+
if not self._handle.cancelled():
|
|
386
|
+
self._handle.cancel()
|
|
387
|
+
self._registry.discard(self)
|
|
388
|
+
|
|
389
|
+
def cancelled(self) -> bool:
|
|
390
|
+
return self._handle.cancelled()
|
|
391
|
+
|
|
392
|
+
def when(self) -> float:
|
|
393
|
+
return self._handle.when()
|
|
394
|
+
|
|
395
|
+
def __getattr__(self, name: str):
|
|
396
|
+
return getattr(self._handle, name)
|
|
397
|
+
|
|
398
|
+
@override
|
|
399
|
+
def __hash__(self) -> int:
|
|
400
|
+
return hash(self._handle)
|
|
401
|
+
|
|
402
|
+
@override
|
|
403
|
+
def __eq__(self, other: object) -> bool:
|
|
404
|
+
if isinstance(other, _TrackedTimerHandle):
|
|
405
|
+
return self._handle is other._handle
|
|
406
|
+
return self._handle is other
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class _TrackedHandle:
|
|
410
|
+
__slots__: tuple[str, ...] = ("_handle", "_registry", "_when")
|
|
411
|
+
_handle: asyncio.Handle
|
|
412
|
+
_registry: "TimerRegistry"
|
|
413
|
+
_when: float
|
|
414
|
+
|
|
415
|
+
def __init__(
|
|
416
|
+
self,
|
|
417
|
+
handle: asyncio.Handle,
|
|
418
|
+
registry: "TimerRegistry",
|
|
419
|
+
*,
|
|
420
|
+
when: float,
|
|
421
|
+
) -> None:
|
|
422
|
+
self._handle = handle
|
|
423
|
+
self._registry = registry
|
|
424
|
+
self._when = when
|
|
425
|
+
|
|
426
|
+
def cancel(self) -> None:
|
|
427
|
+
if not self._handle.cancelled():
|
|
428
|
+
self._handle.cancel()
|
|
429
|
+
self._registry.discard(self)
|
|
430
|
+
|
|
431
|
+
def cancelled(self) -> bool:
|
|
432
|
+
return self._handle.cancelled()
|
|
433
|
+
|
|
434
|
+
def when(self) -> float:
|
|
435
|
+
return self._when
|
|
436
|
+
|
|
437
|
+
def __getattr__(self, name: str):
|
|
438
|
+
return getattr(self._handle, name)
|
|
439
|
+
|
|
440
|
+
@override
|
|
441
|
+
def __hash__(self) -> int:
|
|
442
|
+
return hash(self._handle)
|
|
443
|
+
|
|
444
|
+
@override
|
|
445
|
+
def __eq__(self, other: object) -> bool:
|
|
446
|
+
if isinstance(other, _TrackedHandle):
|
|
447
|
+
return self._handle is other._handle
|
|
448
|
+
return self._handle is other
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
pulse/__init__.py,sha256=
|
|
1
|
+
pulse/__init__.py,sha256=cXNVXz0aizbkOG1aj2zytgzodyVNv7nNylsXcWmH-Lc,32183
|
|
2
2
|
pulse/_examples.py,sha256=dFuhD2EVXsbvAeexoG57s4VuN4gWLaTMOEMNYvlPm9A,561
|
|
3
|
-
pulse/app.py,sha256=
|
|
4
|
-
pulse/channel.py,sha256=
|
|
3
|
+
pulse/app.py,sha256=oVwe7ioAGl7OXIEiT8J0bj5GBxEkBroOJw6qgdyA7Bg,37343
|
|
4
|
+
pulse/channel.py,sha256=ePpvD2mDbddt_LMxxxDjNRgOLbVi8Ed6TmJFgkrALB0,15790
|
|
5
5
|
pulse/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
pulse/cli/cmd.py,sha256=zh3Ah6c16cNg3o_v_If_S58Qe8rvxNe5M2VrTkwvDU8,15957
|
|
7
7
|
pulse/cli/dependencies.py,sha256=qU-rF7QyP0Rl1Fl0YKQubrGNBzj84BAbH1uUT3ehxik,4283
|
|
@@ -17,7 +17,7 @@ pulse/code_analysis.py,sha256=NBba_7VtOxZYMyfku_p-bWkG0O_1pi1AxcaNyVM1nmY,1024
|
|
|
17
17
|
pulse/codegen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
18
|
pulse/codegen/codegen.py,sha256=Zw55vzevg_17hFtSi6KLl-EWSiABKRfZe6fB-cWpLAk,10330
|
|
19
19
|
pulse/codegen/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
pulse/codegen/templates/layout.py,sha256=
|
|
20
|
+
pulse/codegen/templates/layout.py,sha256=xz2ImZrbpu-tUkHZ38U6npIDzeYhrR_s41BuNPJUnOU,4470
|
|
21
21
|
pulse/codegen/templates/route.py,sha256=UjBrb3e_8tMkd1OjBjEsnYmK6PCQqOYZBWDuU59FcrI,9234
|
|
22
22
|
pulse/codegen/templates/routes_ts.py,sha256=nPgKCvU0gzue2k6KlOL1TJgrBqqRLmyy7K_qKAI8zAE,1129
|
|
23
23
|
pulse/codegen/utils.py,sha256=QoXcV-h-DLLmq_t03hDNUePS0fNnofUQLoR-TXzDFCY,539
|
|
@@ -37,8 +37,8 @@ pulse/dom/svg.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
37
37
|
pulse/dom/tags.py,sha256=U6mKmwB9JAFM6LTESMJcoIejNfnyxIdQo2-TLM5OaZ0,7585
|
|
38
38
|
pulse/dom/tags.pyi,sha256=0BC7zTh22roPBuMQawL8hgI6IrfN8xJZuDIoKMd4QKc,14393
|
|
39
39
|
pulse/env.py,sha256=etfubfwq7VWlo2iKy_972WtHZSiVe4StAnjFga0xgj8,4244
|
|
40
|
-
pulse/
|
|
41
|
-
pulse/helpers.py,sha256=
|
|
40
|
+
pulse/forms.py,sha256=0irpErCMJk8-YO1BrxjMkFb8dnvSz3rfzTywmMeib7g,14042
|
|
41
|
+
pulse/helpers.py,sha256=imVA9XzkYrYmdeqEdD7ot0g99adL1SVKv5bQGkKb-aQ,9504
|
|
42
42
|
pulse/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
43
|
pulse/hooks/core.py,sha256=tDEcB_CTD4yI5bNKn7CtB40sRKIanGNqPD5_qLgSzf4,10982
|
|
44
44
|
pulse/hooks/effects.py,sha256=cEPurXwRQoFmxasI0tZE1cQsZYnZr6VB5mWMUK0Db8c,2604
|
|
@@ -74,25 +74,26 @@ pulse/js/window.py,sha256=yC1BjyH2jqp1x-CXJCUFta-ASyZ5668ozQ0AmAjZcxA,4097
|
|
|
74
74
|
pulse/messages.py,sha256=hz5EUFVHbzXHkcByZcV_Y199vb-M9cGjMMBL1HXPctE,4024
|
|
75
75
|
pulse/middleware.py,sha256=2syzmJ0r9fEa0k1pD7zC_1DHUMs9qLSWRzo5XXtKqsA,10896
|
|
76
76
|
pulse/plugin.py,sha256=bu90qaUVFtZsIsW41dpshVK1vvIGHUsg6mFoiF0Wfso,2370
|
|
77
|
-
pulse/proxy.py,sha256=
|
|
77
|
+
pulse/proxy.py,sha256=tj30GIJJVNKMxMwPNJ39-gn3PAXbx5wua4PiQ7XupvQ,7857
|
|
78
78
|
pulse/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
79
|
pulse/queries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
80
|
-
pulse/queries/client.py,sha256=
|
|
80
|
+
pulse/queries/client.py,sha256=52T4MvorDr-T5UsJlndcEgnrPDs3QxIYP-MZtBSEuvc,18583
|
|
81
81
|
pulse/queries/common.py,sha256=TYhn6LyldfmOKYYurxINgCEr3C3WSEwB0cIki1a5iBM,2488
|
|
82
|
-
pulse/queries/effect.py,sha256=
|
|
83
|
-
pulse/queries/infinite_query.py,sha256=
|
|
82
|
+
pulse/queries/effect.py,sha256=1ePUi2TwP49L9LhlkKI2qV_HhIO4jKj1r5jyPaWiUn8,1508
|
|
83
|
+
pulse/queries/infinite_query.py,sha256=xWmFl5UqW7DXkquR-8EUPqlS6Z-ZF4l7qarEUUXjdN0,49091
|
|
84
84
|
pulse/queries/mutation.py,sha256=fhEpOZ7CuHImH4Y02QapYdTJrwe6K52-keb0d67wmms,8274
|
|
85
85
|
pulse/queries/protocol.py,sha256=TOrUiI4QK55xuh0i4ch1u96apNl12QeYafkf6RVDd08,3544
|
|
86
|
-
pulse/queries/query.py,sha256=
|
|
87
|
-
pulse/queries/store.py,sha256=
|
|
86
|
+
pulse/queries/query.py,sha256=2VlYMeLqHfEokoEtocKjld8zi6Oy-_lieV4baTfF5DU,41332
|
|
87
|
+
pulse/queries/store.py,sha256=4pWTDSl71LUM7YqhWanKjZkFh3t8F_04o48js_H4ttQ,3728
|
|
88
88
|
pulse/react_component.py,sha256=8RLg4Bi7IcjqbnbEnp4hJpy8t1UsE7mG0UR1Q655LDk,2332
|
|
89
|
-
pulse/reactive.py,sha256=
|
|
89
|
+
pulse/reactive.py,sha256=GSh9wSH3THCBjDTafwWttyx7djeKBWV_KqjaKRYUNsA,31393
|
|
90
90
|
pulse/reactive_extensions.py,sha256=yQ1PpdAh4kMvll7R15T72FOg8NFdG_HGBsGc63dawYk,33754
|
|
91
|
-
pulse/render_session.py,sha256=
|
|
91
|
+
pulse/render_session.py,sha256=w6eJcMt4VFu84HeBAmBeEogxPtiwRHnRgQSTabrSXQM,24889
|
|
92
92
|
pulse/renderer.py,sha256=fjSsUvCqV12jyN7Y5XspKUfjQJJzKX-Chha5oF5PrAk,16001
|
|
93
93
|
pulse/request.py,sha256=N0oFOLiGxpbgSgxznjvu64lG3YyOcZPKC8JFyKx6X7w,6023
|
|
94
94
|
pulse/requirements.py,sha256=nMnE25Uu-TUuQd88jW7m2xwus6fD-HvXxQ9UNb7OOGc,1254
|
|
95
95
|
pulse/routing.py,sha256=LzTITvGgaLI1w7qTDZjFwoBcWAb4O8Dz7AmXeTNYrFU,16903
|
|
96
|
+
pulse/scheduling.py,sha256=D-L5mVsbakK_-1NCq62dU6wEJpq6I_HxI4PCmR9aj9w,11714
|
|
96
97
|
pulse/serializer.py,sha256=HmQZgxQiaCx2SL2XwmEQLd_xsk_P8XfLtGciLLLOxx0,7616
|
|
97
98
|
pulse/state.py,sha256=VMphVpYNU1CyHMMg1_kNJO3cfqLXJPAuq9gr9RYyUAw,15922
|
|
98
99
|
pulse/test_helpers.py,sha256=4iO5Ymy3SMvSjh-UaAaSdqm1I_SAJMNjdY2iYVro5f8,436
|
|
@@ -121,7 +122,7 @@ pulse/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
121
122
|
pulse/types/event_handler.py,sha256=psQCydj-WEtBcFU5JU4mDwvyzkW8V2O0g_VFRU2EOHI,1618
|
|
122
123
|
pulse/user_session.py,sha256=nsnsMgqq2xGJZLpbHRMHUHcLrElMP8WcA4gjGMrcoBk,10208
|
|
123
124
|
pulse/version.py,sha256=711vaM1jVIQPgkisGgKZqwmw019qZIsc_QTae75K2pg,1895
|
|
124
|
-
pulse_framework-0.1.
|
|
125
|
-
pulse_framework-0.1.
|
|
126
|
-
pulse_framework-0.1.
|
|
127
|
-
pulse_framework-0.1.
|
|
125
|
+
pulse_framework-0.1.66a1.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
126
|
+
pulse_framework-0.1.66a1.dist-info/entry_points.txt,sha256=i7aohd3QaPu5IcuGKKvsQQEiMYMe5HcF56QEsaLVO64,46
|
|
127
|
+
pulse_framework-0.1.66a1.dist-info/METADATA,sha256=5eOY1sXHWh-_WZB1EUlHxRKoqU3jIAJH0_iqQ2niZhI,8302
|
|
128
|
+
pulse_framework-0.1.66a1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|