Fix review nits from PR #460 self-review

Address the actionable findings from the `/code-review` pass
(#1-6); the d2-ext + docstring nits (#7-10) are left for a
follow-up.

Deats,
- `uds_transport_actor_tree.py`: `portal.chan.raddr.sockpath` is
  the *shared listener* socket (named for the root registrar), NOT
  the child's path — relabel it + lead with the per-child peer pid,
  and stop claiming it's the child addr in the docstring,
- `docs.yml`: scope the `pages` `concurrency` group to the `deploy`
  job w/ `cancel-in-progress: false` so a PR build can't cancel an
  in-flight production deploy,
- `architecture.rst`: `'subint'` is not a selectable `start_method`
  on this branch (`SpawnMethodKey` lacks it) — reframe as
  in-development/roadmap,
- `context.rst`: `StreamOverrun` isn't re-exported from `tractor`;
  point at `tractor._exceptions`,
- `debugging/`: sweep the 3 literalinclude'd examples off the
  deprecated `.result()` -> `.wait_for_result()`,
- `quickstart.rst`: proc-title is `name@pid` (per `Aid.reprol`),
  not `@uuid`.

NOTE, the `debugging/` examples are pexpect-tested; re-run
`tests/devx/test_debugger.py` for them.

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Gud Boi 2026-06-25 17:07:44 -04:00
parent ea982875a5
commit 37e7470f26
8 changed files with 32 additions and 25 deletions

View File

@ -17,11 +17,6 @@ permissions:
pages: write pages: write
id-token: write id-token: write
# never run >1 pages deploy at once
concurrency:
group: 'pages'
cancel-in-progress: true
jobs: jobs:
build: build:
name: 'sphinx build' name: 'sphinx build'
@ -52,6 +47,12 @@ jobs:
name: 'deploy to gh-pages' name: 'deploy to gh-pages'
if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }} if: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
needs: build needs: build
# serialize deploys but NEVER cancel an in-flight one
# mid-upload; queue the next instead (scoped to this job so
# PR builds can't cancel a production deploy).
concurrency:
group: 'pages'
cancel-in-progress: false
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: environment:
name: github-pages name: github-pages

View File

@ -192,13 +192,14 @@ spawn backend, selected with the ``start_method`` kwarg to
names (forkserver is posix-only). Mostly interesting for names (forkserver is posix-only). Mostly interesting for
ecosystem compat and start-up-latency tuning. ecosystem compat and start-up-latency tuning.
``'subint'`` (experimental, py3.14+) ``'subint'`` (in development, py3.14+)
Each actor runs as a `PEP 734`_ sub-interpreter On the roadmap (not yet selectable via ``start_method`` on
(``concurrent.interpreters``) driven on its own OS thread this release): run each actor as a `PEP 734`_
*inside the parent process*: interpreter-level sub-interpreter (``concurrent.interpreters``) driven on its
shared-nothing isolation with much faster start-up. Yes, own OS thread *inside the parent process* — interpreter-level
this bends the one-actor-one-process rule; the rest of the shared-nothing isolation with much faster start-up. Yes, this
model is unchanged. bends the one-actor-one-process rule; the rest of the model
is unchanged.
The ``TRACTOR_SPAWN_METHOD`` env-var beats any caller-passed The ``TRACTOR_SPAWN_METHOD`` env-var beats any caller-passed
``start_method``, so you can swap backends under an unmodified ``start_method``, so you can swap backends under an unmodified

View File

@ -302,9 +302,9 @@ Overruns and backpressure
Stream msgs land in a bounded per-context buffer on the receiver Stream msgs land in a bounded per-context buffer on the receiver
side. A sender that outpaces a non-consuming receiver *overruns* side. A sender that outpaces a non-consuming receiver *overruns*
it and the runtime raises :exc:`tractor.StreamOverrun` (also a it and the runtime raises ``StreamOverrun`` (from
:exc:`trio.TooSlowError`) instead of buffering without bound — SC ``tractor._exceptions``; also a :exc:`trio.TooSlowError`) instead
discipline applies to memory too. of buffering without bound — SC discipline applies to memory too.
Your knobs: Your knobs:

View File

@ -141,7 +141,7 @@ breakfast - run this while monitoring your process tree::
Every subactor (best-effort, via the optional Every subactor (best-effort, via the optional
``setproctitle`` dep) re-titles its OS process like ``setproctitle`` dep) re-titles its OS process like
``_subactor[worker_0@<uuid>]``, so ``pstree``/``htop``/ ``_subactor[worker_0@<pid>]``, so ``pstree``/``htop``/
``pgrep -f`` can tell your actors apart at a glance. ``pgrep -f`` can tell your actors apart at a glance.
You'll see something like:: You'll see something like::

View File

@ -78,7 +78,7 @@ async def main():
portal: tractor.Portal = await n.run_in_actor( portal: tractor.Portal = await n.run_in_actor(
cancelled_before_pause, cancelled_before_pause,
) )
await portal.result() await portal.wait_for_result()
# ensure the same works in the root actor! # ensure the same works in the root actor!
await pm_on_cancelled() await pm_on_cancelled()

View File

@ -22,7 +22,7 @@ async def main():
portal = await n.run_in_actor( portal = await n.run_in_actor(
breakpoint_forever, breakpoint_forever,
) )
await portal.result() await portal.wait_for_result()
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -18,10 +18,10 @@ async def main():
p: tractor.Portal = await an.run_in_actor(name_error) p: tractor.Portal = await an.run_in_actor(name_error)
# with this style, should raise on this line # with this style, should raise on this line
await p.result() await p.wait_for_result()
# with this alt style should raise at `open_nusery()` # with this alt style should raise at `open_nusery()`
# return await p.result() # return await p.wait_for_result()
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -4,9 +4,9 @@ Demonstrate an actor tree which talks over unix-domain-socket
`enable_transports=['uds']` when opening the root and every `enable_transports=['uds']` when opening the root and every
subactor inherits the preference. subactor inherits the preference.
The child's channel address is a filesystem socket path (no Every channel address is a filesystem socket path (no TCP port
TCP port in sight!) and, as a kernel-provided bonus, the in sight!) and, as a kernel-provided bonus, the peer's pid is
peer's pid is exchanged for free via `SO_PEERCRED`. exchanged for free via `SO_PEERCRED`.
''' '''
import os import os
@ -38,12 +38,17 @@ async def main() -> None:
# filesystem socket path, NOT a (host, port) pair! # filesystem socket path, NOT a (host, port) pair!
raddr = portal.chan.raddr raddr = portal.chan.raddr
assert raddr.proto_key == 'uds' assert raddr.proto_key == 'uds'
# NOTE, `.sockpath` is the *shared listener* socket file
# (named for the root registrar) this channel rode in
# on, NOT a per-child path; the child-specific identity
# we get for free is the kernel-reported peer pid (via
# `SO_PEERCRED`).
print( print(
f'portal chan tpt proto: {raddr.proto_key!r}\n' f'portal chan tpt proto: {raddr.proto_key!r}\n'
f'portal chan sock file: {raddr.sockpath}\n' f'listener sock file: {raddr.sockpath}\n'
f'kernel-reported peer pid: {raddr.maybe_pid}\n' f'kernel-reported peer pid: {raddr.maybe_pid}\n'
) )
# ask the child for its own bind addr: also a # ask the child for its OWN distinct bind addr: another
# socket-file path under the runtime dir. # socket-file path under the runtime dir.
print(f'child says: {await portal.run(report_addr)}') print(f'child says: {await portal.run(report_addr)}')
await portal.cancel_actor() await portal.cancel_actor()