Skip to content

Commit a1586f3

Browse files
committed
pr feedback
1 parent 9836102 commit a1586f3

3 files changed

Lines changed: 71 additions & 112 deletions

File tree

dimos/core/native_module.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ def start(self) -> None:
150150
shutdown_timeout=self.config.shutdown_timeout,
151151
log_json=self.config.log_format == LogFormat.JSON,
152152
)
153+
self._proc.start()
153154

154155
def _resolve_paths(self) -> None:
155156
"""Resolve relative ``cwd`` and ``executable`` against the subclass's source file."""

dimos/utils/test_thread_utils.py

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,6 @@ def test_basic_get_set(self) -> None:
6262
v.set(99)
6363
assert v.get() == 99
6464

65-
def test_bool_truthy(self) -> None:
66-
v = ThreadSafeVal(True)
67-
assert bool(v) is True
68-
v.set(False)
69-
assert bool(v) is False
70-
71-
def test_bool_zero(self) -> None:
72-
v = ThreadSafeVal(0)
73-
assert bool(v) is False
74-
v.set(1)
75-
assert bool(v) is True
76-
7765
def test_context_manager_returns_value(self) -> None:
7866
v = ThreadSafeVal("hello")
7967
with v as val:
@@ -113,20 +101,6 @@ def do_it() -> None:
113101
t.join(timeout=2)
114102
assert result.is_set(), "Deadlocked! get() inside with block hung"
115103

116-
def test_bool_inside_context_manager_no_deadlock(self) -> None:
117-
v = ThreadSafeVal(True)
118-
result = threading.Event()
119-
120-
def do_it() -> None:
121-
with v:
122-
_ = bool(v)
123-
result.set()
124-
125-
t = threading.Thread(target=do_it)
126-
t.start()
127-
t.join(timeout=2)
128-
assert result.is_set(), "Deadlocked! bool() inside with block hung"
129-
130104
def test_context_manager_blocks_other_threads(self) -> None:
131105
"""While one thread holds the lock via `with`, others should block on set()."""
132106
v = ThreadSafeVal(0)
@@ -245,6 +219,7 @@ def target() -> None:
245219
ran.set()
246220

247221
mt = ModuleThread(module=mod, target=target, name="test-basic")
222+
mt.start()
248223
ran.wait(timeout=2)
249224
assert ran.is_set()
250225
mt.stop()
@@ -254,14 +229,15 @@ def test_auto_start(self) -> None:
254229
mod = FakeModule()
255230
started = threading.Event()
256231
mt = ModuleThread(module=mod, target=started.set, name="test-autostart")
232+
mt.start()
257233
started.wait(timeout=2)
258234
assert started.is_set()
259235
mt.stop()
260236

261237
def test_deferred_start(self) -> None:
262238
mod = FakeModule()
263239
started = threading.Event()
264-
mt = ModuleThread(module=mod, target=started.set, name="test-deferred", start=False)
240+
mt = ModuleThread(module=mod, target=started.set, name="test-deferred")
265241
time.sleep(0.1)
266242
assert not started.is_set()
267243
mt.start()
@@ -275,11 +251,11 @@ def test_stopping_property(self) -> None:
275251
holder: list[ModuleThread] = []
276252

277253
def target() -> None:
278-
while not holder[0].stopping:
254+
while holder[0].status.get() == "running":
279255
time.sleep(0.01)
280256
saw_stopping.set()
281257

282-
mt = ModuleThread(module=mod, target=target, name="test-stopping", start=False)
258+
mt = ModuleThread(module=mod, target=target, name="test-stopping")
283259
holder.append(mt)
284260
mt.start()
285261
time.sleep(0.05)
@@ -290,6 +266,7 @@ def target() -> None:
290266
def test_stop_idempotent(self) -> None:
291267
mod = FakeModule()
292268
mt = ModuleThread(module=mod, target=lambda: time.sleep(0.01), name="test-idem")
269+
mt.start()
293270
time.sleep(0.05)
294271
mt.stop()
295272
mt.stop() # second call should not raise
@@ -305,7 +282,7 @@ def target() -> None:
305282
holder[0].stop() # stop ourselves — should not deadlock
306283
result.set()
307284

308-
mt = ModuleThread(module=mod, target=target, name="test-self-stop", start=False)
285+
mt = ModuleThread(module=mod, target=target, name="test-self-stop")
309286
holder.append(mt)
310287
mt.start()
311288
result.wait(timeout=3)
@@ -319,10 +296,10 @@ def test_dispose_stops_thread(self) -> None:
319296

320297
def target() -> None:
321298
running.set()
322-
while not holder[0].stopping:
299+
while holder[0].status.get() == "running":
323300
time.sleep(0.01)
324301

325-
mt = ModuleThread(module=mod, target=target, name="test-dispose", start=False)
302+
mt = ModuleThread(module=mod, target=target, name="test-dispose")
326303
holder.append(mt)
327304
mt.start()
328305
running.wait(timeout=2)
@@ -336,10 +313,10 @@ def test_concurrent_stop_calls(self) -> None:
336313
holder: list[ModuleThread] = []
337314

338315
def target() -> None:
339-
while not holder[0].stopping:
316+
while holder[0].status.get() == "running":
340317
time.sleep(0.01)
341318

342-
mt = ModuleThread(module=mod, target=target, name="test-concurrent-stop", start=False)
319+
mt = ModuleThread(module=mod, target=target, name="test-concurrent-stop")
343320
holder.append(mt)
344321
mt.start()
345322
time.sleep(0.05)
@@ -369,6 +346,7 @@ def stubborn_target() -> None:
369346
mt = ModuleThread(
370347
module=mod, target=stubborn_target, name="test-timeout", close_timeout=0.2
371348
)
349+
mt.start()
372350
start = time.monotonic()
373351
mt.stop()
374352
elapsed = time.monotonic() - start
@@ -381,10 +359,10 @@ def test_stop_concurrent_with_dispose(self) -> None:
381359
holder: list[ModuleThread] = []
382360

383361
def target(h: list[ModuleThread] = holder) -> None:
384-
while not h[0].stopping:
362+
while h[0].status.get() == "running":
385363
time.sleep(0.001)
386364

387-
mt = ModuleThread(module=mod, target=target, name="test-stop-dispose", start=False)
365+
mt = ModuleThread(module=mod, target=target, name="test-stop-dispose")
388366
holder.append(mt)
389367
mt.start()
390368
time.sleep(0.02)
@@ -490,6 +468,7 @@ def test_basic_lifecycle(self) -> None:
490468
args=[PYTHON, "-c", "import time; time.sleep(30)"],
491469
shutdown_timeout=2.0,
492470
)
471+
mp.start()
493472
assert mp.is_alive
494473
assert mp.pid is not None
495474
mp.stop()
@@ -503,6 +482,7 @@ def test_stop_idempotent(self) -> None:
503482
args=[PYTHON, "-c", "import time; time.sleep(30)"],
504483
shutdown_timeout=1.0,
505484
)
485+
mp.start()
506486
mp.stop()
507487
mp.stop() # should not raise
508488
mp.stop()
@@ -514,6 +494,7 @@ def test_dispose_stops_process(self) -> None:
514494
args=[PYTHON, "-c", "import time; time.sleep(30)"],
515495
shutdown_timeout=2.0,
516496
)
497+
mp.start()
517498
mod.dispose()
518499
time.sleep(0.5)
519500
assert not mp.is_alive
@@ -523,23 +504,25 @@ def test_on_exit_fires_on_natural_exit(self) -> None:
523504
mod = FakeModule()
524505
exit_called = threading.Event()
525506

526-
ModuleProcess(
507+
mp = ModuleProcess(
527508
module=mod,
528509
args=[PYTHON, "-c", "print('done')"],
529510
on_exit=exit_called.set,
530511
)
512+
mp.start()
531513
exit_called.wait(timeout=5)
532514
assert exit_called.is_set(), "on_exit was not called after natural process exit"
533515

534516
def test_on_exit_fires_on_crash(self) -> None:
535517
mod = FakeModule()
536518
exit_called = threading.Event()
537519

538-
ModuleProcess(
520+
mp = ModuleProcess(
539521
module=mod,
540522
args=[PYTHON, "-c", "import sys; sys.exit(1)"],
541523
on_exit=exit_called.set,
542524
)
525+
mp.start()
543526
exit_called.wait(timeout=5)
544527
assert exit_called.is_set(), "on_exit was not called after process crash"
545528

@@ -554,6 +537,7 @@ def test_on_exit_not_fired_on_stop(self) -> None:
554537
on_exit=exit_called.set,
555538
shutdown_timeout=2.0,
556539
)
540+
mp.start()
557541
time.sleep(0.2) # let watchdog start
558542
mp.stop()
559543
time.sleep(1.0) # give watchdog time to potentially fire
@@ -565,6 +549,7 @@ def test_stdout_logged(self) -> None:
565549
module=mod,
566550
args=[PYTHON, "-c", "print('hello from subprocess')"],
567551
)
552+
mp.start()
568553
time.sleep(1.0) # let output be read
569554
mp.stop()
570555

@@ -574,6 +559,7 @@ def test_stderr_logged(self) -> None:
574559
module=mod,
575560
args=[PYTHON, "-c", "import sys; sys.stderr.write('error msg\\n')"],
576561
)
562+
mp.start()
577563
time.sleep(1.0)
578564
mp.stop()
579565

@@ -588,6 +574,7 @@ def test_log_json_mode(self) -> None:
588574
],
589575
log_json=True,
590576
)
577+
mp.start()
591578
time.sleep(1.0)
592579
mp.stop()
593580

@@ -598,6 +585,7 @@ def test_log_json_malformed(self) -> None:
598585
args=[PYTHON, "-c", "print('not json')"],
599586
log_json=True,
600587
)
588+
mp.start()
601589
time.sleep(1.0)
602590
mp.stop()
603591

@@ -614,6 +602,7 @@ def test_stop_process_that_ignores_sigterm(self) -> None:
614602
shutdown_timeout=0.5,
615603
kill_timeout=2.0,
616604
)
605+
mp.start()
617606
time.sleep(0.2)
618607
start = time.monotonic()
619608
mp.stop()
@@ -629,6 +618,7 @@ def test_stop_already_dead_process(self) -> None:
629618
module=mod,
630619
args=[PYTHON, "-c", "pass"], # exits immediately
631620
)
621+
mp.start()
632622
time.sleep(1.0) # let it die
633623
mp.stop() # should not raise
634624

@@ -639,6 +629,7 @@ def test_concurrent_stop(self) -> None:
639629
args=[PYTHON, "-c", "import time; time.sleep(30)"],
640630
shutdown_timeout=2.0,
641631
)
632+
mp.start()
642633
errors = []
643634

644635
def stop_it() -> None:
@@ -667,11 +658,12 @@ def fake_module_stop() -> None:
667658
mod.dispose()
668659
stop_called.set()
669660

670-
ModuleProcess(
661+
mp = ModuleProcess(
671662
module=mod,
672663
args=[PYTHON, "-c", "pass"], # exits immediately
673664
on_exit=fake_module_stop,
674665
)
666+
mp.start()
675667
stop_called.wait(timeout=5)
676668
assert stop_called.is_set(), "Deadlocked! on_exit -> dispose -> stop chain hung"
677669

@@ -685,7 +677,6 @@ def test_deferred_start(self) -> None:
685677
mp = ModuleProcess(
686678
module=mod,
687679
args=[PYTHON, "-c", "import time; time.sleep(30)"],
688-
start=False,
689680
)
690681
assert not mp.is_alive
691682
mp.start()
@@ -696,7 +687,7 @@ def test_env_passed(self) -> None:
696687
mod = FakeModule()
697688
exit_called = threading.Event()
698689

699-
ModuleProcess(
690+
mp = ModuleProcess(
700691
module=mod,
701692
args=[
702693
PYTHON,
@@ -706,6 +697,7 @@ def test_env_passed(self) -> None:
706697
env={**os.environ, "MY_VAR": "42"},
707698
on_exit=exit_called.set,
708699
)
700+
mp.start()
709701
exit_called.wait(timeout=5)
710702
# Process should have exited with 0 (our on_exit fires for all unmanaged exits)
711703
assert exit_called.is_set()
@@ -717,6 +709,7 @@ def test_cwd_passed(self) -> None:
717709
args=[PYTHON, "-c", "import os; print(os.getcwd())"],
718710
cwd="/tmp",
719711
)
712+
mp.start()
720713
time.sleep(1.0)
721714
mp.stop()
722715

@@ -826,23 +819,25 @@ def test_chain_no_deadlock_fast_exit(self) -> None:
826819
for _ in range(20):
827820
mod = FakeModule()
828821
done = threading.Event()
829-
ModuleProcess(
822+
mp = ModuleProcess(
830823
module=mod,
831824
args=[PYTHON, "-c", "pass"],
832825
on_exit=self._make_fake_stop(mod, done),
833826
)
827+
mp.start()
834828
assert done.wait(timeout=5), "Deadlock in dispose chain (fast exit)"
835829

836830
def test_chain_no_deadlock_slow_exit(self) -> None:
837831
"""Process runs briefly then exits."""
838832
for _ in range(10):
839833
mod = FakeModule()
840834
done = threading.Event()
841-
ModuleProcess(
835+
mp = ModuleProcess(
842836
module=mod,
843837
args=[PYTHON, "-c", "import time; time.sleep(0.1)"],
844838
on_exit=self._make_fake_stop(mod, done),
845839
)
840+
mp.start()
846841
assert done.wait(timeout=5), "Deadlock in dispose chain (slow exit)"
847842

848843
def test_chain_concurrent_with_external_stop(self) -> None:
@@ -856,6 +851,7 @@ def test_chain_concurrent_with_external_stop(self) -> None:
856851
on_exit=self._make_fake_stop(mod, done),
857852
shutdown_timeout=1.0,
858853
)
854+
mp.start()
859855
# Race: the process might exit naturally or we might stop it
860856
time.sleep(0.03)
861857
mp.stop()
@@ -874,11 +870,12 @@ def slow_stop(self_mt: ModuleThread) -> None:
874870
mod = FakeModule()
875871
done = threading.Event()
876872
with mock.patch.object(ModuleThread, "stop", slow_stop):
877-
ModuleProcess(
873+
mp = ModuleProcess(
878874
module=mod,
879875
args=[PYTHON, "-c", "pass"],
880876
on_exit=self._make_fake_stop(mod, done),
881877
)
878+
mp.start()
882879
assert done.wait(timeout=10), "Deadlock with slow ModuleThread.stop()"
883880

884881

0 commit comments

Comments
 (0)