Skip to content

Commit

Permalink
adding All, Or, BitwiseAnd and BitwiseOr operators (close issue #20)
Browse files Browse the repository at this point in the history
  • Loading branch information
semiversus committed Oct 3, 2018
1 parent 706867a commit d39d113
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 19 deletions.
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ Also fancy stuff like getting item by index or key is possible:
Some python built in functions can't return Publishers (e.g. ``len()`` needs to
return an integer). For this cases special functions are defined in broqer: ``Str``,
``Int``, ``Float``, ``Len`` and ``In`` (for ``x in y``).
``Int``, ``Float``, ``Len`` and ``In`` (for ``x in y``). Also other functions
for convenience are available: ``All``, ``Any``, ``BitwiseAnd`` and ``BitwiseOr``.

Attribute access on a publisher is building a publisher where the actual attribute
access is done on emitting values:
Expand All @@ -126,7 +127,7 @@ access is done on emitting values:
>>> i = Value('Attribute access made REACTIVE')
>>> i.lower().strip(sep=' ') | op.Sink(print)
['attribute', 'access', 'made', 'reactive']
>>> i.emit('Reactive and pythonic')
['reactive', 'and', 'pythonic']
Expand Down Expand Up @@ -339,4 +340,3 @@ Subjects
+--------------------------+--------------------------------------------------------------+
| Value_ (\*init) | Source with a state (initialized via ``init``) |
+--------------------------+--------------------------------------------------------------+

4 changes: 2 additions & 2 deletions broqer/op/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

# enable operator overloading
from .operator_overloading import apply_operator_overloading, Str, Bool, Int, \
Float, Repr, Len, In
Float, Repr, Len, In, All, Any, BitwiseAnd, BitwiseOr

apply_operator_overloading()

Expand All @@ -44,5 +44,5 @@
'False_', 'Trace', 'build_map', 'build_reduce', 'build_combine_latest',
'build_filter', 'build_accumulate', 'build_map_async',
'build_map_threaded', 'build_sink', 'Str', 'Bool', 'Int', 'Float', 'Repr',
'Len', 'In'
'Len', 'In', 'All', 'Any', 'BitwiseAnd', 'BitwiseOr'
]
68 changes: 61 additions & 7 deletions broqer/op/operator_overloading.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import asyncio
import math
import operator
from functools import partial
from typing import Any
from functools import partial, reduce
from typing import Any as Any_

from broqer import Publisher
from broqer.op import CombineLatest
Expand All @@ -20,7 +20,7 @@ def __init__(self, publisher: Publisher, value, operation) -> None:
def get(self):
return self._operation(self._publisher.get(), self._value)

def emit(self, value: Any, who: Publisher) -> asyncio.Future:
def emit(self, value: Any_, who: Publisher) -> asyncio.Future:
assert who is self._publisher, 'emit from non assigned publisher'

result = self._operation(value, self._value)
Expand All @@ -38,7 +38,7 @@ def __init__(self, publisher: Publisher, value, operation) -> None:
def get(self):
return self._operation(self._value, self._publisher.get())

def emit(self, value: Any, who: Publisher) -> asyncio.Future:
def emit(self, value: Any_, who: Publisher) -> asyncio.Future:
assert who is self._publisher, 'emit from non assigned publisher'

result = self._operation(self._value, value)
Expand All @@ -55,7 +55,7 @@ def __init__(self, publisher: Publisher, operation) -> None:
def get(self):
return self._operation(self._publisher.get())

def emit(self, value: Any, who: Publisher) -> asyncio.Future:
def emit(self, value: Any_, who: Publisher) -> asyncio.Future:
assert who is self._publisher, 'emit from non assigned publisher'

result = self._operation(value)
Expand Down Expand Up @@ -83,7 +83,7 @@ def __call__(self, *args, **kwargs):
self._kwargs = kwargs
return self

def emit(self, value: Any, who: Publisher) -> asyncio.Future:
def emit(self, value: Any_, who: Publisher) -> asyncio.Future:
assert who is self._publisher, 'emit from non assigned publisher'

attribute = getattr(value, self._attribute_name)
Expand Down Expand Up @@ -189,7 +189,7 @@ class In(CombineLatest):
:param item: publisher or constant to check for availability in container.
:param container: container (publisher or constant)
"""
def __init__(self, item: Any, container: Any) -> None:
def __init__(self, item: Any_, container: Any_) -> None:
if isinstance(item, Publisher) and isinstance(container, Publisher):
CombineLatest.__init__(self, item, container, map_=_in)
elif isinstance(item, Publisher):
Expand All @@ -200,3 +200,57 @@ def __init__(self, item: Any, container: Any) -> None:
'item or container has to be a publisher'
function = partial(_in, item)
CombineLatest.__init__(self, container, map_=function)


def _all(*items):
return all(items)


class All(CombineLatest):
""" Implement the functionality of ``all`` operator for publishers.
One big difference is that ``all`` takes an iterator and ``All`` take a
variable amount of publishers as arguments.
:param publishers: Publishers evaluated for all to be True
"""
def __init__(self, *publishers: Any_) -> None:
CombineLatest.__init__(self, *publishers, map_=_all)


def _any(*items):
return any(items)


class Any(CombineLatest):
""" Implement the functionality of ``any`` operator for publishers.
One big difference is that ``any`` takes an iterator and ``Any`` take a
variable amount of publishers as arguments.
:param publishers: Publishers evaluated for one to be True
"""
def __init__(self, *publishers: Any_) -> None:
CombineLatest.__init__(self, *publishers, map_=_any)


def _bitwise_or(*items):
return reduce(operator.or_, items)


class BitwiseOr(CombineLatest):
""" Implement the functionality of bitwise or (``|``) operator for
publishers.
:param publishers: Publishers evaluated for bitwise or
"""
def __init__(self, *publishers: Any_) -> None:
CombineLatest.__init__(self, *publishers, map_=_bitwise_or)


def _bitwise_and(*items):
return reduce(operator.and_, items)


class BitwiseAnd(CombineLatest):
""" Implement the functionality of bitwise and (``&``) operator for
publishers.
:param publishers: Publishers evaluated for bitwise and
"""
def __init__(self, *publishers: Any_) -> None:
CombineLatest.__init__(self, *publishers, map_=_bitwise_and)
33 changes: 26 additions & 7 deletions test/test_core_publisher_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ def test_in_operator():
dut3 = op.In(pi, cc)
with pytest.raises(AssertionError):
op.In(ci, cc)

assert dut1.get() == True
assert dut2.get() == True
assert dut3.get() == True
Expand Down Expand Up @@ -316,17 +316,17 @@ def test_getattr_method():

with pytest.raises(ValueError):
dut1.get()

with pytest.raises(ValueError):
dut2.get()

with pytest.raises(ValueError):
dut3.get()

mock1.assert_not_called()
mock2.assert_not_called()
mock3.assert_not_called()

p.notify('This is just a test, honestly!')

assert dut1.get() == ['This', 'is', 'just', 'a', 'test,', 'honestly!']
Expand All @@ -349,11 +349,30 @@ def __init__(self, a=5):

with pytest.raises(ValueError):
dut.get()

m.assert_not_called()

p.notify(Foo(3))

assert dut.get() == 3

m.assert_called_once_with(3)

@pytest.mark.parametrize('operator, values, result', [
(op.All, (False, False, False), False),
(op.All, (False, True, False), False),
(op.All, (True, True, True), True),
(op.Any, (False, False, False), False),
(op.Any, (False, True, False), True),
(op.Any, (True, True, True), True),
(op.BitwiseAnd, (0, 5, 15), 0),
(op.BitwiseAnd, (7, 14, 255), 6),
(op.BitwiseAnd, (3,), 3),
(op.BitwiseOr, (0, 5, 8), 13),
(op.BitwiseOr, (7, 14, 255), 255),
(op.BitwiseOr, (3,), 3),
])
def test_multi_operators(operator, values, result):
sources = [StatefulPublisher(v) for v in values]
dut = operator(*sources)
assert dut.get() == result

0 comments on commit d39d113

Please sign in to comment.