pulse-framework 0.1.46__py3-none-any.whl → 0.1.48__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 (73) hide show
  1. pulse/__init__.py +9 -23
  2. pulse/app.py +6 -25
  3. pulse/cli/processes.py +1 -0
  4. pulse/codegen/codegen.py +43 -88
  5. pulse/codegen/js.py +35 -5
  6. pulse/codegen/templates/route.py +341 -254
  7. pulse/form.py +1 -1
  8. pulse/helpers.py +51 -27
  9. pulse/hooks/core.py +2 -2
  10. pulse/hooks/effects.py +1 -1
  11. pulse/hooks/init.py +2 -1
  12. pulse/hooks/setup.py +1 -1
  13. pulse/hooks/stable.py +2 -2
  14. pulse/hooks/states.py +2 -2
  15. pulse/html/props.py +3 -2
  16. pulse/html/tags.py +135 -0
  17. pulse/html/tags.pyi +4 -0
  18. pulse/js/__init__.py +110 -0
  19. pulse/js/__init__.pyi +95 -0
  20. pulse/js/_types.py +297 -0
  21. pulse/js/array.py +253 -0
  22. pulse/js/console.py +47 -0
  23. pulse/js/date.py +113 -0
  24. pulse/js/document.py +138 -0
  25. pulse/js/error.py +139 -0
  26. pulse/js/json.py +62 -0
  27. pulse/js/map.py +84 -0
  28. pulse/js/math.py +66 -0
  29. pulse/js/navigator.py +76 -0
  30. pulse/js/number.py +54 -0
  31. pulse/js/object.py +173 -0
  32. pulse/js/promise.py +150 -0
  33. pulse/js/regexp.py +54 -0
  34. pulse/js/set.py +109 -0
  35. pulse/js/string.py +35 -0
  36. pulse/js/weakmap.py +50 -0
  37. pulse/js/weakset.py +45 -0
  38. pulse/js/window.py +199 -0
  39. pulse/messages.py +22 -3
  40. pulse/proxy.py +21 -8
  41. pulse/react_component.py +167 -14
  42. pulse/reactive_extensions.py +5 -5
  43. pulse/render_session.py +144 -34
  44. pulse/renderer.py +80 -115
  45. pulse/routing.py +1 -18
  46. pulse/transpiler/__init__.py +131 -0
  47. pulse/transpiler/builtins.py +731 -0
  48. pulse/transpiler/constants.py +110 -0
  49. pulse/transpiler/context.py +26 -0
  50. pulse/transpiler/errors.py +2 -0
  51. pulse/transpiler/function.py +250 -0
  52. pulse/transpiler/ids.py +16 -0
  53. pulse/transpiler/imports.py +409 -0
  54. pulse/transpiler/js_module.py +274 -0
  55. pulse/transpiler/modules/__init__.py +30 -0
  56. pulse/transpiler/modules/asyncio.py +38 -0
  57. pulse/transpiler/modules/json.py +20 -0
  58. pulse/transpiler/modules/math.py +320 -0
  59. pulse/transpiler/modules/re.py +466 -0
  60. pulse/transpiler/modules/tags.py +268 -0
  61. pulse/transpiler/modules/typing.py +59 -0
  62. pulse/transpiler/nodes.py +1216 -0
  63. pulse/transpiler/py_module.py +119 -0
  64. pulse/transpiler/transpiler.py +938 -0
  65. pulse/transpiler/utils.py +4 -0
  66. pulse/vdom.py +112 -6
  67. {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.48.dist-info}/METADATA +1 -1
  68. pulse_framework-0.1.48.dist-info/RECORD +119 -0
  69. pulse/codegen/imports.py +0 -204
  70. pulse/css.py +0 -155
  71. pulse_framework-0.1.46.dist-info/RECORD +0 -80
  72. {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.48.dist-info}/WHEEL +0 -0
  73. {pulse_framework-0.1.46.dist-info → pulse_framework-0.1.48.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,119 @@
1
+ """Python module transpilation system for javascript_v2.
2
+
3
+ Provides infrastructure for mapping Python modules (like `math`) to JavaScript equivalents.
4
+ For direct JavaScript module bindings, use the pulse.js.* module system instead.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Callable
10
+ from dataclasses import dataclass
11
+ from types import ModuleType
12
+ from typing import Any, TypeAlias, cast, override
13
+
14
+ from pulse.transpiler.errors import JSCompilationError
15
+ from pulse.transpiler.nodes import JSExpr, JSTransformer
16
+
17
+ # Type alias for module transpilers - either a PyModule class or a dict
18
+ # The dict can contain JSExpr or Callable[..., JSExpr] during construction,
19
+ # but will be normalized to only JSExpr before storage
20
+ PyModuleTranspiler: TypeAlias = dict[str, JSExpr]
21
+
22
+
23
+ @dataclass
24
+ class PyModuleExpr(JSExpr):
25
+ """JSExpr for a Python module imported as a whole (e.g., `import math`).
26
+
27
+ Holds a transpiler dict mapping attribute names to JSExpr.
28
+ Attribute access looks up the attr in the dict and returns the result.
29
+ """
30
+
31
+ transpiler: dict[str, JSExpr]
32
+
33
+ @override
34
+ def emit(self) -> str:
35
+ raise JSCompilationError("PyModuleExpr cannot be emitted directly")
36
+
37
+ @override
38
+ def emit_call(self, args: list[Any], kwargs: dict[str, Any]) -> JSExpr:
39
+ raise JSCompilationError("PyModuleExpr cannot be called directly")
40
+
41
+ @override
42
+ def emit_subscript(self, indices: list[Any]) -> JSExpr:
43
+ raise JSCompilationError("PyModuleExpr cannot be subscripted")
44
+
45
+ @override
46
+ def emit_getattr(self, attr: str) -> JSExpr:
47
+ value = self.transpiler.get(attr)
48
+ if value is None:
49
+ raise JSCompilationError(f"Module has no attribute '{attr}'")
50
+ # transpiler always contains JSExpr (wrapping happens in register_module)
51
+ return value
52
+
53
+
54
+ class PyModule:
55
+ """Base class for Python module transpilation mappings.
56
+
57
+ Subclasses define static methods and class attributes that map Python module
58
+ functions and constants to their JavaScript equivalents.
59
+
60
+ Example:
61
+ class PyMath(PyModule):
62
+ # Constants - JSExpr values
63
+ pi = JSMember(JSIdentifier("Math"), "PI")
64
+
65
+ # Functions - return JSExpr
66
+ @staticmethod
67
+ def floor(x: JSExpr) -> JSExpr:
68
+ return JSMemberCall(JSIdentifier("Math"), "floor", [x])
69
+ """
70
+
71
+
72
+ PY_MODULES: dict[ModuleType, PyModuleTranspiler] = {}
73
+
74
+
75
+ def register_module(
76
+ module: ModuleType,
77
+ transpilation: type[PyModule] | dict[str, JSExpr | Callable[..., JSExpr]],
78
+ ) -> None:
79
+ """Register a Python module for transpilation.
80
+
81
+ Args:
82
+ module: The Python module to register (e.g., `math`, `pulse.html.tags`)
83
+ transpilation: Either a PyModule subclass or a dict mapping attribute names
84
+ to JSExpr (for constants) or Callable[..., JSExpr] (for functions).
85
+ Callables will be wrapped in JSTransformer during registration.
86
+ """
87
+ # Convert PyModule class to dict if needed (wraps callables)
88
+ transpiler_dict: PyModuleTranspiler = {}
89
+
90
+ # Get items to iterate over - either from dict or PyModule class
91
+ if isinstance(transpilation, dict):
92
+ items = transpilation.items()
93
+ else:
94
+ # Convert PyModule class to (name, attr) pairs
95
+ items = (
96
+ (attr_name, getattr(transpilation, attr_name, None))
97
+ for attr_name in dir(transpilation)
98
+ if not attr_name.startswith("_")
99
+ )
100
+
101
+ # Normalize: wrap callables in JSTransformer and register via JSExpr.register
102
+ for attr_name, attr in items:
103
+ if isinstance(attr, JSExpr):
104
+ pass
105
+ elif callable(attr):
106
+ # Wrap callables in JSTransformer so result always contains JSExpr
107
+ attr = JSTransformer(cast(Callable[..., JSExpr], attr))
108
+ else:
109
+ # Skip non-JSExpr, non-callable values
110
+ continue
111
+
112
+ transpiler_dict[attr_name] = attr
113
+ # Register the module attribute value for lookup by id
114
+ module_value = getattr(module, attr_name, None)
115
+ if module_value is not None:
116
+ JSExpr.register(module_value, attr)
117
+
118
+ # Store as dict (now normalized to only JSExpr)
119
+ PY_MODULES[module] = transpiler_dict