Skip to content

Commit

Permalink
Update to 2.1.
Browse files Browse the repository at this point in the history
  • Loading branch information
frobnitzem committed Feb 7, 2022
1 parent 18058d6 commit 24bc23c
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 37 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# ChangeLog

## [2.1.0] - 2022-02-07

### Changed

- `Repeat` and `Forever` no longer use `ApplyM` internally.
This was non-intuitive, and overall decreased usability.
Now `Repeat` and `Forever` always send the same arguments (if any)
to their enclosed `Wire`.

- `RepeatM` and `ForeverM` perform wire repetition with
argument passing.

### Fixed

- `Repeat` called its embedded function with incorrect arguments.
This has been fixed.


## [2.0.0] - 2022-02-06

This release adds a killer feature -- wires can return
Expand Down Expand Up @@ -46,5 +64,6 @@ into `wire2`'s call. For example, if `wire1` returns

- Fixed ZMQ dependency name to `pyzmq`.

[2.1.0]: https://github.com/frobnitzem/aiowire/compare/v2.0.0...v2.1.0
[2.0.0]: https://github.com/frobnitzem/aiowire/compare/v1.1.0...v2.0.0
[1.1.0]: https://github.com/frobnitzem/aiowire/compare/v1.0.0...v1.0.1
3 changes: 2 additions & 1 deletion aiowire/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@

from .event_loop import EventLoop
from .poller import Poller
from .wire import Wire, Sequence, ApplyM, Call, Repeat, Forever
from .wire import Wire, Sequence, Call, Repeat, Forever
from .wire import ApplyM, RepeatM, ForeverM
49 changes: 36 additions & 13 deletions aiowire/wire.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Wire:
def __init__(self, a):
self._aiowire = a
def __rshift__(a, b): # >>
return Sequence(a, b)
return Sequence(a, b, repeatArgs=False)
def __ge__(a, b): # >=
return ApplyM(a, b)
def __mul__(a, n : int):
Expand All @@ -40,27 +40,29 @@ async def __call__(self, ev, *args): # -> Optional[Wire]:
class Sequence(Wire):
"""
This Wire codifies the pattern, "call a, then b".
It prevents us from having to write it for each kind of wire.
Instead, we just create a Sequence wire.
Any return value from `a` is ignored, so `b` is always
called as b(ev).
Any return value from `a` is ignored.
In default mode, `b` is always called as `b(ev)`.
When `repeatArgs` is `True`, then b is always called as `b(ev, *args)`,
with the same args passed to `a`.
Note:
If `a` returns a tuple with a wire and a result, `(w, x)`,
then both w(ev, *x) and b(ev, *x) will be run concurrently.
then both w(ev, *x) and b(ev) / b(ev, *args) will be run concurrently.
"""
def __init__(self, a, b):
def __init__(self, a, b, repeatArgs=False):
self.a = a
self.b = b
self.repeatArgs = repeatArgs
async def __call__(self, ev, *args):
ret = await self.a(ev, *args)
if ret is not None:
args = ev.start( ret )
else:
args = []
return self.b, args
ev.start( ret )
if self.repeatArgs:
return self.b, args
return self.b

class ApplyM(Wire):
"""
Expand All @@ -85,7 +87,7 @@ class ApplyM(Wire):
This kind of wire can lead to difficult-to-diagnose errors.
If `a` returns something other than
None | Callable | (Callable, List),
then get_args will throw an error, and you'll have to find
then `get_args` will throw an error, and you'll have to find
the wire `a` and fix its return type.
"""
def __init__(self, a, b):
Expand Down Expand Up @@ -126,6 +128,8 @@ async def __call__(self, ev, *ignored):
ret = self.fn(*self.args, **self.kwargs)
if isawaitable(ret):
ret = await ret
# Ensure that the return value is compatible with
# wire's expected (Optional[Callable], List) format.
if ret is None:
return None
if not isinstance(ret, (list,tuple)):
Expand All @@ -137,14 +141,33 @@ def __init__(self, a, n : int):
self.a = a
self.n = n

async def __call__(self, ev, *args):
self.n -= 1
if self.n >= 0:
M = Sequence(self.a, self, repeatArgs=True)
return await M(ev, *args)
return None, args

class RepeatM(Wire):
def __init__(self, a, n : int):
self.a = a
self.n = n

async def __call__(self, ev, *args):
self.n -= 1
if self.n >= 0:
M = ApplyM(self.a, self)
return await M(ev, args)
return await M(ev, *args)
return None, args

class Forever(Wire):
def __init__(self, a):
self.a = a
async def __call__(self, ev, *args):
M = Sequence(self.a, self, repeatArgs=True)
return await M(ev, *args)

class ForeverM(Wire):
def __init__(self, a):
self.a = a
async def __call__(self, ev, *args):
Expand Down
24 changes: 6 additions & 18 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "aiowire"
version = "2.0.0"
version = "2.1.0"
description = "A simple event loop using asyncio"
authors = ["David M. Rogers <[email protected]>"]
readme = "README.rst"
Expand All @@ -16,9 +16,14 @@ python = "^3.9"
pyzmq = "^22.3.0"

[tool.poetry.dev-dependencies]
pytest = "^7.0.0"
pytest-asyncio = "^0.17.2"
pytest-cov = "^3.0.0"

[tool.pytest.ini_options]
asyncio_mode = "strict"
testpaths = [ "tests" ]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
2 changes: 0 additions & 2 deletions pytest.ini

This file was deleted.

41 changes: 39 additions & 2 deletions tests/test_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import datetime

from aiowire import __version__
from aiowire import EventLoop, Poller, Wire, Call, Forever
from aiowire import EventLoop, Poller, Wire, Call
from aiowire import Repeat, RepeatM, Forever, ForeverM

# Wires with strange return values.
async def return_three(ev, x):
Expand Down Expand Up @@ -84,4 +85,40 @@ async def return_launcher(eve, *arg):
return launcher, arg + (arg1,)

async with EventLoop(timeout=0.02) as event:
event.start( (Forever(return_launcher), [arg0]) )
event.start( (ForeverM(return_launcher), [arg0]) )

async def aparrot(*args):
return args
def parrot(*args):
return args
async def callee(eve, *args):
assert args == (1,2)
await asyncio.sleep(0.01)
return None, (3,)

async def callee2(eve, *args):
assert args == (1,2) or args == (3,)
await asyncio.sleep(0.01)
return None, (3,)

@pytest.mark.asyncio
async def test_repeat_call():
async with EventLoop(timeout=0.2) as eve:
eve.start( Call(parrot, 1, 2) >= Repeat(callee, 2) )
eve.start( Call(aparrot, 1, 2) >= Repeat(callee, 3) )

@pytest.mark.asyncio
async def test_forever_call():
async with EventLoop(timeout=0.2) as eve:
eve.start( Call(aparrot, 1, 2) >= Forever(callee) )

@pytest.mark.asyncio
async def test_repeatM_call():
async with EventLoop(timeout=0.2) as eve:
eve.start( Call(parrot, 1, 2) >= RepeatM(callee2, 20) )
eve.start( Call(aparrot, 1, 2) >= RepeatM(callee2, 4) )

@pytest.mark.asyncio
async def test_foreverM_call():
async with EventLoop(timeout=0.2) as eve:
eve.start( Call(aparrot, 1, 2) >= ForeverM(callee2) )

0 comments on commit 24bc23c

Please sign in to comment.