Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 5 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
with:
args: tox

build:
tests:
needs: lint
runs-on: ubuntu-latest

Expand All @@ -38,19 +38,20 @@ jobs:

matrix:
toxenv:
- py35
- py36
- py37
- py38
- py39
- py310

steps:
- uses: actions/checkout@v2

- name: tox ${{ matrix.toxenv }}
uses: docker://snakepacker/python:all
env:
TOXENV: ${{ matrix.toxenv }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
FORCE_COLOR: yes
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TOXENV: ${{ matrix.toxenv }}
with:
args: tox
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
python_files = test_*
python_functions = test_*
python_classes = TestSuite*
asyncio_mode=auto
30 changes: 13 additions & 17 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@
).load_module()


TEST_REQUIRES = [
"async-timeout~=4.0.2",
"pytest~=7.1.2",
"pytest-aiohttp~=1.0.4",
"pytest-cov~=3.0.0",
"coverage!=4.3",
"coveralls~=3.3.1",
]


setup(
name="wsrpc-aiohttp",
version=module.__version__,
Expand All @@ -31,11 +41,10 @@
"Operating System :: Microsoft",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: Implementation :: CPython",
],
long_description=open("README.md").read(),
Expand All @@ -46,24 +55,11 @@
python_requires=">3.5.*, <4",
extras_require={
"ujson": ["ujson"],
"testing": [
"async-timeout",
"pytest",
"pytest-aiohttp",
"pytest-cov",
"coverage!=4.3",
"coveralls",
],
"testing": TEST_REQUIRES,
"develop": [
"async-timeout",
"coverage!=4.3",
"coveralls",
"pytest",
"pytest-aiohttp",
"pytest-cov",
"Sphinx",
"sphinxcontrib-plantuml",
"tox>=2.4",
],
] + TEST_REQUIRES,
},
)
8 changes: 5 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ def application(handler, socket_path):


@pytest.fixture
async def session(aiohttp_client, application, loop) -> ClientSession:
async def session(aiohttp_client, application, event_loop) -> ClientSession:
return await aiohttp_client(application)


@pytest.fixture
async def client(session: ClientSession, socket_path, loop) -> WSRPCClient:
return WSRPCClient(socket_path, session=session, loop=loop)
async def client(
session: ClientSession, socket_path, event_loop
) -> WSRPCClient:
return WSRPCClient(socket_path, session=session, loop=event_loop)
4 changes: 2 additions & 2 deletions tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ async def emitter(socket: WSRPCBase):
await socket.emit({"Hello": "world"})


async def test_emitter(client: WSRPCClient, handler, loop):
async def test_emitter(client: WSRPCClient, handler, event_loop):
handler.add_route("emitter", emitter)

async with client:
future = loop.create_future()
future = event_loop.create_future()

client.add_event_listener(future.set_result)

Expand Down
10 changes: 4 additions & 6 deletions tests/test_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,21 @@ def foobar(self):
assert "foobar" not in FooRoute.__no_proxy__


def test_route__init__(loop):
async def test_route__init__(event_loop):
socket = Mock()
socket._loop = object()

route = Route(socket)

assert route.socket is socket
assert route.loop is socket._loop
assert route.loop is event_loop

socket = object()
route = Route(socket)

assert route.socket is socket
assert route.loop is loop
assert route.loop is event_loop


def test_abc_inheritance(loop):
def test_abc_inheritance(event_loop):
class AbstractMixin(ABC):
@abstractmethod
def foo(self):
Expand Down
6 changes: 3 additions & 3 deletions tests/test_rpc_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,10 @@ async def test_call_when_params_none(


async def test_broadcast(
client: WSRPCClient, handler: WebSocketAsync, route: Route, loop
client: WSRPCClient, handler: WebSocketAsync, route: Route, event_loop
):
async with client:
future = loop.create_future()
future = event_loop.create_future()

async def on_broadcast(_, result):
nonlocal future
Expand Down Expand Up @@ -229,7 +229,7 @@ def get_data(self, _):

async def test_call_timeout(client: WSRPCClient, handler: WebSocketAsync):
async def will_sleep_for(_, seconds):
with timeout(0.5):
async with timeout(0.5):
await asyncio.sleep(seconds)
return DATA_TO_RETURN

Expand Down
8 changes: 4 additions & 4 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[tox]
envlist = lint,mypy,py3{5,6,7,8,9}
envlist = lint,mypy,py3{7,8,9,10}

[testenv]
passenv = COVERALLS_* AMQP_*
passenv = COVERALLS_* AMQP_* GITHUB_* FORCE_COLOR

extras =
testing

commands=
py.test -vv -p no:asyncio \
py.test -vv \
--cov=wsrpc_aiohttp \
--cov-report=term-missing \
--doctest-modules \
Expand All @@ -21,7 +21,7 @@ deps =
pylava

commands=
pylava -o pylava.ini .
pylava -o pylava.ini wsrpc_aiohttp tests

[testenv:mypy]
usedevelop = true
Expand Down
2 changes: 1 addition & 1 deletion wsrpc_aiohttp/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

team_email = "me@mosquito.su"

version_info = (3, 1, 2)
version_info = (3, 2, 1)


__author__ = ", ".join("{} <{}>".format(*info) for info in author_info)
Expand Down
88 changes: 49 additions & 39 deletions wsrpc_aiohttp/websocket/abc.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import asyncio
from abc import (
ABC, abstractmethod, abstractclassmethod, abstractproperty,
abstractstaticmethod
)
from abc import ABC, abstractmethod
from enum import IntEnum
from typing import Any, Mapping, Coroutine, Union, Callable, Dict, Tuple

Expand All @@ -16,7 +13,8 @@ class AbstractWebSocket(ABC):
def __init__(self, request: Request):
raise NotImplementedError(request)

@abstractclassmethod
@classmethod
@abstractmethod
def configure(cls, keepalive_timeout: int,
client_timeout: int,
max_concurrent_requests: int) -> None:
Expand Down Expand Up @@ -55,7 +53,8 @@ async def authorize(self) -> bool:
async def __handle_request(self) -> WebSocketResponse:
raise NotImplementedError

@abstractclassmethod
@classmethod
@abstractmethod
def broadcast(
cls, func, callback=None, return_exceptions=True,
**kwargs: Mapping[str, Any]
Expand Down Expand Up @@ -115,7 +114,7 @@ def __getattr__(self, item: str):
EventListenerType = Callable[[Dict[str, Any]], Any]


class AbstactWSRPC(ABC):
class WSRPCBase(ABC):
@abstractmethod
def __init__(self, loop: asyncio.AbstractEventLoop = None,
timeout: Union[int, float] = None):
Expand All @@ -138,28 +137,31 @@ async def handle_message(self, message: WSMessage):
async def _on_message(self, msg: WSMessage):
raise NotImplementedError

@abstractclassmethod
@classmethod
@abstractmethod
def get_routes(cls) -> Mapping[str, "RouteType"]:
raise NotImplementedError

@classmethod
def get_clients(cls) -> Dict[str, "AbstactWSRPC"]:
def get_clients(cls) -> Dict[str, "AbstractWSRPC"]:
raise NotImplementedError

@abstractproperty
@property
@abstractmethod
def routes(self) -> Dict[str, "RouteType"]:
raise NotImplementedError

@property
def clients(self) -> Dict[str, "AbstactWSRPC"]:
def clients(self) -> Dict[str, "AbstractWSRPC"]:
""" Property which contains the socket clients """
raise NotImplementedError

@abstractmethod
def prepare_args(self, args) -> Tuple[Tuple[Any, ...], Dict[str, Any]]:
raise NotImplementedError

@abstractstaticmethod
@staticmethod
@abstractmethod
def is_route(func) -> bool:
raise NotImplementedError

Expand Down Expand Up @@ -217,30 +219,6 @@ async def make_something(self, foo, bar):
async def emit(self, event: Any) -> None:
pass

@abstractclassmethod
def add_route(cls, route: str,
handler: Union[AbstractRoute, Callable]) -> None:
""" Expose local function through RPC

:param route: Name which function will be aliased for this function.
Remote side should call function by this name.
:param handler: Function or Route class (classes based on
:class:`wsrpc_aiohttp.WebSocketRoute`).
For route classes the public methods will
be registered automatically.

.. note::

Route classes might be initialized only once for the each
socket instance.

In case the method of class will be called first,
:func:`wsrpc_aiohttp.WebSocketRoute.init` will be called
without params before callable method.

"""
raise NotImplementedError

@abstractmethod
def add_event_listener(self, func: EventListenerType) -> None:
raise NotImplementedError
Expand All @@ -255,7 +233,8 @@ def remove_route(cls, route: str, fail=True):

raise NotImplementedError

@abstractproperty
@property
@abstractmethod
def proxy(self) -> Proxy:
""" Special property which allow run the remote functions
by `dot` notation
Expand All @@ -272,8 +251,39 @@ def proxy(self) -> Proxy:


RouteType = Union[
Callable[[AbstactWSRPC, Any], Any],
Callable[[AbstactWSRPC, Any], Coroutine[Any, None, Any]],
Callable[[WSRPCBase, Any], Any],
Callable[[WSRPCBase, Any], Coroutine[Any, None, Any]],
AbstractRoute
]


class AbstractWSRPC(WSRPCBase, ABC):
@classmethod
@abstractmethod
def add_route(cls, route: str, handler: RouteType) -> None:
""" Expose local function through RPC

:param route: Name which function will be aliased for this function.
Remote side should call function by this name.
:param handler: Function or Route class (classes based on
:class:`wsrpc_aiohttp.WebSocketRoute`).
For route classes the public methods will
be registered automatically.

.. note::

Route classes might be initialized only once for each
socket instance.

In case the method of class will be called first,
:func:`wsrpc_aiohttp.WebSocketRoute.init` will be called
without params before callable method.

"""
raise NotImplementedError


# backward compatibility for typo
# noinspection SpellCheckingInspection
AbstactWSRPC = AbstractWSRPC
FrameMappingItemType = Mapping[IntEnum, Callable[[WSMessage], Any]]
Loading