Update `sync_bp` + tighten `test_pause_from_sync`

Add `disable_pdbp_color()` to the `sync_bp` example
to suppress pygments prompt coloring when
`PYTHON_COLORS=0` — makes pexpect pattern matching
deterministic.

Deats,
- set `loglevel='pdb'` in both script + test spawn.
- disable `enable_stack_on_sig` in example, assert
  no `stackscope` output in test.
- update `attach_patts` keys/values with `|_<Task`
  / `|_<Thread` / `|_('subactor'` prefixes to match
  actual tree-dump format.
- add call-site patterns (`tractor.pause_from_sync()`
  `tractor.pause()`, `breakpoint(hide_tb=...)`).
- trim trailing `\n` from `Lock.repr()` output.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
subint_forkserver_backend
Gud Boi 2026-04-30 20:54:50 -04:00
parent 48523358cf
commit fc2e298a29
4 changed files with 52 additions and 25 deletions

View File

@ -1,9 +1,22 @@
from functools import partial from functools import partial
import os
import time import time
# ?TODO? how to make `pdbp` enforce this?
# os.environ['PYTHON_COLORS'] = '0'
# os.environ['NO_COLOR'] = '1'
import trio import trio
import tractor import tractor
# disable `pbdp` prompt colors
# for prompt matching in test.
def disable_pdbp_color():
if os.environ['PYTHON_COLORS'] == '0':
from tractor.devx.debug import _repl
_repl.TractorConfig.use_pygments = False
# TODO: only import these when not running from test harness? # TODO: only import these when not running from test harness?
# can we detect `pexpect` usage maybe? # can we detect `pexpect` usage maybe?
# from tractor.devx.debug import ( # from tractor.devx.debug import (
@ -42,6 +55,7 @@ async def start_n_sync_pause(
ctx: tractor.Context, ctx: tractor.Context,
): ):
actor: tractor.Actor = tractor.current_actor() actor: tractor.Actor = tractor.current_actor()
disable_pdbp_color()
# sync to parent-side task # sync to parent-side task
await ctx.started() await ctx.started()
@ -52,13 +66,15 @@ async def start_n_sync_pause(
async def main() -> None: async def main() -> None:
disable_pdbp_color()
async with ( async with (
tractor.open_nursery( tractor.open_nursery(
debug_mode=True, debug_mode=True,
maybe_enable_greenback=True, maybe_enable_greenback=True,
enable_stack_on_sig=True,
# loglevel='warning', # XXX flags required for test pattern matching.
# loglevel='devx', loglevel='pdb',
# enable_stack_on_sig=True,
) as an, ) as an,
trio.open_nursery() as tn, trio.open_nursery() as tn,
): ):
@ -68,8 +84,8 @@ async def main() -> None:
p: tractor.Portal = await an.start_actor( p: tractor.Portal = await an.start_actor(
'subactor', 'subactor',
enable_modules=[__name__], enable_modules=[__name__],
# infect_asyncio=True,
debug_mode=True, debug_mode=True,
# infect_asyncio=True,
) )
# TODO: 3 sub-actor usage cases: # TODO: 3 sub-actor usage cases:

View File

@ -95,6 +95,8 @@ def spawn(
os.environ['PYTHON_COLORS'] = '0' os.environ['PYTHON_COLORS'] = '0'
# disable all ANSI color output # disable all ANSI color output
# os.environ['NO_COLOR'] = '1' # os.environ['NO_COLOR'] = '1'
# ?TODO, doesn't seem to disable prompt color
# for `pdbp`?
def set_spawn_method( def set_spawn_method(
start_method: str, start_method: str,

View File

@ -66,19 +66,28 @@ def test_pause_from_sync(
# XXX required for `breakpoint()` overload and # XXX required for `breakpoint()` overload and
# thus`tractor.devx.pause_from_sync()`. # thus`tractor.devx.pause_from_sync()`.
pytest.importorskip('greenback') pytest.importorskip('greenback')
child = spawn('sync_bp') child = spawn(
'sync_bp',
loglevel='pdb', # XXX pattern matching
)
# first `sync_pause()` after nurseries open # first `sync_pause()` after nurseries open
child.expect(PROMPT) child.expect(PROMPT)
assert_before( _before: str = assert_before(
child, child,
[ [
# pre-prompt line # devx-loglevel
_pause_msg, # "imported <module 'greenback' from",
"<Task '__main__.main'", # "successfully scheduled `._pause()` in `trio` thread on behalf of <Task",
_pause_msg, # pre-prompt line
"('root'", "('root'",
"<Task '__main__.main'",
"tractor.pause_from_sync()",
] ]
) )
# XXX `enable_stack_on_sig=False` in script
assert 'stackscope' not in _before
if ctlc: if ctlc:
do_ctlc(child) do_ctlc(child)
# ^NOTE^ subactor not spawned yet; don't need extra delay. # ^NOTE^ subactor not spawned yet; don't need extra delay.
@ -88,18 +97,18 @@ def test_pause_from_sync(
# first `await tractor.pause()` inside `p.open_context()` body # first `await tractor.pause()` inside `p.open_context()` body
child.expect(PROMPT) child.expect(PROMPT)
# XXX shouldn't see gb loaded message with PDB loglevel!
# assert not in_prompt_msg(
# child,
# ['`greenback` portal opened!'],
# )
# should be same root task # should be same root task
assert_before( assert_before(
child, child,
[ [
# XXX should see gb loaded with devx-loglevel.
# "`greenback` portal opened!",
# "Activated `greenback` for `tractor.pause_from_sync()` support!",
_pause_msg, _pause_msg,
"<Task '__main__.main'",
"('root'", "('root'",
"<Task '__main__.main'",
"tractor.pause()",
] ]
) )
@ -130,17 +139,17 @@ def test_pause_from_sync(
# `Lock.acquire()`-ed # `Lock.acquire()`-ed
# (NOT both, which will result in REPL clobbering!) # (NOT both, which will result in REPL clobbering!)
attach_patts: dict[str, list[str]] = { attach_patts: dict[str, list[str]] = {
'subactor': [ "|_<Task 'start_n_sync_pause'": [
"'start_n_sync_pause'", "|_('subactor'",
"('subactor'", "tractor.pause_from_sync()",
], ],
'inline_root_bg_thread': [ "|_<Thread(inline_root_bg_thread": [
"<Thread(inline_root_bg_thread",
"('root'", "('root'",
"breakpoint(hide_tb=hide_tb)",
], ],
'start_soon_root_bg_thread': [ "|_<Thread(start_soon_root_bg_thread": [
"<Thread(start_soon_root_bg_thread", "|_('root'",
"('root'", "tractor.pause_from_sync()",
], ],
} }
conts: int = 0 # for debugging below matching logic on failure conts: int = 0 # for debugging below matching logic on failure

View File

@ -181,7 +181,7 @@ class Lock:
return ( return (
f'<{cls.__name__}(\n' f'<{cls.__name__}(\n'
f'{body}' f'{body}'
')>\n\n' ')>\n'
) )
@classmethod @classmethod
@ -282,7 +282,7 @@ class Lock:
): ):
message += ( message += (
'-> No new task holds the TTY lock!\n\n' '-> No new task holds the TTY lock!\n\n'
f'{Lock.repr()}\n' f'{Lock.repr()}'
) )
elif ( elif (