pulseml 0.2.1__tar.gz → 0.2.2__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.
- {pulseml-0.2.1 → pulseml-0.2.2}/PKG-INFO +1 -1
- {pulseml-0.2.1 → pulseml-0.2.2}/pyproject.toml +1 -1
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulse/pulse.py +42 -19
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulseml.egg-info/PKG-INFO +1 -1
- {pulseml-0.2.1 → pulseml-0.2.2}/LICENSE +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/README.md +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/setup.cfg +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulse/__init__.py +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulse/pulse_backend.py +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulse/pulse_cli.py +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulse/pulse_pdf.py +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulse/pulse_supabase.py +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulseml.egg-info/SOURCES.txt +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulseml.egg-info/dependency_links.txt +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulseml.egg-info/requires.txt +0 -0
- {pulseml-0.2.1 → pulseml-0.2.2}/src/pulseml.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pulseml"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "A live ML training debugger - GUI or CLI, any backend (NumPy, PyTorch, TensorFlow, CuPy, JAX)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -3541,6 +3541,24 @@ def _install_cli_excepthook(cli):
|
|
|
3541
3541
|
previous_hook = sys.excepthook
|
|
3542
3542
|
|
|
3543
3543
|
def _hook(exc_type, exc_value, exc_tb):
|
|
3544
|
+
# Disarm the global line/call tracer immediately. Once the script
|
|
3545
|
+
# has crashed, nothing below should still be traced -- and if we
|
|
3546
|
+
# leave `sys.settrace` pointed at `cli_tracer`, it stays the
|
|
3547
|
+
# process-wide trace function into interpreter shutdown, where it
|
|
3548
|
+
# gets invoked on unrelated __del__ calls (e.g. litellm's
|
|
3549
|
+
# AsyncHTTPHandler) after module globals have already been cleared
|
|
3550
|
+
# to None. That produces a second, uncatchable "Exception ignored
|
|
3551
|
+
# in ..." failure from *inside* the tracer, separate from (and
|
|
3552
|
+
# after) whatever we report here.
|
|
3553
|
+
sys.settrace(None)
|
|
3554
|
+
try:
|
|
3555
|
+
frame = exc_tb.tb_frame if exc_tb else None
|
|
3556
|
+
while frame is not None:
|
|
3557
|
+
frame.f_trace = None
|
|
3558
|
+
frame = frame.f_back
|
|
3559
|
+
except Exception:
|
|
3560
|
+
pass
|
|
3561
|
+
|
|
3544
3562
|
previous_hook(exc_type, exc_value, exc_tb)
|
|
3545
3563
|
if issubclass(exc_type, KeyboardInterrupt):
|
|
3546
3564
|
return
|
|
@@ -3634,14 +3652,29 @@ def _start_cli_tracker(
|
|
|
3634
3652
|
cli.extra_files = dict(extra_files or {})
|
|
3635
3653
|
cli.pending_startup_error = startup_error
|
|
3636
3654
|
cli.print_banner()
|
|
3637
|
-
|
|
3655
|
+
# Agent/provider setup used to be deferred until cli_tracer saw its
|
|
3656
|
+
# first 'line' event with a trackable/resolved local in scope -- fine
|
|
3657
|
+
# for a training loop, but it meant any crash before that point (e.g.
|
|
3658
|
+
# failing during data loading, before the loop even starts) left
|
|
3659
|
+
# cli.agent_provider unset, so the crash hook below had no agent to
|
|
3660
|
+
# hand the traceback to and could only print "no AI agent is
|
|
3661
|
+
# configured". discover_variables() already falls back to the
|
|
3662
|
+
# statically-discovered `cli.discovered` names (populated from AST
|
|
3663
|
+
# parsing at auto_track()-call time, before any user code runs) when
|
|
3664
|
+
# there are no live locals yet, so setup doesn't actually need to wait
|
|
3665
|
+
# for a live frame -- run it eagerly, before the excepthook can matter.
|
|
3666
|
+
cli.interactive_setup()
|
|
3638
3667
|
|
|
3639
3668
|
if startup_error:
|
|
3640
3669
|
print("[Pulse] The function passed to auto_track() raised an exception during its dry run:")
|
|
3641
3670
|
print(startup_error)
|
|
3642
|
-
|
|
3671
|
+
if cli.agent_provider:
|
|
3672
|
+
print("[Pulse] Pulse will offer to diagnose/fix it (via the agent set up above) before continuing.\n")
|
|
3673
|
+
else:
|
|
3674
|
+
print("[Pulse] No agent configured (see summary above) -- run /agent to set one up before continuing.\n")
|
|
3675
|
+
|
|
3676
|
+
_install_cli_excepthook(cli)
|
|
3643
3677
|
|
|
3644
|
-
setup_done = {"value": False}
|
|
3645
3678
|
last_logged = {"t": 0.0}
|
|
3646
3679
|
|
|
3647
3680
|
# ------------------------------------------------------------------
|
|
@@ -3672,7 +3705,7 @@ def _start_cli_tracker(
|
|
|
3672
3705
|
# next window. Between windows, the training loop runs at native,
|
|
3673
3706
|
# untraced speed.
|
|
3674
3707
|
_CAPTURE_SPAN = min(0.05, throttle_interval / 4 if throttle_interval > 0 else 0.05)
|
|
3675
|
-
window = {"open": True, "closes_at": 0.0} # start open so
|
|
3708
|
+
window = {"open": True, "closes_at": 0.0} # start open so the first snapshot can run immediately
|
|
3676
3709
|
|
|
3677
3710
|
def _close_window():
|
|
3678
3711
|
window["open"] = False
|
|
@@ -3694,6 +3727,11 @@ def _start_cli_tracker(
|
|
|
3694
3727
|
if window["open"]:
|
|
3695
3728
|
_close_window()
|
|
3696
3729
|
|
|
3730
|
+
# Setup now runs eagerly, before tracing even starts (see above), so
|
|
3731
|
+
# the ticker can start right away too -- no need to wait for the
|
|
3732
|
+
# tracer to see a live frame first.
|
|
3733
|
+
threading.Thread(target=_ticker, daemon=True).start()
|
|
3734
|
+
|
|
3697
3735
|
def cli_tracer(frame, event, arg):
|
|
3698
3736
|
filename = frame.f_code.co_filename
|
|
3699
3737
|
if _is_library_frame(filename):
|
|
@@ -3740,21 +3778,6 @@ def _start_cli_tracker(
|
|
|
3740
3778
|
):
|
|
3741
3779
|
cli.var_states[name] = "track"
|
|
3742
3780
|
|
|
3743
|
-
if not setup_done["value"]:
|
|
3744
|
-
has_resolved_shape = any(shape is not None for shape in cli.discovered.values())
|
|
3745
|
-
has_trackable_local = any(
|
|
3746
|
-
not n.startswith("__") and is_trackable(v) for n, v in local_vars.items()
|
|
3747
|
-
)
|
|
3748
|
-
if has_resolved_shape or has_trackable_local:
|
|
3749
|
-
cli.watch_locals = local_vars
|
|
3750
|
-
cli.interactive_setup()
|
|
3751
|
-
setup_done["value"] = True
|
|
3752
|
-
# Setup is interactive/blocking; once it's done, start the
|
|
3753
|
-
# background ticker that opens/closes future windows. (Kept
|
|
3754
|
-
# off until now so setup itself isn't racing a window close.)
|
|
3755
|
-
threading.Thread(target=_ticker, daemon=True).start()
|
|
3756
|
-
return cli_tracer
|
|
3757
|
-
|
|
3758
3781
|
cli.watch_locals = local_vars
|
|
3759
3782
|
|
|
3760
3783
|
now = time.time()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|