Commit Graph

1650 Commits (modden_spawn_from_client_req)

Author SHA1 Message Date
Tyler Goodlet 84171c2d10 Unmask `pytest.ini` log-capture lines (again) 2025-03-18 13:41:45 -04:00
Tyler Goodlet 1dd9c4ae75 More spaceless union type annots 2025-03-18 13:37:09 -04:00
Tyler Goodlet 8dd7bc0ba9 Add a `._state._runtime_vars['_registry_addrs']`
Such that it's set to whatever `Actor.reg_addrs: list[tuple]` is during
the actor's init-after-spawn guaranteeing each actor has at least the
registry infos from its parent. Ensure we read this if defined over
`_root._default_lo_addrs` in `._discovery` routines, namely
`.find_actor()` since it's the one API normally used without expecting
the runtime's `current_actor()` to be up.

Update the latest inter-peer cancellation test to use the `reg_addr`
fixture (and thus test this new runtime-vars value via `find_actor()`
usage) since it was failing if run *after* the infected `asyncio` suite
due to registry contact failure.
2025-03-18 13:37:09 -04:00
Tyler Goodlet aaba298361 `_root`: drop unused `typing` import 2025-03-18 13:34:34 -04:00
Tyler Goodlet 7ceeae9390 Use `import <name> as <name>,` style over `__all__` in pkg mod 2025-03-18 13:34:34 -04:00
Tyler Goodlet 8b74d9ff51 Log chan-server-startup failures via `.exception()` 2025-03-18 13:34:34 -04:00
Tyler Goodlet 75522c80b8 `.discovery.get_arbiter()`: add warning around this now deprecated usage 2025-03-18 13:34:34 -04:00
Tyler Goodlet 7eea1ec61a Add `open_root_actor(ensure_registry: bool)`
Allows forcing the opened actor to either obtain the passed registry
addrs or raise a runtime error.
2025-03-18 13:34:34 -04:00
Tyler Goodlet 5892d15579 Fix doc string "its" typo.. 2025-03-18 13:34:34 -04:00
Tyler Goodlet 382fd91bf3 Test with `any(portals)` since `gather_contexts()` will return `list[None | tuple]` 2025-03-18 13:34:34 -04:00
Tyler Goodlet 1a1f136c85 Change remaining internals to use `Actor.reg_addrs` 2025-03-18 13:34:34 -04:00
Tyler Goodlet 1d01fa16b1 Expose per-actor registry addrs via `.reg_addrs`
Since it's handy to be able to debug the *writing* of this instance var
(particularly when checking state passed down to a child in
`Actor._from_parent()`), rename and wrap the underlying
`Actor._reg_addrs` as a settable `@property` and add validation to
the `.setter` for sanity - actor discovery is a critical functionality.

Other tweaks:
- fix `.cancel_soon()` to pass expected argument..
- update internal runtime error message to be simpler and link to GH issues.
- use new `Actor.reg_addrs` throughout core.
2025-03-18 13:34:34 -04:00
Tyler Goodlet 9f126a476a Get remaining suites passing..
..by ensuring `reg_addr` fixture value passthrough to subactor eps
2025-03-18 13:34:34 -04:00
Tyler Goodlet ff7dfa9b8e Always dynamically re-read the `._root._default_lo_addrs` value in `find_actor()` 2025-03-18 13:34:34 -04:00
Tyler Goodlet 85e677bf3b Ensure `registry_addrs` is always set to something 2025-03-18 13:34:34 -04:00
Tyler Goodlet 2e5ba84270 Rename fixture `arb_addr` -> `reg_addr` and set the session value globally as `._root._default_lo_addrs` 2025-03-18 13:34:34 -04:00
Tyler Goodlet a8c0fc3b79 Facepalm, `wait_for_actor()` dun take an addr `list`.. 2025-03-18 13:34:34 -04:00
Tyler Goodlet 99ccab7574 ._root: set a `_default_lo_addrs` and apply it when not provided by caller 2025-03-18 13:34:34 -04:00
Tyler Goodlet 12211c8295 Always set default reg addr in `find_actor()` if not defined 2025-03-18 13:34:34 -04:00
Tyler Goodlet 63aeac8d4c Oof, default reg addrs needs to be in `list[tuple]` form.. 2025-03-18 13:34:34 -04:00
Tyler Goodlet 7951251c47 Add post-mortem catch around failed transport addr binds to aid with runtime debugging 2025-03-18 13:34:34 -04:00
Tyler Goodlet 07bc675f5e Rename to `parse_maddr()` and fill out doc strings 2025-03-18 13:34:34 -04:00
Tyler Goodlet 4f7d1ea704 Add libp2p style "multi-address" parser from `piker`
Details are in the module docs; this is a first draft with lotsa room
for refinement and extension.
2025-03-18 13:34:34 -04:00
Tyler Goodlet 47d52f3cc0 Init-support for "multi homed" transports
Since we'd like to eventually allow a diverse set of transport
(protocol) methods and stacks, and a multi-peer discovery system for
distributed actor-tree applications, this reworks all runtime internals
to support multi-homing for any given tree on a logical host. In other
words any actor can now bind its transport server (currently only
unsecured TCP + `msgspec`) to more then one address available in its
(linux) network namespace. Further, registry actors (now dubbed
"registars" instead of "arbiters") can also similarly bind to multiple
network addresses and provide discovery services to remote actors via
multiple addresses which can now be provided at runtime startup.

Deats:
- adjust `._runtime` internals to use a `list[tuple[str, int]]` (and
  thus pluralized) socket address sequence where applicable for transport
  server socket binds, now exposed via `Actor.accept_addrs`:
  - `Actor.__init__()` now takes a `registry_addrs: list`.
  - `Actor.is_arbiter` -> `.is_registrar`.
  - `._arb_addr` -> `._reg_addrs: list[tuple]`.
  - always reg and de-reg from all registrars in `async_main()`.
  - only set the global runtime var `'_root_mailbox'` to the loopback
    address since normally all in-tree processes should have access to
    it, right?
  - `._serve_forever()` task now takes `listen_sockaddrs: list[tuple]`
- make `open_root_actor()` take a `registry_addrs: list[tuple[str, int]]`
  and defaults when not passed.
- change `ActorNursery.start_..()` methods take `bind_addrs: list` and
  pass down through the spawning layer(s) via the parent-seed-msg.
- generalize all `._discovery()` APIs to accept `registry_addrs`-like
  inputs and move all relevant subsystems to adopt the "registry" style
  naming instead of "arbiter":
  - make `find_actor()` support batched concurrent portal queries over
    all provided input addresses using `.trionics.gather_contexts()` Bo
  - syntax: move to using `async with <tuples>` 3.9+ style chained
    @acms.
  - a general modernization of the code to a python 3.9+ style.
  - start deprecation and change to "registry" naming / semantics:
    - `._discovery.get_arbiter()` -> `.get_registry()`
2025-03-18 13:34:34 -04:00
Tyler Goodlet d0876bb0a4 Yahh, add `.devx` package to installed subpkgs.. 2025-03-18 13:32:59 -04:00
Tyler Goodlet 850b9999ff Add `stackscope` as dep, drop legacy `pdb` issue cruft 2025-03-18 13:32:59 -04:00
Tyler Goodlet 4571b8cc84 Enable `stackscope` render via root in debug mode
If `stackscope` is importable and debug_mode is enabled then we by
default call and report `.devx.enable_stack_on_sig()` is set B)

This makes debugging unexpected (SIGINT ignoring) hangs a cinch!
2025-03-18 13:32:59 -04:00
Tyler Goodlet db58f6e1b5 Woops, fix `_post_mortem()` type sig..
We're passing a `extra_frames_up_when_async=2` now (from prior attempt
to hide `CancelScope.__exit__()` when `shield=True`) and thus both
`debug_func`s must accept it 🤦

On the brighter side found out that the `TypeError` from the call-sig
mismatch was actually being swallowed entirely so add some
`.exception()` msgs for such cases to at least alert the dev they broke
stuff XD
2025-03-16 23:24:52 -04:00
Tyler Goodlet 76b7006977 Add `shield: bool` support to `.pause()`
It's been on the todo for a while and I've given up trying to properly
hide the `trio.CancelScope.__exit__()` frame for now instead opting to
just `log.pdb()` a big apology XD

Users can obvi still just not use the flag and wrap `tractor.pause()` in
their own cs block if they want to avoid having to hit `'up'` in the pdb
REPL if needed in a cancelled task-scope.

Impl deatz:
- factor orig `.pause()` impl into new `._pause()` so that we can more tersely
  wrap the original content depending on `shield: bool` input; only open
  the cancel-scope when shield is set to avoid aforemented extra strack
  frame annoyance.
- pass through `shield` to underlying `_pause` and `debug_func()` so we
  can actually know when so log our apology.
- add a buncha notes to new `.pause()` wrapper regarding the inability
  to hide the cancel-scope `.__exit__()`, inluding that overriding the
  code in `trio._core._run.CancelScope` doesn't seem to solve the issue
  either..

Unrelated `maybe_wait_for_debugger()` tweaks:
- don't read `Lock.global_actor_in_debug` more then needed, rename local
  read var to `in_debug` (since it can also hold the root actor uid, not
  just sub-actors).
- shield the `await debug_complete.wait()` since ideally we avoid the
  root cancellation child-actors in debug even when the root calls this
  func in a cancelled scope.
2025-03-16 23:24:52 -04:00
Tyler Goodlet bd1885bce1 Mk debugger tests work for arbitrary pre-REPL format
Since this was changed as part of overall project wide logging format
updates, and i ended up changing the both the crash and pause `.pdb()`
msgs to include some multi-line-ascii-"stuff", might as well make the
pre-prompt checks in the test suite more flexible to match.

As such, this exposes 2 new constants inside the `.devx._debug` mod:
- `._pause_msg: str` for the pre `tractor.pause()` header emitted via
  `log.pdb()` and,
- `._crash_msg: str` for the pre `._post_mortem()` equiv when handling
  errors in debug mode.

Adjust the test suite to use these values and thus make us more capable
to absorb changes in the future as well:
- add a new `in_prompt_msg()` predicate, very similar to `assert_before()`
  but minus `assert`s which takes in a `parts: list[str]` to match
  in the pre-prompt stdout.
- delegate to `in_prompt_msg()` in `assert_before()` since it was mostly
  duplicate minus `assert`.
- adjust all previous `<patt> in before` asserts to instead use
  `in_prompt_msg()` with separated pre-prompt-header vs. actor-name
  `parts`.
- use new `._pause/crash_msg` values in all such calls including any
  `assert_before()` cases.
2025-03-16 23:24:51 -04:00
Tyler Goodlet 066a35322e Support `maybe_wait_for_debugger(header_msg: str)`
Allow callers to stick in a header to the `.pdb()` level emitted msg(s)
such that any "waiting status" content is only shown if the caller
actually get's blocked waiting for the debug lock; use it inside the
`._spawn` sub-process reaper call.

Also, return early if `Lock.global_actor_in_debug == None` and thus
only enter the poll loop when actually needed, consequently raise
if we fall through the loop without acquisition.
2025-03-16 23:22:40 -04:00
Tyler Goodlet 2ebc30d708 Fix `.devx.maybe_wait_for_debugger()` polling deats
When entered by the root actor avoid excessive polling cycles by,
- blocking on the `Lock.no_remote_has_tty: trio.Event` and breaking
  *immediately* when set (though we should really also lock
  it from the root right?) to avoid extra loops..
- shielding the `await trio.sleep(poll_delay)` call to avoid any local
  cancellation causing the (presumably root-actor task) caller to move
  on (possibly to cancel its children) and instead to continue
  poll-blocking until the lock is actually released by its user.
- `break` the poll loop immediately if no remote locker is detected.
- use `.pdb()` level for reporting lock state changes.

Also add a #TODO to handle calls by non-root actors as it pertains to
2025-03-16 23:22:40 -04:00
Tyler Goodlet 57a5b7eb6f Add `stackscope` tree pprinter triggered by SIGUSR1
Can be optionally enabled via a new `enable_stack_on_sig()` which will
swap in the SIGUSR1 handler. Much thanks to @oremanj for writing this
amazing project, it's thus far helped me fix some very subtle hangs
inside our new IPC-context cancellation machinery that would have
otherwise taken much more manual pdb-ing and hair pulling XD

Full credit for `dump_task_tree()` goes to the original project author
with some minor tweaks as was handed to me via the trio-general matrix
room B)

Slight changes from orig version:
- use a `log.pdb()` emission to pprint to console
- toss in an ex sh CLI cmd to trigger the dump from another terminal
  using `kill` + `pgrep`.
2025-03-16 23:22:40 -04:00
Tyler Goodlet e269aa3751 Only use `greenback` if actor-runtime is up.. 2025-03-16 23:05:15 -04:00
Tyler Goodlet 7fc9297104 Ignore `greenback` import error if not installed 2025-03-16 23:05:15 -04:00
Tyler Goodlet 9208708b3a Change old `._debug._pause()` name, cherry to #362 re `greenback` 2025-03-16 23:05:15 -04:00
Tyler Goodlet cf2f2adec2 Runtime import `.get_root()` in stdin hijacker to avoid import cycle 2025-03-16 23:05:15 -04:00
Tyler Goodlet f28abc6720 Ignore kbis in `open_crash_handler()` by default 2025-03-16 23:05:15 -04:00
Tyler Goodlet 6f33a9891e Comment all `.pause(shield=True)` attempts again, need to solve cancel scope `.__exit__()` frame hiding issue.. 2025-03-16 23:05:15 -04:00
Tyler Goodlet 79604b7f98 Add shielding support to `.pause()`
Implement it like you'd expect using simply a wrapping
`trio.CancelScope` which is itself shielded by the input `shield: bool`
B)

There's seemingly still some issues with the frame selection when the
REPL engages and not sure how to resolve it yet but at least this does
indeed work for practical purposes. Still needs a test obviously!
2025-03-16 23:05:15 -04:00
Tyler Goodlet cec4a2a0ab Move `maybe_open_crash_handler()` CLI `--pdb`-driven wrapper to debug mod 2025-03-16 23:05:15 -04:00
Tyler Goodlet 4089e4b3ac Start `.devx.cli` extensions for pop CLI frameworks
Starting of with just a `typer` (and thus transitively `click`)
`typer.Typer.callback` hook which allows passthrough of the `--ll
<loglevel: str>` and `--pdb <debug_mode: bool>` flags for use when
building CLIs that use the runtime Bo

Still needs lotsa refinement and obviously better docs but, the doc
string for `load_runtime_vars()` shows how to use the underlying
`.devx._debug.open_crash_handler()` via a wrapper that can be passed the
`--pdb` flag and then enable debug mode throughout the entire actor
system.
2025-03-16 23:05:15 -04:00
Tyler Goodlet 5ec48310b6 Kick off `.devx` subpkg for our dev tools B)
Where `.devx` is "developer experience", a hopefully broad enough subpkg
name for all the slick stuff planned to augment working on the actor
runtime 💥

Move the `._debug` module into the new subpkg and adjust rest of core
code base to reflect import path change. Also add a new
`.devx._debug.open_crash_handler()` manager for wrapping any sync code
outside a `trio.run()` which is handy for eventual CLI addons for
popular frameworks like `click`/`typer`.
2025-03-16 23:05:14 -04:00
Tyler Goodlet 697900deb1 Expose `Channel` @ pkg level, drop `_debug.pp()` alias 2025-03-16 21:49:46 -04:00
Tyler Goodlet 2e55c124b1 Move `.to_asyncio` to modern optional value type annots 2025-03-16 21:49:46 -04:00
Tyler Goodlet 0f21c8ba6a Map `breakpoint()` built-in to new `.pause_from_sync()` ep 2025-03-16 21:48:54 -04:00
Tyler Goodlet 7b7410bc0f Fix frame-selection display on first REPL entry
For whatever reason pdb(p), and in general, will show the frame of the
*next* python instruction/LOC on initial entry (at least using
`.set_trace()`), as such remove the `try/finally` block in the sync
code entrypoint `.pause_from_sync()`, and also since doesn't seem like
we really need it anyway.

Further, and to this end:
- enable hidden frames support in our default config.
- fix/drop/mask all the frame ref-ing/mangling we had prior since it's no
  longer needed as well as manual `Lock` releasing which seems to work
  already by having the `greenback` spawned task do it's normal thing?
- move to no `Union` type annots.
- hide all frames that can add "this is the runtime confusion" to
  traces.
2025-03-16 21:45:00 -04:00
Tyler Goodlet b59cba74cd ._runtime: log level tweaks, use crit for stale debug lock detection 2025-03-16 21:44:58 -04:00
Tyler Goodlet 7e39ef7ed1 Add longer "required reading" list B) 2025-03-16 21:42:41 -04:00
Tyler Goodlet c8ea0fdf53 First proto: use `greenback` for sync func breakpointing
This works now for supporting a new `tractor.pause_from_sync()`
`tractor`-aware-replacement for `Pdb.set_trace()` from sync functions
which are also scheduled from our runtime. Uses `greenback` to do all
the magic of scheduling the bg `tractor._debug._pause()` task and
engaging the normal TTY locking machinery triggered by `await
tractor.breakpoint()`

Further this starts some public API renaming, making a switch to
`tractor.pause()` from `.breakpoint()` which IMO much better expresses
the semantics of the runtime intervention required to suffice
multi-process "breakpointing"; it also is an alternate name for the same
in computer science more generally: https://en.wikipedia.org/wiki/Breakpoint
It also avoids using the same name as the `breakpoint()` built-in which
is important since there **is alot more going on** when you call our
equivalent API.

Deats of that:
- add deprecation warning for `tractor.breakpoint()`
- add `tractor.pause()` and a shorthand, easier-to-type, alias `.pp()`
  for "pause-point" B)
- add `pause_from_sync()` as the new `breakpoint()`-from-sync-function
  hack which does all the `greenback` stuff for the user.

Still TODO:
- figure out where in the runtime and when to call
  `greenback.ensure_portal()`.
- fix the frame selection issue where
  `trio._core._ki._ki_protection_decorator:wrapper` seems to be always
  shown on REPL start as the selected frame..
2025-03-16 21:42:30 -04:00