Add tractor.pause_from_sync() using the amazing greenback! #1

Open
goodboy wants to merge 3 commits from pause_from_sync_w_greenback into mv_to_new_trio_py3.11

It’s been a super long outstanding task (with multiple “attempts” on my part) to support engaging the pdbp REPL from sync code whilst keeping all the benefits of our normal await tractor.pause() version.

Finally that PR has come brethren, and you shall debug from whatever func you so desire.


Desired feats before landing:

It's been a super long outstanding task (with multiple "attempts" on my part) to support engaging the `pdbp` REPL from sync code whilst keeping all the benefits of our normal `await tractor.pause()` version. *Finally that PR has come brethren, and you shall debug from whatever func you so desire.* --- #### Desired feats before landing: - [x] basic `tractor.pause_from_sync()` support in the root actor - [x] also in (nested) subactors.. => see the first draft of `examples/debugging/sync_bp.py` - [ ] use `greenback.with_portal_run[_sync]()` in/around `pause_from_sync()` ? (https://greenback.readthedocs.io/en/latest/reference.html#greenback.with_portal_run) - ideally we don't have to do any manual call to `.ensure_portal()` since we only are using this all for `.pause_from_sync()`? - [ ] **with non-main-thread support** such that you can leverage all the benefits of a multi-core debugger even from another `threading.Thread` in any subactor-process. => currently this does NOT work since we always override the `SIGINT` handler in sub-actors with a shielder func and in python this is only allowed from the "main thread": https://docs.python.org/3/library/signal.html#signals-and-threads - [ ] prolly implement this by checking the current thread's id and then using `trio.from_thread.run_sync()` to call `Lock.shield_sigint()` only when the current thread is not the main thread? - https://docs.python.org/3/library/threading.html#threading.get_ident - https://docs.python.org/3/library/threading.html#threading.main_thread - https://trio.readthedocs.io/en/stable/reference-core.html#trio.from_thread.run_sync - [ ] from `asyncio` tasks when using `.to_asyncio` and infected mode: - [ ] using both `.pause_from_sync()` and `breakpoint()` - [ ] ensure crashes in `asyncio` tasks engage correctly as well! - [ ] decide on `greenback` bootstrapping: - [ ] should it be an optional dep? - [ ] always override the `breakpoint()` hook when it **is** installed? - [ ] where is the best place to call `._rpc.maybe_import_gb()`? - [ ] when **not** installed how should we guard against `debug_mode=True` usage from sync code?
goodboy added 1 commit 2024-03-21 21:43:10 +00:00
goodboy added 2 commits 2024-03-24 20:47:40 +00:00
4f863a6989 Refine and test `tractor.pause_from_sync()`
Now supports use from any `trio` task, any sync thread started with
`trio.to_thread.run_sync()` AND also via `breakpoint()` builtin API!
The only bit missing now is support for `asyncio` tasks when in infected
mode.. Bo

`greenback` setup/API adjustments:
- move `._rpc.maybe_import_gb()` to -> `devx._debug` and factor out the cached
  import checking into a sync func whilst placing the async `.ensure_portal()`
  bootstrapping into a new async `maybe_init_greenback()`.
- use the new init-er func inside `open_root_actor()` with the output
  predicating whether we override the `breakpoint()` hook.

core `devx._debug` implementation deatz:
- make `mk_mpdb()` only return the `pdp.Pdb` subtype instance since
  the sigint unshielding func is now accessible from the `Lock`
  singleton from anywhere.

- add non-main thread support (at least for `trio.to_thread` use cases)
  to our `Lock` with a new `.is_trio_thread()` predicate that delegates
  directly to `trio`'s internal version.

- do `Lock.is_trio_thread()` checks inside any methods which require
  special provisions when invoked from a non-main `trio` thread:
  - `.[un]shield_sigint()` methods since `signal.signal` usage is only
    allowed from cpython's main thread.
  - `.release()` since `trio.StrictFIFOLock` can only be called from
    a `trio` task.

- rework `.pause_from_sync()` itself to directly call `._set_trace()`
  and don't bother with `greenback._await()` when we're already calling
  it from a `.to_thread.run_sync()` thread, oh and try to use the
  thread/task name when setting `Lock.local_task_in_debug`.

- make it an RTE for now if you try to use `.pause_from_sync()` from any
  infected-`asyncio` task, but support is (hopefully) coming soon!

For testing we add a new `test_debugger.py::test_pause_from_sync()`
which includes a ctrl-c parametrization around the
`examples/debugging/sync_bp.py` script which includes all currently
supported/working usages:
- `tractor.pause_from_sync()`.
- via `breakpoint()` overload.
- from a `trio.to_thread.run_sync()` spawn.
0055c1d954 Tweak main thread predicate to ensure `trio.run()`
Change the name to `Lock.is_main_trio_thread()` indicating that when
`True` the thread is both the main one **and** the one that called
`trio.run()`. Add a todo for just copying the
`trio._util.is_main_thread()` impl (since it's private / may change) and
some brief notes about potential usage of
`trio.from_thread.check_cancelled()` to detect non-`.to_thread` thread
spawns.
This pull request can be merged automatically.
You are not authorized to merge this pull request.
You can also view command line instructions.

Step 1:

From your project repository, check out a new branch and test the changes.
git checkout -b pause_from_sync_w_greenback mv_to_new_trio_py3.11
git pull origin pause_from_sync_w_greenback

Step 2:

Merge the changes and update on Gitea.
git checkout mv_to_new_trio_py3.11
git merge --no-ff pause_from_sync_w_greenback
git push origin mv_to_new_trio_py3.11
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: goodboy/tractor#1
There is no content yet.