pulse-framework 0.1.38a7__tar.gz → 0.1.38a9__tar.gz

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 (70) hide show
  1. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/PKG-INFO +1 -1
  2. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/pyproject.toml +1 -1
  3. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/cmd.py +13 -2
  4. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/processes.py +6 -2
  5. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/state.py +21 -0
  6. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/README.md +0 -0
  7. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/__init__.py +0 -0
  8. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/app.py +0 -0
  9. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/channel.py +0 -0
  10. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/__init__.py +0 -0
  11. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/dependencies.py +0 -0
  12. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/folder_lock.py +0 -0
  13. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/helpers.py +0 -0
  14. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/models.py +0 -0
  15. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/packages.py +0 -0
  16. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/secrets.py +0 -0
  17. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cli/uvicorn_log_config.py +0 -0
  18. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/codegen/__init__.py +0 -0
  19. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/codegen/codegen.py +0 -0
  20. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/codegen/imports.py +0 -0
  21. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/codegen/js.py +0 -0
  22. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/codegen/templates/__init__.py +0 -0
  23. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/codegen/templates/layout.py +0 -0
  24. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/codegen/templates/route.py +0 -0
  25. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/codegen/templates/routes_ts.py +0 -0
  26. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/codegen/utils.py +0 -0
  27. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/components/__init__.py +0 -0
  28. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/components/for_.py +0 -0
  29. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/components/if_.py +0 -0
  30. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/components/react_router.py +0 -0
  31. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/context.py +0 -0
  32. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/cookies.py +0 -0
  33. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/css.py +0 -0
  34. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/decorators.py +0 -0
  35. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/env.py +0 -0
  36. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/form.py +0 -0
  37. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/helpers.py +0 -0
  38. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/hooks/__init__.py +0 -0
  39. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/hooks/core.py +0 -0
  40. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/hooks/effects.py +0 -0
  41. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/hooks/runtime.py +0 -0
  42. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/hooks/setup.py +0 -0
  43. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/hooks/stable.py +0 -0
  44. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/hooks/states.py +0 -0
  45. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/html/__init__.py +0 -0
  46. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/html/elements.py +0 -0
  47. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/html/events.py +0 -0
  48. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/html/props.py +0 -0
  49. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/html/svg.py +0 -0
  50. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/html/tags.py +0 -0
  51. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/html/tags.pyi +0 -0
  52. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/messages.py +0 -0
  53. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/middleware.py +0 -0
  54. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/plugin.py +0 -0
  55. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/proxy.py +0 -0
  56. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/py.typed +0 -0
  57. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/query.py +0 -0
  58. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/react_component.py +0 -0
  59. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/reactive.py +0 -0
  60. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/reactive_extensions.py +0 -0
  61. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/render_session.py +0 -0
  62. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/renderer.py +0 -0
  63. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/request.py +0 -0
  64. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/routing.py +0 -0
  65. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/serializer.py +0 -0
  66. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/types/__init__.py +0 -0
  67. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/types/event_handler.py +0 -0
  68. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/user_session.py +0 -0
  69. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/vdom.py +0 -0
  70. {pulse_framework-0.1.38a7 → pulse_framework-0.1.38a9}/src/pulse/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pulse-framework
3
- Version: 0.1.38a7
3
+ Version: 0.1.38a9
4
4
  Summary: Pulse - Full-stack framework for building real-time React applications in Python
5
5
  Requires-Dist: websockets>=12.0
6
6
  Requires-Dist: fastapi>=0.104.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pulse-framework"
3
- version = "0.1.38a7"
3
+ version = "0.1.38a9"
4
4
  description = "Pulse - Full-stack framework for building real-time React applications in Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -204,7 +204,10 @@ def run(
204
204
  )
205
205
  )
206
206
 
207
- tag_colors = {"server": "cyan", "web": "orange1"}
207
+ # Only add tags in dev mode to avoid breaking structured output (e.g., CloudWatch EMF metrics)
208
+ tag_colors = (
209
+ {"server": "cyan", "web": "orange1"} if env.pulse_env == "dev" else None
210
+ )
208
211
 
209
212
  with FolderLock(web_root):
210
213
  try:
@@ -249,7 +252,15 @@ def generate(
249
252
  app = app_ctx.app
250
253
  console.log(f"📋 Found {len(app.routes.flat_tree)} routes")
251
254
 
252
- addr = app.server_address or "localhost:8000"
255
+ # In CI or prod mode, server_address must be provided
256
+ if (ci or prod) and not app.server_address:
257
+ typer.echo(
258
+ "❌ server_address must be provided when generating in CI or production mode. "
259
+ + "Set it in your App constructor or via the PULSE_SERVER_ADDRESS environment variable."
260
+ )
261
+ raise typer.Exit(1)
262
+
263
+ addr = app.server_address or "http://localhost:8000"
253
264
  app.run_codegen(addr)
254
265
 
255
266
  if len(app.routes.flat_tree) > 0:
@@ -215,8 +215,12 @@ def _run_without_pty(
215
215
 
216
216
 
217
217
  def _write_tagged_line(name: str, message: str, colors: Mapping[str, str]) -> None:
218
- color = ANSI_CODES.get(colors.get(name, ""), ANSI_CODES["default"])
219
- sys.stdout.write(f"{color}[{name}]\033[0m {message}\n")
218
+ # Only add tags if colors dict is not empty (i.e., tagging is enabled)
219
+ if colors:
220
+ color = ANSI_CODES.get(colors.get(name, ""), ANSI_CODES["default"])
221
+ sys.stdout.write(f"{color}[{name}]\033[0m {message}\n")
222
+ else:
223
+ sys.stdout.write(f"{message}\n")
220
224
  sys.stdout.flush()
221
225
 
222
226
 
@@ -206,6 +206,15 @@ class State(ABC, metaclass=StateMeta):
206
206
  ```
207
207
 
208
208
  Properties will automatically trigger re-renders when changed.
209
+
210
+ Override `on_dispose()` to run cleanup code when the state is disposed:
211
+ ```python
212
+ class MyState(ps.State):
213
+ def on_dispose(self):
214
+ # Clean up timers, connections, etc.
215
+ self.timer.cancel()
216
+ self.connection.close()
217
+ ```
209
218
  """
210
219
 
211
220
  @override
@@ -310,7 +319,19 @@ class State(ABC, metaclass=StateMeta):
310
319
  # if isinstance(value,QueryProperty):
311
320
  # value.
312
321
 
322
+ def on_dispose(self) -> None:
323
+ """
324
+ Override this method to run cleanup code when the state is disposed.
325
+
326
+ This is called automatically when `dispose()` is called, before effects are disposed.
327
+ Use this to clean up timers, connections, or other resources.
328
+ """
329
+ pass
330
+
313
331
  def dispose(self):
332
+ # Call user-defined cleanup hook first
333
+ self.on_dispose()
334
+
314
335
  disposed = set()
315
336
  for value in self.__dict__.values():
316
337
  if isinstance(value, Effect):