Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wen? Multiprocessing-native debugger now! #129

Merged
merged 61 commits into from
Oct 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
8c97f7b
Create runtime variables
goodboy Jul 23, 2020
b11e913
Initial attempt at multi-actor debugging
goodboy Jul 23, 2020
b06d4b0
Add support for "debug mode"
goodboy Jul 23, 2020
1d1c881
WIP debugging test script
goodboy Jul 23, 2020
e7ee0fe
Pass a copy of the expected exposed modules
goodboy Jul 26, 2020
8eb9a74
Add multi-process debugging support using `pdbpp`
goodboy Jul 26, 2020
f7cd2be
Play with re-entrant trace
goodboy Jul 30, 2020
efd7095
Add pdbpp as dep
goodboy Jul 30, 2020
abaa2f5
Drop uneeded `parent_chan_cs()` cancel call
goodboy Jul 30, 2020
68773d5
Always expose the debug module
goodboy Jul 30, 2020
f9ef3fc
Cleanups and more comments
goodboy Jul 30, 2020
ebb21b9
Support re-entrant breakpoints
goodboy Aug 1, 2020
fd5fb92
Sparsen some lines
goodboy Aug 3, 2020
bd157e0
Port to service nursery
goodboy Aug 9, 2020
291ecec
Maybe not sticky by default
goodboy Aug 7, 2020
150179b
Support entering post mortem on crashes in root actor
goodboy Sep 12, 2020
8b6e9f5
Port to new debug api, set `_is_root` state flag on startup
goodboy Sep 12, 2020
09daba4
Explicitly handle `debug_mode` flag correctly
goodboy Sep 12, 2020
9e1d9a8
Add an internal context stack
goodboy Sep 24, 2020
f1b242f
Block SIGINT handling while in the debugger
goodboy Sep 28, 2020
363498b
Disable SIGINT handling in child processes
goodboy Sep 28, 2020
25e9392
Add a cancel scope around child debugger requests
goodboy Sep 28, 2020
5dd2d35
Huh, maybe we don't need to block SIGINT
goodboy Sep 28, 2020
d7a472c
Update our debugging example to wait on results
goodboy Sep 28, 2020
fc2cb61
Make "hard kill" just a `Process.terminate()`
goodboy Sep 28, 2020
29ed065
Ack our inability to hard kill sub-procs
goodboy Sep 28, 2020
0a2a94f
Add initial root actor debugger tests
goodboy Oct 3, 2020
9067bb2
Shorten arbiter contact timeout
goodboy Oct 3, 2020
73a32f7
Add initial subactor debug tests
goodboy Oct 4, 2020
a2151cd
Allow re-entrant breakpoints during pdb stepping
goodboy Oct 4, 2020
e387e8b
Add a multi-subactor test with nesting
goodboy Oct 4, 2020
83a4511
Add "root mailbox" contact info passing
goodboy Oct 4, 2020
31c1a32
Add re-entrant root breakpoint test; demonstrates a bug..
goodboy Oct 4, 2020
d43d367
Facepalm: tty locking from root doesn't require an extra task
goodboy Oct 5, 2020
3710259
Add a multi-subactor test where the root errors
goodboy Oct 5, 2020
2b53c74
Change to relative conftest.py imports
goodboy Oct 5, 2020
abf8bb2
Add a deep nested error propagation test
goodboy Oct 6, 2020
0711208
Add mention subactor uid during locking
goodboy Oct 7, 2020
acb4cb0
Add test showing issue with child in tty lock when cancelled
goodboy Oct 7, 2020
0e344ee
Add a "cancel arrives during a sync sleep in child" test
goodboy Oct 12, 2020
79c38b0
Report `trio.Cancelled` when exhausting portals..
goodboy Oct 13, 2020
a88a6ba
Add pattern matching to test
goodboy Oct 13, 2020
c41e5c8
Fix missing await
goodboy Oct 13, 2020
1710b64
Make tests a package (for relative imports)
goodboy Oct 13, 2020
c375a2d
mypy fixes
goodboy Oct 13, 2020
0ce6d2b
Add `pexpect` dep for debugger tests
goodboy Oct 13, 2020
573b8fe
Add better actor cancellation tracking
goodboy Oct 13, 2020
08ff989
Add some comments
goodboy Oct 13, 2020
24ef919
Skip sync sleep test on mp backend
goodboy Oct 13, 2020
ba52de7
Skip quad ex on local mp tests as well
goodboy Oct 13, 2020
e3c2694
Support debug mode only on the trio backend
goodboy Oct 13, 2020
6669660
Revert "Change to relative conftest.py imports"
goodboy Oct 13, 2020
a49deb4
Revert "Make tests a package (for relative imports)"
goodboy Oct 13, 2020
a934eb0
Factor `repodir()` helper into conftest.py
goodboy Oct 13, 2020
fd59f4a
On windows .spawn dne?
goodboy Oct 13, 2020
15edcc6
Skip it on windows too
goodboy Oct 13, 2020
1b6ee2e
Skip sync sleep test on windows
goodboy Oct 13, 2020
0177268
Report on skipped tests
goodboy Oct 13, 2020
1c25f25
Drop travisCI; it's slower and has worse windows support.
goodboy Oct 14, 2020
bba47e4
Add gh actions badge
goodboy Oct 14, 2020
61a8df3
Comments tweak
goodboy Oct 14, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ jobs:
- name: Install dependencies
run: pip install -U . -r requirements-test.txt -r requirements-docs.txt --upgrade-strategy eager
- name: Run tests
run: pytest tests/ --spawn-backend=${{ matrix.spawn_backend }}
run: pytest tests/ --spawn-backend=${{ matrix.spawn_backend }} -rs
68 changes: 0 additions & 68 deletions .travis.yml

This file was deleted.

7 changes: 4 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ tractor
=======
A `structured concurrent`_, async-native "`actor model`_" built on trio_ and multiprocessing_.

|travis| |docs|
|gh_actions|
|docs|

.. _actor model: https://en.wikipedia.org/wiki/Actor_model
.. _trio: https://github.com/python-trio/trio
Expand Down Expand Up @@ -54,8 +55,8 @@ say hi, please feel free to ping me on the `trio gitter channel`_!
.. _trio gitter channel: https://gitter.im/python-trio/general


.. |travis| image:: https://img.shields.io/travis/goodboy/tractor/master.svg
:target: https://travis-ci.org/goodboy/tractor
.. |gh_actions| image:: https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fgoodboy%2Ftractor%2Fbadge&style=popout-square
:target: https://actions-badge.atrox.dev/goodboy/tractor/goto
.. |docs| image:: https://readthedocs.org/projects/tractor/badge/?version=latest
:target: https://tractor.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import tractor


async def name_error():
"Raise a ``NameError``"
getattr(doggypants)


async def breakpoint_forever():
"Indefinitely re-enter debugger in child actor."
while True:
await tractor.breakpoint()


async def spawn_until(depth=0):
""""A nested nursery that triggers another ``NameError``.
"""
async with tractor.open_nursery() as n:
if depth < 1:
# await n.run_in_actor('breakpoint_forever', breakpoint_forever)
await n.run_in_actor('name_error', name_error)
else:
depth -= 1
await n.run_in_actor(f'spawn_until_{depth}', spawn_until, depth=depth)


async def main():
"""The main ``tractor`` routine.

The process tree should look as approximately as follows when the debugger
first engages:

python examples/debugging/multi_nested_subactors_bp_forever.py
โ”œโ”€ python -m tractor._child --uid ('spawner1', '7eab8462 ...)
โ”‚ โ””โ”€ python -m tractor._child --uid ('spawn_until_3', 'afcba7a8 ...)
โ”‚ โ””โ”€ python -m tractor._child --uid ('spawn_until_2', 'd2433d13 ...)
โ”‚ โ””โ”€ python -m tractor._child --uid ('spawn_until_1', '1df589de ...)
โ”‚ โ””โ”€ python -m tractor._child --uid ('spawn_until_0', '3720602b ...)
โ”‚
โ””โ”€ python -m tractor._child --uid ('spawner0', '1d42012b ...)
โ””โ”€ python -m tractor._child --uid ('spawn_until_2', '2877e155 ...)
โ””โ”€ python -m tractor._child --uid ('spawn_until_1', '0502d786 ...)
โ””โ”€ python -m tractor._child --uid ('spawn_until_0', 'de918e6d ...)

"""
async with tractor.open_nursery() as n:

# spawn both actors
portal = await n.run_in_actor('spawner0', spawn_until, depth=3)
portal1 = await n.run_in_actor('spawner1', spawn_until, depth=4)

# gah still an issue here.
# await portal.result()
# await portal1.result()


if __name__ == '__main__':
tractor.run(main, debug_mode=True)
43 changes: 43 additions & 0 deletions examples/debugging/multi_subactor_root_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import tractor


async def name_error():
"Raise a ``NameError``"
getattr(doggypants)


async def spawn_error():
""""A nested nursery that triggers another ``NameError``.
"""
async with tractor.open_nursery() as n:
portal = await n.run_in_actor('name_error_1', name_error)
return await portal.result()


async def main():
"""The main ``tractor`` routine.

The process tree should look as approximately as follows:

python examples/debugging/multi_subactors.py
โ”œโ”€ python -m tractor._child --uid ('name_error', 'a7caf490 ...)
`-python -m tractor._child --uid ('spawn_error', '52ee14a5 ...)
`-python -m tractor._child --uid ('name_error', '3391222c ...)
"""
async with tractor.open_nursery() as n:

# spawn both actors
portal = await n.run_in_actor('name_error', name_error)
portal1 = await n.run_in_actor('spawn_error', spawn_error)

# trigger a root actor error
assert 0

# attempt to collect results (which raises error in parent)
# still has some issues where the parent seems to get stuck
await portal.result()
await portal1.result()


if __name__ == '__main__':
tractor.run(main, debug_mode=True)
47 changes: 47 additions & 0 deletions examples/debugging/multi_subactors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import tractor
import trio


async def breakpoint_forever():
"Indefinitely re-enter debugger in child actor."
while True:
await trio.sleep(0.1)
await tractor.breakpoint()


async def name_error():
"Raise a ``NameError``"
getattr(doggypants)


async def spawn_error():
""""A nested nursery that triggers another ``NameError``.
"""
async with tractor.open_nursery() as n:
portal = await n.run_in_actor('name_error_1', name_error)
return await portal.result()


async def main():
"""The main ``tractor`` routine.

The process tree should look as approximately as follows:

-python examples/debugging/multi_subactors.py
|-python -m tractor._child --uid ('name_error', 'a7caf490 ...)
|-python -m tractor._child --uid ('bp_forever', '1f787a7e ...)
`-python -m tractor._child --uid ('spawn_error', '52ee14a5 ...)
`-python -m tractor._child --uid ('name_error', '3391222c ...)
"""
async with tractor.open_nursery() as n:

# Spawn both actors, don't bother with collecting results
# (would result in a different debugger outcome due to parent's
# cancellation).
await n.run_in_actor('bp_forever', breakpoint_forever)
await n.run_in_actor('name_error', name_error)
await n.run_in_actor('spawn_error', spawn_error)


if __name__ == '__main__':
tractor.run(main, debug_mode=True)
15 changes: 15 additions & 0 deletions examples/debugging/root_actor_breakpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import trio
import tractor


async def main():

await trio.sleep(0.1)

await tractor.breakpoint()

await trio.sleep(0.1)


if __name__ == '__main__':
tractor.run(main, debug_mode=True)
11 changes: 11 additions & 0 deletions examples/debugging/root_actor_breakpoint_forever.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import tractor


async def main():

while True:
await tractor.breakpoint()


if __name__ == '__main__':
tractor.run(main, debug_mode=True)
9 changes: 9 additions & 0 deletions examples/debugging/root_actor_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import tractor


async def main():
assert 0


if __name__ == '__main__':
tractor.run(main, debug_mode=True)
48 changes: 48 additions & 0 deletions examples/debugging/root_cancelled_but_child_is_in_tty_lock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import tractor


async def name_error():
"Raise a ``NameError``"
getattr(doggypants)


async def spawn_until(depth=0):
""""A nested nursery that triggers another ``NameError``.
"""
async with tractor.open_nursery() as n:
if depth < 1:
# await n.run_in_actor('breakpoint_forever', breakpoint_forever)
await n.run_in_actor('name_error', name_error)
else:
depth -= 1
await n.run_in_actor(f'spawn_until_{depth}', spawn_until, depth=depth)


async def main():
"""The main ``tractor`` routine.

The process tree should look as approximately as follows when the debugger
first engages:

python examples/debugging/multi_nested_subactors_bp_forever.py
โ”œโ”€ python -m tractor._child --uid ('spawner1', '7eab8462 ...)
โ”‚ โ””โ”€ python -m tractor._child --uid ('spawn_until_0', '3720602b ...)
โ”‚ โ””โ”€ python -m tractor._child --uid ('name_error', '505bf71d ...)
โ”‚
โ””โ”€ python -m tractor._child --uid ('spawner0', '1d42012b ...)
โ””โ”€ python -m tractor._child --uid ('name_error', '6c2733b8 ...)

"""
async with tractor.open_nursery() as n:

# spawn both actors
portal = await n.run_in_actor('spawner0', spawn_until, depth=0)
portal1 = await n.run_in_actor('spawner1', spawn_until, depth=1)

# nursery cancellation should be triggered due to propagated error
await portal.result()
await portal1.result()


if __name__ == '__main__':
tractor.run(main, debug_mode=True, loglevel='warning')
25 changes: 25 additions & 0 deletions examples/debugging/subactor_breakpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import trio
import tractor


async def breakpoint_forever():
"""Indefinitely re-enter debugger in child actor.
"""
while True:
await trio.sleep(0.1)
await tractor.breakpoint()


async def main():

async with tractor.open_nursery() as n:

portal = await n.run_in_actor(
'breakpoint_forever',
breakpoint_forever,
)
await portal.result()


if __name__ == '__main__':
tractor.run(main, debug_mode=True)
16 changes: 16 additions & 0 deletions examples/debugging/subactor_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import tractor


async def name_error():
getattr(doggypants)


async def main():
async with tractor.open_nursery() as n:

portal = await n.run_in_actor('name_error', name_error)
await portal.result()


if __name__ == '__main__':
tractor.run(main, debug_mode=True)
1 change: 1 addition & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pytest-trio
pdbpp
mypy
trio_typing
pexpect
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
],
install_requires=[
'msgpack', 'trio>0.8', 'async_generator', 'colorlog', 'wrapt',
'trio_typing'
'trio_typing', 'pdbpp',
Copy link
Owner Author

@goodboy goodboy Jul 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually required as a dependency since we needed something where we could hook into the debugger's "tear down" (i.e. where a user releases the debugger and let's code run again using either of the continue or quit commands).

The main requirement for this is tty locking in the root actor such that no two subactors can use it simultaneously.

],
tests_require=['pytest'],
python_requires=">=3.7",
Expand Down
Loading