pulse-framework 0.1.46__py3-none-any.whl → 0.1.47__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 +9 -23
- pulse/app.py +2 -24
- pulse/codegen/codegen.py +43 -88
- pulse/codegen/js.py +35 -5
- pulse/codegen/templates/route.py +341 -254
- pulse/form.py +1 -1
- pulse/helpers.py +40 -8
- pulse/hooks/core.py +2 -2
- pulse/hooks/effects.py +1 -1
- pulse/hooks/init.py +2 -1
- pulse/hooks/setup.py +1 -1
- pulse/hooks/stable.py +2 -2
- pulse/hooks/states.py +2 -2
- pulse/html/props.py +3 -2
- pulse/html/tags.py +135 -0
- pulse/html/tags.pyi +4 -0
- pulse/js/__init__.py +110 -0
- pulse/js/__init__.pyi +95 -0
- pulse/js/_types.py +297 -0
- pulse/js/array.py +253 -0
- pulse/js/console.py +47 -0
- pulse/js/date.py +113 -0
- pulse/js/document.py +138 -0
- pulse/js/error.py +139 -0
- pulse/js/json.py +62 -0
- pulse/js/map.py +84 -0
- pulse/js/math.py +66 -0
- pulse/js/navigator.py +76 -0
- pulse/js/number.py +54 -0
- pulse/js/object.py +173 -0
- pulse/js/promise.py +150 -0
- pulse/js/regexp.py +54 -0
- pulse/js/set.py +109 -0
- pulse/js/string.py +35 -0
- pulse/js/weakmap.py +50 -0
- pulse/js/weakset.py +45 -0
- pulse/js/window.py +199 -0
- pulse/messages.py +22 -3
- pulse/react_component.py +167 -14
- pulse/reactive_extensions.py +5 -5
- pulse/render_session.py +144 -34
- pulse/renderer.py +80 -115
- pulse/routing.py +1 -18
- pulse/transpiler/__init__.py +131 -0
- pulse/transpiler/builtins.py +731 -0
- pulse/transpiler/constants.py +110 -0
- pulse/transpiler/context.py +26 -0
- pulse/transpiler/errors.py +2 -0
- pulse/transpiler/function.py +250 -0
- pulse/transpiler/ids.py +16 -0
- pulse/transpiler/imports.py +409 -0
- pulse/transpiler/js_module.py +274 -0
- pulse/transpiler/modules/__init__.py +30 -0
- pulse/transpiler/modules/asyncio.py +38 -0
- pulse/transpiler/modules/json.py +20 -0
- pulse/transpiler/modules/math.py +320 -0
- pulse/transpiler/modules/re.py +466 -0
- pulse/transpiler/modules/tags.py +268 -0
- pulse/transpiler/modules/typing.py +59 -0
- pulse/transpiler/nodes.py +1216 -0
- pulse/transpiler/py_module.py +119 -0
- pulse/transpiler/transpiler.py +938 -0
- pulse/transpiler/utils.py +4 -0
- pulse/vdom.py +112 -6
- {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.47.dist-info}/METADATA +1 -1
- pulse_framework-0.1.47.dist-info/RECORD +119 -0
- pulse/codegen/imports.py +0 -204
- pulse/css.py +0 -155
- pulse_framework-0.1.46.dist-info/RECORD +0 -80
- {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.47.dist-info}/WHEEL +0 -0
- {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.47.dist-info}/entry_points.txt +0 -0
pulse/vdom.py
CHANGED
|
@@ -7,6 +7,7 @@ the TypeScript UINode format exactly, eliminating the need for translation.
|
|
|
7
7
|
|
|
8
8
|
import functools
|
|
9
9
|
import math
|
|
10
|
+
import re
|
|
10
11
|
import warnings
|
|
11
12
|
from collections.abc import Callable, Iterable, Sequence
|
|
12
13
|
from inspect import Parameter, signature
|
|
@@ -14,11 +15,13 @@ from types import NoneType
|
|
|
14
15
|
from typing import (
|
|
15
16
|
Any,
|
|
16
17
|
Generic,
|
|
18
|
+
Literal,
|
|
17
19
|
NamedTuple,
|
|
18
20
|
NotRequired,
|
|
19
21
|
ParamSpec,
|
|
20
22
|
TypeAlias,
|
|
21
23
|
TypedDict,
|
|
24
|
+
final,
|
|
22
25
|
overload,
|
|
23
26
|
override,
|
|
24
27
|
)
|
|
@@ -94,11 +97,16 @@ class Callback(NamedTuple):
|
|
|
94
97
|
n_args: int
|
|
95
98
|
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
return None
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
@final
|
|
101
101
|
class Node:
|
|
102
|
+
__slots__ = (
|
|
103
|
+
"tag",
|
|
104
|
+
"props",
|
|
105
|
+
"children",
|
|
106
|
+
"allow_children",
|
|
107
|
+
"key",
|
|
108
|
+
)
|
|
109
|
+
|
|
102
110
|
tag: str
|
|
103
111
|
props: dict[str, Any] | None
|
|
104
112
|
children: "Sequence[Element] | None"
|
|
@@ -284,7 +292,19 @@ class Component(Generic[P]):
|
|
|
284
292
|
return self.name
|
|
285
293
|
|
|
286
294
|
|
|
295
|
+
@final
|
|
287
296
|
class ComponentNode:
|
|
297
|
+
__slots__ = (
|
|
298
|
+
"fn",
|
|
299
|
+
"args",
|
|
300
|
+
"kwargs",
|
|
301
|
+
"key",
|
|
302
|
+
"name",
|
|
303
|
+
"takes_children",
|
|
304
|
+
"hooks",
|
|
305
|
+
"contents",
|
|
306
|
+
)
|
|
307
|
+
|
|
288
308
|
fn: Callable[..., Any]
|
|
289
309
|
args: tuple[Any, ...]
|
|
290
310
|
kwargs: dict[str, Any]
|
|
@@ -292,6 +312,7 @@ class ComponentNode:
|
|
|
292
312
|
name: str
|
|
293
313
|
takes_children: bool
|
|
294
314
|
hooks: HookContext
|
|
315
|
+
contents: "Element | None"
|
|
295
316
|
|
|
296
317
|
def __init__(
|
|
297
318
|
self,
|
|
@@ -309,7 +330,7 @@ class ComponentNode:
|
|
|
309
330
|
self.name = name or _infer_component_name(fn)
|
|
310
331
|
self.takes_children = takes_children
|
|
311
332
|
# Used for rendering
|
|
312
|
-
self.contents
|
|
333
|
+
self.contents = None
|
|
313
334
|
self.hooks = HookContext()
|
|
314
335
|
# Dev-only validation for JSON-unsafe values
|
|
315
336
|
if env.pulse_env == "dev":
|
|
@@ -388,11 +409,95 @@ Callbacks = dict[str, Callback]
|
|
|
388
409
|
VDOM: TypeAlias = VDOMNode | Primitive
|
|
389
410
|
Props = dict[str, Any]
|
|
390
411
|
|
|
412
|
+
|
|
413
|
+
# ============================================================================
|
|
414
|
+
# VDOM Operations (updates sent from server to client)
|
|
415
|
+
# ============================================================================
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
class ReplaceOperation(TypedDict):
|
|
419
|
+
type: Literal["replace"]
|
|
420
|
+
path: str
|
|
421
|
+
data: VDOM
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
# This payload makes it easy for the client to rebuild an array of React nodes
|
|
425
|
+
# from the previous children array:
|
|
426
|
+
# - Allocate array of size N
|
|
427
|
+
# - For i in 0..N-1, check the following scenarios
|
|
428
|
+
# - i matches the next index in `new` -> use provided tree
|
|
429
|
+
# - i matches the next index in `reuse` -> reuse previous child
|
|
430
|
+
# - otherwise, reuse the element at the same index
|
|
431
|
+
class ReconciliationOperation(TypedDict):
|
|
432
|
+
type: Literal["reconciliation"]
|
|
433
|
+
path: str
|
|
434
|
+
N: int
|
|
435
|
+
new: tuple[list[int], list[VDOM]]
|
|
436
|
+
reuse: tuple[list[int], list[int]]
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
class UpdatePropsDelta(TypedDict, total=False):
|
|
440
|
+
# Only send changed/new keys under `set` and removed keys under `remove`
|
|
441
|
+
set: Props
|
|
442
|
+
remove: list[str]
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
class UpdatePropsOperation(TypedDict):
|
|
446
|
+
type: Literal["update_props"]
|
|
447
|
+
path: str
|
|
448
|
+
data: UpdatePropsDelta
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class PathDelta(TypedDict, total=False):
|
|
452
|
+
add: list[str]
|
|
453
|
+
remove: list[str]
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class UpdateCallbacksOperation(TypedDict):
|
|
457
|
+
type: Literal["update_callbacks"]
|
|
458
|
+
path: str
|
|
459
|
+
data: PathDelta
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
class UpdateRenderPropsOperation(TypedDict):
|
|
463
|
+
type: Literal["update_render_props"]
|
|
464
|
+
path: str
|
|
465
|
+
data: PathDelta
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
class UpdateJsExprPathsOperation(TypedDict):
|
|
469
|
+
type: Literal["update_jsexpr_paths"]
|
|
470
|
+
path: str
|
|
471
|
+
data: PathDelta
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
VDOMOperation: TypeAlias = (
|
|
475
|
+
ReplaceOperation
|
|
476
|
+
| UpdatePropsOperation
|
|
477
|
+
| ReconciliationOperation
|
|
478
|
+
| UpdateCallbacksOperation
|
|
479
|
+
| UpdateRenderPropsOperation
|
|
480
|
+
| UpdateJsExprPathsOperation
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
|
|
391
484
|
# ----------------------------------------------------------------------------
|
|
392
485
|
# Component naming heuristics
|
|
393
486
|
# ----------------------------------------------------------------------------
|
|
394
487
|
|
|
395
488
|
|
|
489
|
+
def _clean_parent_name_for_warning(parent_name: str) -> str:
|
|
490
|
+
"""Strip $$ prefix and hexadecimal suffix from ReactComponent tags in warning messages.
|
|
491
|
+
|
|
492
|
+
ReactComponent tags are in the format <$$ComponentName_1a2b> or <$$ComponentName_1a2b.prop>.
|
|
493
|
+
This function strips the $$ prefix and _1a2b suffix to show just the component name.
|
|
494
|
+
"""
|
|
495
|
+
|
|
496
|
+
# Match ReactComponent tags: <$$ComponentName_hex> or <$$ComponentName_hex.prop>
|
|
497
|
+
# Strip the $$ prefix and _hex suffix but keep the rest (hex digits are 0-9, a-f)
|
|
498
|
+
return re.sub(r"\$\$([^_]+)_[0-9a-f]+", r"\1", parent_name)
|
|
499
|
+
|
|
500
|
+
|
|
396
501
|
def _flatten_children(
|
|
397
502
|
children: Children, *, parent_name: str, warn_stacklevel: int = 5
|
|
398
503
|
) -> Sequence[Element]:
|
|
@@ -424,9 +529,10 @@ def _flatten_children(
|
|
|
424
529
|
visit(sub)
|
|
425
530
|
if missing_key:
|
|
426
531
|
# Warn once per iterable without keys on its elements.
|
|
532
|
+
clean_name = _clean_parent_name_for_warning(parent_name)
|
|
427
533
|
warnings.warn(
|
|
428
534
|
(
|
|
429
|
-
f"[Pulse] Iterable children of {
|
|
535
|
+
f"[Pulse] Iterable children of {clean_name} contain elements without 'key'. "
|
|
430
536
|
"Add a stable 'key' to each element inside iterables to improve reconciliation."
|
|
431
537
|
),
|
|
432
538
|
stacklevel=warn_stacklevel,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
pulse/__init__.py,sha256=F97Jf6i99NXZe05ICjGbBjIaT4JURdh7dC1T1-Gnb0o,32790
|
|
2
|
+
pulse/app.py,sha256=57o_FrJjKxMDkbxKTzeCUtBRXHfGrCbSpJUnP0Z8evg,31474
|
|
3
|
+
pulse/channel.py,sha256=d9eLxgyB0P9UBVkPkXV7MHkC4LWED1Cq3GKsEu_SYy4,13056
|
|
4
|
+
pulse/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
pulse/cli/cmd.py,sha256=UBT7OoqWRU-idLOKkA9TDN8m8ugi1gwRMiUJTUmkVfU,14853
|
|
6
|
+
pulse/cli/dependencies.py,sha256=ZBqBAfMvMBQUvh4THdPDztTMQ_dyR52S1IuotP_eEZs,5623
|
|
7
|
+
pulse/cli/folder_lock.py,sha256=kvUmZBg869lwCTIZFoge9dhorv8qPXHTWwVv_jQg1k8,3477
|
|
8
|
+
pulse/cli/helpers.py,sha256=8bRlV3d7w3w-jHaFvFYt9Pzue6_CbKOq_Z3jBsBOeUk,8820
|
|
9
|
+
pulse/cli/models.py,sha256=NBV5byBDNoAQSk0vKwibLjoxuA85XBYIyOVJn64L8oU,858
|
|
10
|
+
pulse/cli/packages.py,sha256=e7ycwwJfdmB4pzrai4DHos6-JzyUgmE4DCZp0BqjdeI,6792
|
|
11
|
+
pulse/cli/processes.py,sha256=C1xU72oUanj-1Mkc9WmqESTsVUn_aUHG8URiPyRHSFM,7016
|
|
12
|
+
pulse/cli/secrets.py,sha256=dNfQe6AzSYhZuWveesjCRHIbvaPd3-F9lEJ-kZA7ROw,921
|
|
13
|
+
pulse/cli/uvicorn_log_config.py,sha256=f7ikDc5foXh3TmFMrnfnW8yev48ZAdlo8F4F_aMVoVk,2391
|
|
14
|
+
pulse/codegen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
pulse/codegen/codegen.py,sha256=hK2gh2hX8ARMdQiQ3ILj6NMOuNiCbcuTdgNrtt4ex-8,9525
|
|
16
|
+
pulse/codegen/js.py,sha256=yw2RKQhiSBZr_FL-3WpZoAhcYCvuPEZXTzP5p9i7mCY,1540
|
|
17
|
+
pulse/codegen/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
pulse/codegen/templates/layout.py,sha256=nmWPQcO9SRXc3mCCVLCmykreSF96TqQfdDY7dvUBxRg,4737
|
|
19
|
+
pulse/codegen/templates/route.py,sha256=ionwEoTq0aAIE3lmnFyHLAlFnpKOT0QZm_XhTL_ZDNQ,10447
|
|
20
|
+
pulse/codegen/templates/routes_ts.py,sha256=nPgKCvU0gzue2k6KlOL1TJgrBqqRLmyy7K_qKAI8zAE,1129
|
|
21
|
+
pulse/codegen/utils.py,sha256=QoXcV-h-DLLmq_t03hDNUePS0fNnofUQLoR-TXzDFCY,539
|
|
22
|
+
pulse/components/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
pulse/components/for_.py,sha256=LUyJEUlDM6b9oPjvUFgSsddxu6b6usF4BQdXe8FIiGI,1302
|
|
24
|
+
pulse/components/if_.py,sha256=rQywsmdirNpkb-61ZEdF-tgzUh-37JWd4YFGblkzIdQ,1624
|
|
25
|
+
pulse/components/react_router.py,sha256=TbRec-NVliUqrvAMeFXCrnDWV1rh6TGTPfRhqLuLubk,1129
|
|
26
|
+
pulse/context.py,sha256=fMK6GdQY4q_3452v5DJli2f2_urVihnpzb-O-O9cJ1Q,1734
|
|
27
|
+
pulse/cookies.py,sha256=c7ua1Lv6mNe1nYnA4SFVvewvRQAbYy9fN5G3Hr_Dr5c,5000
|
|
28
|
+
pulse/decorators.py,sha256=ywNgLN6VFcKOM5fbFdUUzh-DWk4BuSXdD1BTfd1N-0U,4827
|
|
29
|
+
pulse/env.py,sha256=p3XI8KG1ZCcXPD3LJP7fW8JPYfyvoYY5ENwae2o0PiA,2889
|
|
30
|
+
pulse/form.py,sha256=UHVyp9fIZaM-Bi9_he8FBT8A2tI7bnRCn1dJkOat0zw,9022
|
|
31
|
+
pulse/helpers.py,sha256=sAIA_X2p6-AEg1Y87NejCZvV82_FtNpePX1Z0sFsdh8,15005
|
|
32
|
+
pulse/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
+
pulse/hooks/core.py,sha256=QfYRz2O8-drNSQx_xnv8mK8ksWcw3LNM1H2hoInT0Rk,7457
|
|
34
|
+
pulse/hooks/effects.py,sha256=pVq5OndlhFLHLpM9Pn9Bp5rEpnpmJEpbIp2UaHHyJFQ,2428
|
|
35
|
+
pulse/hooks/init.py,sha256=iTNmEcFgZCXsLImPONbSNwc5asT7NQRz04b1Jopgzxs,11960
|
|
36
|
+
pulse/hooks/runtime.py,sha256=k5LZ8hnlNBMKOiEkQcAvs8BKwYxV6gwea2WCfju5K7Y,5106
|
|
37
|
+
pulse/hooks/setup.py,sha256=c_uVi0S0HPioEvjdWUaSdAGT9M3Cxpw8J-llvtmDOGo,4496
|
|
38
|
+
pulse/hooks/stable.py,sha256=mLNS6WyA4tC-65gNybPOE0DLEz1YlxOCddD9odElArU,1772
|
|
39
|
+
pulse/hooks/states.py,sha256=fFqN3gf7v7rY6QmieKWN1hVCQRRnL-5H4TeG9LTnKSc,6778
|
|
40
|
+
pulse/html/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
+
pulse/html/elements.py,sha256=YHXkVpfMAC4-0o61fK-E0LGTOM3KMCtBfpHHAwLx7dw,23241
|
|
42
|
+
pulse/html/events.py,sha256=SiZxaQV340hc5YGoKWXC5uCmbLsuijuEgnQz1hmdqYg,14700
|
|
43
|
+
pulse/html/props.py,sha256=FMXGHcO6GhTYp2Uw_6LpjDH5b-fVR88to3YlLvysjAM,26731
|
|
44
|
+
pulse/html/svg.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
+
pulse/html/tags.py,sha256=NEH1otY0mURAl8STVunWzBC3fnmiwGqxcPucBPmtVzU,7568
|
|
46
|
+
pulse/html/tags.pyi,sha256=y0zHmPDmZbkPUbO8YJ8Yaw47NicoorOU-xjCTWWB_NM,14094
|
|
47
|
+
pulse/js/__init__.py,sha256=nI_FQpUlLUyCTmpj5cIbpNXjN7Oqwmp5je13ixB9pWw,3104
|
|
48
|
+
pulse/js/__init__.pyi,sha256=Xrub3LEm4JjyNTIyehm-Bfyy2gmpJgvFsEoRfpmq7bg,2624
|
|
49
|
+
pulse/js/_types.py,sha256=zkXtH1B7qURL24VFBPbyH3VIO1H2ptPaU2iE9ZHLDkQ,7402
|
|
50
|
+
pulse/js/array.py,sha256=WC7Z_A_dsf0_uyRTjuWqkTftBWXnNDdHzZJ4pvCqjsE,7102
|
|
51
|
+
pulse/js/console.py,sha256=rAbKq9uMYmsAsQ4j2crYpIoFkrfGHPWdPyU62zHQyWc,1818
|
|
52
|
+
pulse/js/date.py,sha256=lqPpZMhOR5bLxqEe9vyCWBKv6PPKfN_Ikwc-WAmu_NQ,3392
|
|
53
|
+
pulse/js/document.py,sha256=nS8C6e5rMMH8dKvwfO6WkfQElqrBxK-EKhl5JUUCzSA,3015
|
|
54
|
+
pulse/js/error.py,sha256=8zQPumWEssBK_gIcFOQu_TZGPNwcH5N6Yd6C0COElxM,2763
|
|
55
|
+
pulse/js/json.py,sha256=mPvJ0meHNuHrjXOJ7PfC_g1kOldAk_w7xYKA2dhwSUs,1945
|
|
56
|
+
pulse/js/map.py,sha256=Q8EAbkrlg1P30nKrx0MoLsmMS25Y860-lnoPzyZ0yLQ,2056
|
|
57
|
+
pulse/js/math.py,sha256=EwVnFN4MfHlY-QFz4_35BIsLEjfOyisTqoOfYHnp-mM,1801
|
|
58
|
+
pulse/js/navigator.py,sha256=viUQGCO18R_UvWhrdWj1x1IzDd6ZZYcb_m0bI5n8fms,1539
|
|
59
|
+
pulse/js/number.py,sha256=d64aZ8VEJSDN8vMzLvL0LnotRhG0bLrMg1IoLf23na8,1321
|
|
60
|
+
pulse/js/object.py,sha256=l5OzfKS_kiUnrzRk4HAhjOOVUAJ8q-VWZv-4pADDtjc,4808
|
|
61
|
+
pulse/js/promise.py,sha256=OnENcz7k0V6oGRtIxp-4LbGdurqd4aFMrFfWibm26Y0,4438
|
|
62
|
+
pulse/js/regexp.py,sha256=vHI8xI2QOT-dDHI6Held4YdwvfGs4i0_3Ce6gMyB7L0,1123
|
|
63
|
+
pulse/js/set.py,sha256=trMVxWfAhV07GKC8wn5Gqm31rk1SQtrJrNzSdDyfqi0,2830
|
|
64
|
+
pulse/js/string.py,sha256=fBd_CKq5nhc300mRa3YgNw0jpTEgGyaXRmGBiJgeu5w,928
|
|
65
|
+
pulse/js/weakmap.py,sha256=Q7kgPQx6rFqYfhIDyRfhuC12JmlKmO2n-OGSpl3g9ZY,1473
|
|
66
|
+
pulse/js/weakset.py,sha256=FJoVR0WtaOaHL7AXzJOb29F_sqG1K2mWxvR0RJk3mS0,1333
|
|
67
|
+
pulse/js/window.py,sha256=ayx3lBl54hTVanlkiC2wCVGNh0IDJqzPO7OlO11YUtI,4081
|
|
68
|
+
pulse/messages.py,sha256=PDsb07QDKvkMitAMgLmOk2c4JDb58Cq9WWCwbQ8unvg,3979
|
|
69
|
+
pulse/middleware.py,sha256=9uyAhVUEGMSwqWC3WXqs7x5JMMNEcSTTu3g7DjsR8w8,9812
|
|
70
|
+
pulse/plugin.py,sha256=RfGl6Vtr7VRHb8bp4Ob4dOX9dVzvc4Riu7HWnStMPpk,580
|
|
71
|
+
pulse/proxy.py,sha256=zh4v5lmYNg5IBE_xdHHmGPwbMQNSXb2npeLXvw_O1Oc,6591
|
|
72
|
+
pulse/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
|
+
pulse/queries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
74
|
+
pulse/queries/client.py,sha256=GGckE0P3YCBO4Mj-08AO_I9eXVC4sIDSNw_xTLrBFuE,15224
|
|
75
|
+
pulse/queries/common.py,sha256=Cr_NV0dWz5DQ7Qg771jvUms1o2-EnTYqjZJe4tVeoVk,1160
|
|
76
|
+
pulse/queries/effect.py,sha256=7KvV_yK7OHTWhfQbZFGzg_pRhyI2mn25pKIF9AmSmcU,1471
|
|
77
|
+
pulse/queries/infinite_query.py,sha256=oUHWjP2OliB7h8VDJooGocefHm4m9TDy4WaJesSrsdI,40457
|
|
78
|
+
pulse/queries/mutation.py,sha256=px1fprFL-RxNfbRSoRtdsOLkEbjSsMrJxGHKBIPYQTM,4959
|
|
79
|
+
pulse/queries/protocol.py,sha256=R8n238Ex9DbYIAVKB83a8FAPtnCiPNhWar-F01K2fTo,3345
|
|
80
|
+
pulse/queries/query.py,sha256=G8eXCaT5wuvVcstlqWU8VBxuuUUS7K1R5Y-VtDpMIG0,35065
|
|
81
|
+
pulse/queries/store.py,sha256=Ct7a-h1-Cq07zEfe9vw-LM85Fm7jIJx7CLAIlsiznlU,3444
|
|
82
|
+
pulse/react_component.py,sha256=m2WJwrCvzaHDC_o4PRvZ3pD3nwy9QshxtVn10vMRDQg,30603
|
|
83
|
+
pulse/reactive.py,sha256=v8a9IttkabeWwYrrHAx33zqzW9WC4WlS4iXbIh2KQkU,24374
|
|
84
|
+
pulse/reactive_extensions.py,sha256=T1V3AasHtvJkmGO55miC9RVPxDFIj7qrooMsn89x5SI,32076
|
|
85
|
+
pulse/render_session.py,sha256=UAI65k28ysZqCp7j6BBdi7MkG2wVKJXj4hIgjK7hsaM,18120
|
|
86
|
+
pulse/renderer.py,sha256=kUwrI5v9XMerVCjBmnYZSyfkDu8B8cW8jFjVnM93TS4,15702
|
|
87
|
+
pulse/request.py,sha256=sPsSRWi5KvReSPBLIs_kzqomn1wlRk1BTLZ5s0chQr4,4979
|
|
88
|
+
pulse/routing.py,sha256=XZdq4gjfYeuz1wKtjPza6YA8ya76_cQ58b2l4dBDbr4,13243
|
|
89
|
+
pulse/serializer.py,sha256=8RAITNoSNm5-U38elHpWmkBpcM_rxZFMCluJSfldfk4,5420
|
|
90
|
+
pulse/state.py,sha256=ikQbK4R8PieV96qd4uWREUvs0jXo9sCapawY7i6oCYo,10776
|
|
91
|
+
pulse/transpiler/__init__.py,sha256=sgfHxLwZEPj3rBBMtzD4997qx7GTr5Wt22-296e-uC8,6492
|
|
92
|
+
pulse/transpiler/builtins.py,sha256=V_H3bpgU22Yb_GzM6YvOutZ65O36xHLzRANl7uHRqUI,22401
|
|
93
|
+
pulse/transpiler/constants.py,sha256=GBYfTGgzDCuy-U5wC6iRYezSPK5UpZuyXrEp_yO6yTM,3188
|
|
94
|
+
pulse/transpiler/context.py,sha256=e-Nh0AKsq9_wVOI8gL_gn-UAP6HzcYN14zWLfNNzjWw,714
|
|
95
|
+
pulse/transpiler/errors.py,sha256=JC6tTEmnHf6JdyW4GIvfXB0IBLe7p3FvCLh14PocH28,43
|
|
96
|
+
pulse/transpiler/function.py,sha256=rP-MZl15_mwaGwPToPcFVYHXqyVdMn91cxtF7MKFPHA,7526
|
|
97
|
+
pulse/transpiler/ids.py,sha256=d91B_LFaAALKXHjGPmL8tJmDqGDFz7-GquYmnV9IZ0o,327
|
|
98
|
+
pulse/transpiler/imports.py,sha256=C4lSi5cRQcoo559_urDqbHAdeTiSeU3eEh89o2YDWhI,10848
|
|
99
|
+
pulse/transpiler/js_module.py,sha256=AjguCbKV_FnoanzojrW5-vlW-DQCTtyd-4FfBYQLxxQ,9603
|
|
100
|
+
pulse/transpiler/modules/__init__.py,sha256=YPM2WhILHXQFDSxDcbs-hHdhFh6i3N5ZJLEVGDZeR3Y,1065
|
|
101
|
+
pulse/transpiler/modules/asyncio.py,sha256=FetybIKGJhVJ3uEQJBw6Z2fsh6g7vA-9tqec4nn5FtI,1409
|
|
102
|
+
pulse/transpiler/modules/json.py,sha256=rlOPiJTpUiB2BGEpLqlP9gqO6jegpBd6vigE_h0iIfw,554
|
|
103
|
+
pulse/transpiler/modules/math.py,sha256=9_h6GGJA1SMIBKIB1AwoHErT-CqgSpyEWWneIcxRofE,9415
|
|
104
|
+
pulse/transpiler/modules/re.py,sha256=lAVy-0ej6PEbxNJ4Z0f9N6f9Fol3IsxPKoqBxh4Il-A,13115
|
|
105
|
+
pulse/transpiler/modules/tags.py,sha256=O3K9bLppCzYNlkXjeTvoQN7OI_HfW3xGy55CBT2Qn84,9079
|
|
106
|
+
pulse/transpiler/modules/typing.py,sha256=Vtiv33JzCe7_2K6IBpGdugI4f0sHJUq5I9qI_-sW80M,1639
|
|
107
|
+
pulse/transpiler/nodes.py,sha256=QAzIiu6R9nmBj-sYhiZf4-ffzdX8n_8Z7GswB7qXe5Y,33079
|
|
108
|
+
pulse/transpiler/py_module.py,sha256=bdSL_tUjOeYorWkOVPW4bx5tuFxYbWcGvuYQ5r8WnWk,3859
|
|
109
|
+
pulse/transpiler/transpiler.py,sha256=xwcJc5px1EShgXpCsqJyIynez2GdKynWy6zn2WaRZJA,29292
|
|
110
|
+
pulse/transpiler/utils.py,sha256=W5PAOWvmYdJCEw1eY7QEJRQFmNLVjFTdlCyWzmnTrCc,94
|
|
111
|
+
pulse/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
112
|
+
pulse/types/event_handler.py,sha256=psQCydj-WEtBcFU5JU4mDwvyzkW8V2O0g_VFRU2EOHI,1618
|
|
113
|
+
pulse/user_session.py,sha256=FITxLSEl3JU-jod6UWuUYC6EpnPG2rbaLCnIOdkQPtg,7803
|
|
114
|
+
pulse/vdom.py,sha256=yHMOYiXcaP3IVnYOAZrd_7cXWULKQ2fRQAiPDPSsOyU,18494
|
|
115
|
+
pulse/version.py,sha256=711vaM1jVIQPgkisGgKZqwmw019qZIsc_QTae75K2pg,1895
|
|
116
|
+
pulse_framework-0.1.47.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
117
|
+
pulse_framework-0.1.47.dist-info/entry_points.txt,sha256=i7aohd3QaPu5IcuGKKvsQQEiMYMe5HcF56QEsaLVO64,46
|
|
118
|
+
pulse_framework-0.1.47.dist-info/METADATA,sha256=AMFl4QOZ07Oxunrd6GrbkKyUiFW6C04HOQuixmt_hcE,580
|
|
119
|
+
pulse_framework-0.1.47.dist-info/RECORD,,
|
pulse/codegen/imports.py
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
from collections.abc import Iterable
|
|
2
|
-
from dataclasses import dataclass, field
|
|
3
|
-
from operator import concat
|
|
4
|
-
|
|
5
|
-
from pulse.codegen.utils import NameRegistry
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Imported:
|
|
9
|
-
name: str
|
|
10
|
-
src: str
|
|
11
|
-
is_default: bool
|
|
12
|
-
prop: str | None
|
|
13
|
-
alias: str | None
|
|
14
|
-
|
|
15
|
-
def __init__(
|
|
16
|
-
self,
|
|
17
|
-
name: str,
|
|
18
|
-
src: str,
|
|
19
|
-
is_default: bool = False,
|
|
20
|
-
prop: str | None = None,
|
|
21
|
-
alias: str | None = None,
|
|
22
|
-
) -> None:
|
|
23
|
-
self.name = name
|
|
24
|
-
self.src = src
|
|
25
|
-
self.is_default = is_default
|
|
26
|
-
self.prop = prop
|
|
27
|
-
self.alias = alias
|
|
28
|
-
|
|
29
|
-
@property
|
|
30
|
-
def expr(self):
|
|
31
|
-
if self.prop:
|
|
32
|
-
return f"{self.alias or self.name}.{self.prop}"
|
|
33
|
-
return self.alias or self.name
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@dataclass
|
|
37
|
-
class ImportMember:
|
|
38
|
-
name: str
|
|
39
|
-
alias: str | None = None
|
|
40
|
-
|
|
41
|
-
@property
|
|
42
|
-
def identifier(self):
|
|
43
|
-
return self.alias or self.name
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@dataclass
|
|
47
|
-
class ImportStatement:
|
|
48
|
-
src: str
|
|
49
|
-
values: list[ImportMember] = field(default_factory=list)
|
|
50
|
-
types: list[ImportMember] = field(default_factory=list)
|
|
51
|
-
default_import: str | None = None
|
|
52
|
-
# When True, emit a side-effect import: `import "<src>";`
|
|
53
|
-
# Can be combined with named/default imports; side-effect line is emitted
|
|
54
|
-
# only when there are no named/default/type imports for the source.
|
|
55
|
-
side_effect: bool = False
|
|
56
|
-
# Optional ordering constraint: ensure this statement is emitted before
|
|
57
|
-
# any import statements whose `src` matches one of these values.
|
|
58
|
-
# Example: ImportStatement(src="@mantine/core/styles.css", side_effect=True,
|
|
59
|
-
# before=["@mantine/dates/styles.css"]) ensures
|
|
60
|
-
# core styles are imported before dates styles.
|
|
61
|
-
before: list[str] = field(default_factory=list)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
class Imports:
|
|
65
|
-
names: NameRegistry
|
|
66
|
-
|
|
67
|
-
def __init__(
|
|
68
|
-
self,
|
|
69
|
-
imports: Iterable[ImportStatement | Imported],
|
|
70
|
-
names: NameRegistry | None = None,
|
|
71
|
-
) -> None:
|
|
72
|
-
self.names = names or NameRegistry()
|
|
73
|
-
# Map (src, name) -> identifier (either name or alias)
|
|
74
|
-
self._import_map: dict[tuple[str, str], str] = {}
|
|
75
|
-
self.sources: dict[str, ImportStatement] = {}
|
|
76
|
-
for stmt in imports:
|
|
77
|
-
if not isinstance(stmt, ImportStatement):
|
|
78
|
-
continue
|
|
79
|
-
|
|
80
|
-
if stmt.default_import:
|
|
81
|
-
stmt.default_import = self.names.register(stmt.default_import)
|
|
82
|
-
|
|
83
|
-
for imp in concat(stmt.values, stmt.types):
|
|
84
|
-
name = self.names.register(imp.name)
|
|
85
|
-
if name != imp.name:
|
|
86
|
-
imp.alias = name
|
|
87
|
-
self._import_map[(stmt.src, imp.name)] = name
|
|
88
|
-
|
|
89
|
-
self.sources[stmt.src] = stmt
|
|
90
|
-
|
|
91
|
-
def import_(
|
|
92
|
-
self, src: str, name: str, is_type: bool = False, is_default: bool = False
|
|
93
|
-
) -> str:
|
|
94
|
-
stmt = self.sources.get(src)
|
|
95
|
-
if not stmt:
|
|
96
|
-
stmt = ImportStatement(src)
|
|
97
|
-
self.sources[src] = stmt
|
|
98
|
-
|
|
99
|
-
if is_default:
|
|
100
|
-
if stmt.default_import:
|
|
101
|
-
return stmt.default_import
|
|
102
|
-
stmt.default_import = self.names.register(name)
|
|
103
|
-
return stmt.default_import
|
|
104
|
-
|
|
105
|
-
else:
|
|
106
|
-
if (src, name) in self._import_map:
|
|
107
|
-
return self._import_map[(src, name)]
|
|
108
|
-
|
|
109
|
-
unique_name = self.names.register(name)
|
|
110
|
-
alias = unique_name if unique_name != name else None
|
|
111
|
-
imp = ImportMember(name, alias)
|
|
112
|
-
if is_type:
|
|
113
|
-
stmt.types.append(imp)
|
|
114
|
-
else:
|
|
115
|
-
stmt.values.append(imp)
|
|
116
|
-
# Remember mapping so future imports of the same (src, name) reuse identifier
|
|
117
|
-
self._import_map[(src, name)] = imp.identifier
|
|
118
|
-
return imp.identifier
|
|
119
|
-
|
|
120
|
-
def add_statement(self, stmt: ImportStatement) -> None:
|
|
121
|
-
"""Merge an ImportStatement into the current Imports registry.
|
|
122
|
-
|
|
123
|
-
Ensures consistent aliasing via NameRegistry and de-duplicates
|
|
124
|
-
previously imported names from the same source.
|
|
125
|
-
"""
|
|
126
|
-
existing = self.sources.get(stmt.src)
|
|
127
|
-
if not existing:
|
|
128
|
-
# Normalize names through registry to avoid later conflicts
|
|
129
|
-
if stmt.default_import:
|
|
130
|
-
stmt.default_import = self.names.register(stmt.default_import)
|
|
131
|
-
for imp in concat(stmt.values, stmt.types):
|
|
132
|
-
name = self.names.register(imp.name)
|
|
133
|
-
if name != imp.name:
|
|
134
|
-
imp.alias = name
|
|
135
|
-
self._import_map[(stmt.src, imp.name)] = name
|
|
136
|
-
self.sources[stmt.src] = stmt
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
# Merge into existing statement for the same src
|
|
140
|
-
if stmt.default_import and not existing.default_import:
|
|
141
|
-
existing.default_import = self.names.register(stmt.default_import)
|
|
142
|
-
|
|
143
|
-
# Merge named imports
|
|
144
|
-
def _merge_list(
|
|
145
|
-
dst: list[ImportMember], src_list: list[ImportMember], is_type: bool = False
|
|
146
|
-
):
|
|
147
|
-
for imp in src_list:
|
|
148
|
-
key = (stmt.src, imp.name)
|
|
149
|
-
if key in self._import_map:
|
|
150
|
-
continue
|
|
151
|
-
unique = self.names.register(imp.name)
|
|
152
|
-
if unique != imp.name:
|
|
153
|
-
imp.alias = unique
|
|
154
|
-
self._import_map[key] = imp.alias or imp.name
|
|
155
|
-
dst.append(imp)
|
|
156
|
-
|
|
157
|
-
_merge_list(existing.values, stmt.values, is_type=False)
|
|
158
|
-
_merge_list(existing.types, stmt.types, is_type=True)
|
|
159
|
-
existing.side_effect = existing.side_effect or stmt.side_effect
|
|
160
|
-
# Merge ordering constraints
|
|
161
|
-
if stmt.before:
|
|
162
|
-
# Preserve order, avoid duplicates
|
|
163
|
-
seen = set(existing.before)
|
|
164
|
-
for s in stmt.before:
|
|
165
|
-
if s not in seen:
|
|
166
|
-
existing.before.append(s)
|
|
167
|
-
seen.add(s)
|
|
168
|
-
|
|
169
|
-
def ordered_sources(self) -> list[ImportStatement]:
|
|
170
|
-
"""Return sources ordered to satisfy `before` constraints.
|
|
171
|
-
|
|
172
|
-
Uses a stable topological sort (Kahn's algorithm) where insertion order
|
|
173
|
-
is preserved among nodes with equal dependency rank. Falls back to
|
|
174
|
-
insertion order if cycles are detected.
|
|
175
|
-
"""
|
|
176
|
-
# Build graph: edge u->v means u must come before v
|
|
177
|
-
keys = list(self.sources.keys())
|
|
178
|
-
index = {k: i for i, k in enumerate(keys)} # for stability
|
|
179
|
-
indegree: dict[str, int] = {k: 0 for k in keys}
|
|
180
|
-
adj: dict[str, list[str]] = {k: [] for k in keys}
|
|
181
|
-
for u, stmt in self.sources.items():
|
|
182
|
-
for v in stmt.before:
|
|
183
|
-
if v in adj: # only consider edges to imports present
|
|
184
|
-
adj[u].append(v)
|
|
185
|
-
indegree[v] += 1
|
|
186
|
-
|
|
187
|
-
# Kahn's algorithm
|
|
188
|
-
queue = [k for k, d in indegree.items() if d == 0]
|
|
189
|
-
# Stable ordering of initial nodes
|
|
190
|
-
queue.sort(key=lambda k: index[k])
|
|
191
|
-
ordered: list[str] = []
|
|
192
|
-
while queue:
|
|
193
|
-
u = queue.pop(0)
|
|
194
|
-
ordered.append(u)
|
|
195
|
-
for v in adj[u]:
|
|
196
|
-
indegree[v] -= 1
|
|
197
|
-
if indegree[v] == 0:
|
|
198
|
-
queue.append(v)
|
|
199
|
-
queue.sort(key=lambda k: index[k])
|
|
200
|
-
|
|
201
|
-
# If not all nodes processed, cycle detected; fall back to insertion order
|
|
202
|
-
if len(ordered) != len(keys):
|
|
203
|
-
ordered = keys
|
|
204
|
-
return [self.sources[k] for k in ordered]
|
pulse/css.py
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import hashlib
|
|
2
|
-
import inspect
|
|
3
|
-
from collections.abc import Iterable, Iterator, MutableMapping
|
|
4
|
-
from dataclasses import dataclass
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import override
|
|
7
|
-
|
|
8
|
-
_CSS_MODULES: MutableMapping[Path, "CssModule"] = {}
|
|
9
|
-
_CSS_IMPORTS: dict[str, "CssImport"] = {}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _caller_file() -> Path:
|
|
13
|
-
frame = inspect.currentframe()
|
|
14
|
-
try:
|
|
15
|
-
if frame is None or frame.f_back is None:
|
|
16
|
-
raise RuntimeError("Cannot determine caller frame for ps.css()")
|
|
17
|
-
caller = frame.f_back
|
|
18
|
-
# Walk past helper wrappers (ps.css may be imported under different name)
|
|
19
|
-
while caller and caller.f_code.co_filename == __file__:
|
|
20
|
-
caller = caller.f_back
|
|
21
|
-
if caller is None:
|
|
22
|
-
raise RuntimeError("Cannot determine caller for ps.css()")
|
|
23
|
-
return Path(caller.f_code.co_filename).resolve()
|
|
24
|
-
finally:
|
|
25
|
-
del frame
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def css_module(path: str | Path, *, relative: bool = False) -> "CssModule":
|
|
29
|
-
source = Path(path)
|
|
30
|
-
caller = _caller_file()
|
|
31
|
-
if relative:
|
|
32
|
-
source = caller.parent / source
|
|
33
|
-
source = source.resolve()
|
|
34
|
-
if not source.exists():
|
|
35
|
-
raise FileNotFoundError(f"CSS module '{source}' not found")
|
|
36
|
-
module = _CSS_MODULES.get(source)
|
|
37
|
-
if not module:
|
|
38
|
-
module = CssModule.create(source)
|
|
39
|
-
_CSS_MODULES[source] = module
|
|
40
|
-
return module
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def css(path: str | Path, *, relative: bool = False) -> "CssImport":
|
|
44
|
-
caller = _caller_file()
|
|
45
|
-
if relative:
|
|
46
|
-
source_path = (caller.parent / Path(path)).resolve()
|
|
47
|
-
if not source_path.exists():
|
|
48
|
-
raise FileNotFoundError(
|
|
49
|
-
f"CSS import '{path}' not found relative to {caller.parent}"
|
|
50
|
-
)
|
|
51
|
-
key = f"file://{source_path}"
|
|
52
|
-
existing = _CSS_IMPORTS.get(key)
|
|
53
|
-
if existing:
|
|
54
|
-
return existing
|
|
55
|
-
imp = CssImport(
|
|
56
|
-
_import_id(str(source_path)), specifier=None, source_path=source_path
|
|
57
|
-
)
|
|
58
|
-
_CSS_IMPORTS[key] = imp
|
|
59
|
-
return imp
|
|
60
|
-
|
|
61
|
-
spec = str(path)
|
|
62
|
-
existing = _CSS_IMPORTS.get(spec)
|
|
63
|
-
if existing:
|
|
64
|
-
return existing
|
|
65
|
-
imp = CssImport(_import_id(spec), specifier=spec, source_path=None)
|
|
66
|
-
_CSS_IMPORTS[spec] = imp
|
|
67
|
-
return imp
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def registered_css_modules() -> list["CssModule"]:
|
|
71
|
-
return list(_CSS_MODULES.values())
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def registered_css_imports() -> list["CssImport"]:
|
|
75
|
-
return list(_CSS_IMPORTS.values())
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@dataclass(frozen=True)
|
|
79
|
-
class CssModule:
|
|
80
|
-
id: str
|
|
81
|
-
source_path: Path
|
|
82
|
-
|
|
83
|
-
@staticmethod
|
|
84
|
-
def create(path: Path) -> "CssModule":
|
|
85
|
-
module_id = _module_id(path)
|
|
86
|
-
return CssModule(module_id, path)
|
|
87
|
-
|
|
88
|
-
def __getattr__(self, key: str) -> "CssReference":
|
|
89
|
-
if key.startswith("__") and key.endswith("__"):
|
|
90
|
-
raise AttributeError(key)
|
|
91
|
-
return CssReference(self, key)
|
|
92
|
-
|
|
93
|
-
def __getitem__(self, key: str) -> "CssReference":
|
|
94
|
-
return self.__getattr__(key)
|
|
95
|
-
|
|
96
|
-
def iter(self, names: Iterable[str]) -> Iterator["CssReference"]:
|
|
97
|
-
for name in names:
|
|
98
|
-
yield CssReference(self, name)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@dataclass(frozen=True)
|
|
102
|
-
class CssReference:
|
|
103
|
-
module: CssModule
|
|
104
|
-
name: str
|
|
105
|
-
|
|
106
|
-
def __post_init__(self) -> None:
|
|
107
|
-
if not self.name:
|
|
108
|
-
raise ValueError("CSS class name cannot be empty")
|
|
109
|
-
|
|
110
|
-
def __bool__(self) -> bool:
|
|
111
|
-
raise TypeError("CssReference objects cannot be coerced to bool")
|
|
112
|
-
|
|
113
|
-
def __int__(self) -> int:
|
|
114
|
-
raise TypeError("CssReference objects cannot be converted to int")
|
|
115
|
-
|
|
116
|
-
def __float__(self) -> float:
|
|
117
|
-
raise TypeError("CssReference objects cannot be converted to float")
|
|
118
|
-
|
|
119
|
-
@override
|
|
120
|
-
def __str__(self) -> str:
|
|
121
|
-
raise TypeError("CssReference objects cannot be converted to str")
|
|
122
|
-
|
|
123
|
-
@override
|
|
124
|
-
def __repr__(self) -> str:
|
|
125
|
-
return f"CssReference(module={self.module.id!r}, name={self.name!r})"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def _module_id(path: Path) -> str:
|
|
129
|
-
data = str(path).encode("utf-8")
|
|
130
|
-
digest = hashlib.sha1(data).hexdigest()
|
|
131
|
-
return f"css_{digest[:12]}"
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
@dataclass(frozen=True)
|
|
135
|
-
class CssImport:
|
|
136
|
-
id: str
|
|
137
|
-
specifier: str | None
|
|
138
|
-
source_path: Path | None
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def _import_id(value: str) -> str:
|
|
142
|
-
data = value.encode("utf-8")
|
|
143
|
-
digest = hashlib.sha1(data).hexdigest()
|
|
144
|
-
return f"css_import_{digest[:12]}"
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
__all__ = [
|
|
148
|
-
"CssModule",
|
|
149
|
-
"CssReference",
|
|
150
|
-
"CssImport",
|
|
151
|
-
"css",
|
|
152
|
-
"css_module",
|
|
153
|
-
"registered_css_modules",
|
|
154
|
-
"registered_css_imports",
|
|
155
|
-
]
|