Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: goodboy/tractor
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: cc18c84389a24da79e0236aeb4a24a95ba18ac19
Choose a base ref
..
head repository: goodboy/tractor
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7dd72e042d8f7811a22a264457c6b0accfefa863
Choose a head ref
Showing with 284 additions and 208 deletions.
  1. +13 −22 .github/workflows/ci.yml
  2. +2 −0 MANIFEST.in
  3. +8 −10 docs/README.rst
  4. +1 −0 examples/infected_asyncio_echo_server.py
  5. +5 −0 nooz/316.misc.rst
  6. +8 −0 nooz/317.misc.rst
  7. +13 −0 nooz/318.bug.rst
  8. +12 −16 setup.py
  9. +92 −3 tests/test_infected_asyncio.py
  10. +69 −128 tractor/_ipc.py
  11. +61 −29 tractor/to_asyncio.py
35 changes: 13 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -28,37 +28,28 @@ jobs:
- name: Run MyPy check
run: mypy tractor/ --ignore-missing-imports

testing-linux:
name: '${{ matrix.os }} Python ${{ matrix.python }} - ${{ matrix.spawn_backend }}'
timeout-minutes: 10
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python: ['3.9', '3.10']
spawn_backend: ['trio', 'mp']
sdist-linux:
name: 'sdist'
runs-on: ubuntu-latest

steps:

- name: Checkout
uses: actions/checkout@v2

- name: Setup python
uses: actions/setup-python@v2
with:
python-version: '${{ matrix.python }}'
python-version: '3.10'

- name: Install dependencies
run: pip install -U . -r requirements-test.txt -r requirements-docs.txt --upgrade-strategy eager
- name: Build sdist
run: python setup.py sdist --formats=zip

- name: Install sdist from .zips
run: python -m pip install dist/*.zip

- name: Run tests
run: pytest tests/ --spawn-backend=${{ matrix.spawn_backend }} -rs

testing-linux-msgspec:
# runs jobs on all OS's but with optional `msgspec` dep installed
name: '${{ matrix.os }} Python ${{ matrix.python }} - ${{ matrix.spawn_backend }} - msgspec'
testing-linux:
name: '${{ matrix.os }} Python ${{ matrix.python }} - ${{ matrix.spawn_backend }}'
timeout-minutes: 10
runs-on: ${{ matrix.os }}

@@ -80,7 +71,7 @@ jobs:
python-version: '${{ matrix.python }}'

- name: Install dependencies
run: pip install -U .[msgspec] -r requirements-test.txt -r requirements-docs.txt --upgrade-strategy eager
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 }} -rs
@@ -103,7 +94,7 @@ jobs:
fail-fast: false
matrix:
os: [windows-latest]
python: ['3.9']
python: ['3.9', '3.10']
spawn_backend: ['trio', 'mp']

steps:
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# https://packaging.python.org/en/latest/guides/using-manifest-in/#using-manifest-in
include docs/README.rst
18 changes: 8 additions & 10 deletions docs/README.rst
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ Features
- Builtin IPC streaming APIs with task fan-out broadcasting
- A (first ever?) "native" multi-core debugger UX for Python using `pdb++`_
- Support for a swappable, OS specific, process spawning layer
- A modular transport stack, allowing for custom serialization (eg.
- A modular transport stack, allowing for custom serialization (eg. with
`msgspec`_), communications protocols, and environment specific IPC
primitives
- Support for spawning process-level-SC, inter-loop one-to-one-task oriented
@@ -490,12 +490,6 @@ From PyPi::
pip install tractor


To try out the (optionally) faster `msgspec`_ codec instead of the
default ``msgpack`` lib::

pip install tractor[msgspec]


From git::

pip install git+git://github.com/goodboy/tractor.git
@@ -564,11 +558,15 @@ properties of the system.

What's on the TODO:
-------------------
Help us push toward the future.
Help us push toward the future of distributed `Python`.

- Typed messaging protocols (ex. via ``msgspec``, see `#36
- Erlang-style supervisors via composed context managers (see `#22
<https://github.com/goodboy/tractor/issues/22>`_)
- Typed messaging protocols (ex. via ``msgspec.Struct``, see `#36
<https://github.com/goodboy/tractor/issues/36>`_)
- Erlang-style supervisors via composed context managers
- Typed capability-based (dialog) protocols ( see `#196
<https://github.com/goodboy/tractor/issues/196>`_ with draft work
started in `#311 <https://github.com/goodboy/tractor/pull/311>`_)


Feel like saying hi?
1 change: 1 addition & 0 deletions examples/infected_asyncio_echo_server.py
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
async def aio_echo_server(
to_trio: trio.MemorySendChannel,
from_trio: asyncio.Queue,

) -> None:

# a first message must be sent **from** this ``asyncio``
5 changes: 5 additions & 0 deletions nooz/316.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Run windows CI jobs on python 3.10 after some
hacks for ``pdbpp`` dependency issues.

Issue was to do with the now deprecated `pyreadline` project which
should be changed over to `pyreadline3`.
8 changes: 8 additions & 0 deletions nooz/317.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Drop use of the ``msgpack`` package and instead move fully to the
``msgspec`` codec library.

We've now used ``msgspec`` extensively in production and there's no
reason to not use it as default. Further this change preps us for the up
and coming typed messaging semantics (#196), dialog-unprotocol system
(#297), and caps-based messaging-protocols (#299) planned before our
first beta.
13 changes: 13 additions & 0 deletions nooz/318.bug.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Fix a previously undetected ``trio``-``asyncio`` task lifetime linking
issue with the ``to_asyncio.open_channel_from()`` api where both sides
where not properly waiting/signalling termination and it was possible
for ``asyncio``-side errors to not propagate due to a race condition.

The implementation fix summary is:
- add state to signal the end of the ``trio`` side task to be
read by the ``asyncio`` side and always cancel any ongoing
task in such cases.
- always wait on the ``asyncio`` task termination from the ``trio``
side on error before maybe raising said error.
- always close the ``trio`` mem chan on exit to ensure the other
side can detect it and follow.
28 changes: 12 additions & 16 deletions setup.py
Original file line number Diff line number Diff line change
@@ -54,29 +54,25 @@
# tooling
'colorlog',
'wrapt',

# 3.10 has an outstanding unreleased issue and `pdbpp` itself
# pins to patched forks of its own dependencies as well.
"pdbpp @ git+https://github.com/pdbpp/pdbpp@master#egg=pdbpp", # noqa: E501
'pdbpp',
# windows deps workaround for ``pdbpp``
# https://github.com/pdbpp/pdbpp/issues/498
# https://github.com/pdbpp/fancycompleter/issues/37
'pyreadline3 ; platform_system == "Windows"',

# serialization
'msgpack>=1.0.3',
'msgspec >= "0.4.0"'

],
extras_require={

# serialization
'msgspec': ['msgspec >= "0.4.0"'],

},
tests_require=['pytest'],
python_requires=">=3.9",
keywords=[
'trio',
"async",
"concurrency",
"actor model",
"distributed",
'async',
'concurrency',
'structured concurrency',
'actor model',
'distributed',
'multiprocessing'
],
classifiers=[
@@ -87,7 +83,7 @@
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.9",
"Intended Audience :: Science/Research",
"Intended Audience :: Developers",
95 changes: 92 additions & 3 deletions tests/test_infected_asyncio.py
Original file line number Diff line number Diff line change
@@ -11,12 +11,25 @@
import pytest
import trio
import tractor
from tractor import to_asyncio
from tractor import RemoteActorError
from tractor import (
to_asyncio,
RemoteActorError,
)
from tractor.trionics import BroadcastReceiver


async def sleep_and_err(sleep_for: float = 0.1):
async def sleep_and_err(
sleep_for: float = 0.1,

# just signature placeholders for compat with
# ``to_asyncio.open_channel_from()``
to_trio: Optional[trio.MemorySendChannel] = None,
from_trio: Optional[asyncio.Queue] = None,

):
if to_trio:
to_trio.send_nowait('start')

await asyncio.sleep(sleep_for)
assert 0

@@ -146,6 +159,80 @@ async def main():
trio.run(main)


@tractor.context
async def trio_ctx(
ctx: tractor.Context,
):

await ctx.started('start')

# this will block until the ``asyncio`` task sends a "first"
# message.
with trio.fail_after(2):
async with (
tractor.to_asyncio.open_channel_from(
sleep_and_err,
) as (first, chan),

trio.open_nursery() as n,
):

assert first == 'start'

# spawn another asyncio task for the cuck of it.
n.start_soon(
tractor.to_asyncio.run_task,
sleep_forever,
)
await trio.sleep_forever()


@pytest.mark.parametrize(
'parent_cancels', [False, True],
ids='parent_actor_cancels_child={}'.format
)
def test_context_spawns_aio_task_that_errors(
arb_addr,
parent_cancels: bool,
):
'''
Verify that spawning a task via an intertask channel ctx mngr that
errors correctly propagates the error back from the `asyncio`-side
task.
'''
async def main():

async with tractor.open_nursery() as n:
p = await n.start_actor(
'aio_daemon',
enable_modules=[__name__],
infect_asyncio=True,
# debug_mode=True,
loglevel='cancel',
)
async with p.open_context(
trio_ctx,
) as (ctx, first):

assert first == 'start'

if parent_cancels:
await p.cancel_actor()

await trio.sleep_forever()

with pytest.raises(RemoteActorError) as excinfo:
trio.run(main)

err = excinfo.value
assert isinstance(err, RemoteActorError)
if parent_cancels:
assert err.type == trio.Cancelled
else:
assert err.type == AssertionError


async def aio_cancel():
''''
Cancel urself boi.
@@ -385,6 +472,8 @@ async def aio_echo_server(
print('breaking aio echo loop')
break

print('exiting asyncio task')

async with to_asyncio.open_channel_from(
aio_echo_server,
) as (first, chan):
Loading