From b020455c98795edda3678ff6c182cd7aa1dd4cc4 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 10 Sep 2024 13:43:50 -0400 Subject: [PATCH 1/2] Add type hints to all defs --- src/nextline_schedule/auto/auto_mode.py | 6 +++--- .../auto/state_machine/machine.py | 3 +-- src/nextline_schedule/plugin.py | 12 ++++++------ src/nextline_schedule/queue/queue.py | 6 +++--- src/nextline_schedule/scheduler.py | 4 +++- tests/auto/test_auto.py | 2 +- tests/auto/test_auto_on_raised.py | 18 ++++++++++-------- tests/auto/test_auto_turn_off.py | 11 ++++++----- tests/queue/test_queue.py | 4 ++-- tests/queue/test_queue_imp.py | 8 ++++---- tests/schema/queries/test_queue_items.py | 2 +- tests/test_plugin.py | 10 ++++++---- tests/test_version.py | 2 +- 13 files changed, 47 insertions(+), 41 deletions(-) diff --git a/src/nextline_schedule/auto/auto_mode.py b/src/nextline_schedule/auto/auto_mode.py index 2e6089b..178212f 100644 --- a/src/nextline_schedule/auto/auto_mode.py +++ b/src/nextline_schedule/auto/auto_mode.py @@ -1,6 +1,6 @@ import dataclasses from collections.abc import AsyncIterator, Awaitable, Callable -from typing import Literal +from typing import Any, Literal from nextline import Nextline from nextline.utils import pubsub @@ -60,8 +60,8 @@ async def __aenter__(self) -> 'AutoMode': await self._machine.__aenter__() return self - async def __aexit__(self, exc_type, exc_value, traceback) -> None: - await self._machine.__aexit__(exc_type, exc_value, traceback) + async def __aexit__(self, *args: Any, **kwargs: Any) -> None: + await self._machine.__aexit__(*args, **kwargs) await self._pubsub_mode.aclose() async def on_state_changed(self, state: str) -> None: diff --git a/src/nextline_schedule/auto/state_machine/machine.py b/src/nextline_schedule/auto/state_machine/machine.py index edb4853..3daed87 100644 --- a/src/nextline_schedule/auto/state_machine/machine.py +++ b/src/nextline_schedule/auto/state_machine/machine.py @@ -90,6 +90,5 @@ async def __aenter__(self) -> 'AutoModeStateMachine': await self.start() return self - async def __aexit__(self, exc_type, exc_value, traceback) -> None: - del exc_type, exc_value, traceback + async def __aexit__(self, *_: Any, **__: Any) -> None: await self.close() diff --git a/src/nextline_schedule/plugin.py b/src/nextline_schedule/plugin.py index 4d760aa..5baeb7f 100644 --- a/src/nextline_schedule/plugin.py +++ b/src/nextline_schedule/plugin.py @@ -1,4 +1,4 @@ -from collections.abc import Mapping, MutableMapping +from collections.abc import AsyncIterator, Mapping, MutableMapping from logging import getLogger from pathlib import Path from typing import Optional @@ -42,7 +42,7 @@ def dynaconf_validators(self) -> Optional[tuple[Validator, ...]]: return VALIDATORS @spec.hookimpl - def configure(self, settings: Dynaconf): + def configure(self, settings: Dynaconf) -> None: logger = getLogger(__name__) logger.info(f'{__package__} version: {__version__}') api_rul = settings.schedule.api @@ -56,19 +56,19 @@ def configure(self, settings: Dynaconf): # self._scheduler = self._dummy @spec.hookimpl - def schema(self): + def schema(self) -> tuple[type, type, type]: return (Query, Mutation, Subscription) @spec.hookimpl @asynccontextmanager - async def lifespan(self, context: Mapping): + async def lifespan(self, context: Mapping) -> AsyncIterator[None]: nextline = context['nextline'] self._queue = Queue() self._auto_mode = AutoMode( nextline=nextline, scheduler=self._scheduler, queue=self._queue ) - async with self._queue, self._auto_mode as y: - yield y + async with self._queue, self._auto_mode: + yield @spec.hookimpl def update_strawberry_context(self, context: MutableMapping) -> None: diff --git a/src/nextline_schedule/queue/queue.py b/src/nextline_schedule/queue/queue.py index 320d184..9c150bc 100644 --- a/src/nextline_schedule/queue/queue.py +++ b/src/nextline_schedule/queue/queue.py @@ -1,6 +1,6 @@ from collections.abc import AsyncIterator, Iterable from logging import getLogger -from typing import Optional +from typing import Any, Optional from nextline.utils import pubsub @@ -26,7 +26,7 @@ async def __call__(self) -> str: return item.script @property - def items(self): + def items(self) -> list[QueueItem]: return self._pubsub.latest() def subscribe(self) -> AsyncIterator[list[QueueItem]]: @@ -84,5 +84,5 @@ async def __aenter__(self) -> 'Queue': await self._pubsub.publish(list(self._queue.items)) return self - async def __aexit__(self, *_, **__) -> None: + async def __aexit__(self, *_: Any, **__: Any) -> None: await self.aclose() diff --git a/src/nextline_schedule/scheduler.py b/src/nextline_schedule/scheduler.py index 725441a..52238c7 100644 --- a/src/nextline_schedule/scheduler.py +++ b/src/nextline_schedule/scheduler.py @@ -5,7 +5,9 @@ class Scheduler: - def __init__(self, api_url: str, length_minutes: int, policy: str, timeout=5.0): + def __init__( + self, api_url: str, length_minutes: int, policy: str, timeout: float = 5.0 + ): self._api_url = api_url self._length_minutes = length_minutes self._policy = policy diff --git a/tests/auto/test_auto.py b/tests/auto/test_auto.py index 6493873..92501f3 100644 --- a/tests/auto/test_auto.py +++ b/tests/auto/test_auto.py @@ -76,5 +76,5 @@ async def test_one() -> None: assert expected == await states -async def subscribe_state(auto_mode: AutoMode): +async def subscribe_state(auto_mode: AutoMode) -> list[str]: return [state async for state in auto_mode.subscribe_state()] diff --git a/tests/auto/test_auto_on_raised.py b/tests/auto/test_auto_on_raised.py index 9448f06..5ea9bc0 100644 --- a/tests/auto/test_auto_on_raised.py +++ b/tests/auto/test_auto_on_raised.py @@ -1,9 +1,11 @@ import asyncio import time +from collections.abc import Callable +from typing import NoReturn from nextline import Nextline -from nextline_schedule.auto import AutoMode, AutoModeStateMachine +from nextline_schedule.auto import AutoMode STATEMENT_QUEUE = ''' """queue""" @@ -20,17 +22,17 @@ class MockError(Exception): pass -def f(): +def f() -> None: time.sleep(0.001) -def g(): +def g() -> NoReturn: time.sleep(0.001) raise MockError() -async def test_on_raised_while_pulling(): - async def pull(): +async def test_on_raised_while_pulling() -> None: + async def pull() -> NoReturn: raise MockError() run_no = 1 @@ -50,8 +52,8 @@ async def pull(): assert expected == await states -async def test_on_raised_while_running(): - async def pull(): +async def test_on_raised_while_running() -> None: + async def pull() -> Callable[[], None]: return g run_no = 1 @@ -71,5 +73,5 @@ async def pull(): assert expected == await states -async def subscribe_state(auto_mode: AutoModeStateMachine): +async def subscribe_state(auto_mode: AutoMode) -> list[str]: return [state async for state in auto_mode.subscribe_state()] diff --git a/tests/auto/test_auto_turn_off.py b/tests/auto/test_auto_turn_off.py index 72cbd4c..0e95642 100644 --- a/tests/auto/test_auto_turn_off.py +++ b/tests/auto/test_auto_turn_off.py @@ -1,9 +1,10 @@ import asyncio import time +from collections.abc import Callable from nextline import Nextline -from nextline_schedule.auto import AutoMode, AutoModeStateMachine +from nextline_schedule.auto import AutoMode STATEMENT_QUEUE = ''' """queue""" @@ -16,15 +17,15 @@ async def mock_queue() -> str: return STATEMENT_QUEUE -def f(): +def f() -> None: time.sleep(0.001) -async def pull(): +async def pull() -> Callable[[], None]: return f -async def test_turn_off_while_waiting(): +async def test_turn_off_while_waiting() -> None: run_no = 1 nextline = Nextline( statement=f, @@ -54,5 +55,5 @@ async def test_turn_off_while_waiting(): assert expected == await states -async def subscribe_state(auto_mode: AutoModeStateMachine): +async def subscribe_state(auto_mode: AutoMode) -> list[str]: return [state async for state in auto_mode.subscribe_state()] diff --git a/tests/queue/test_queue.py b/tests/queue/test_queue.py index 2772ffe..b639940 100644 --- a/tests/queue/test_queue.py +++ b/tests/queue/test_queue.py @@ -8,7 +8,7 @@ @given(st.data()) -async def test_queue(data: st.DataObject): +async def test_queue(data: st.DataObject) -> None: queue = data.draw(st_queue()) async def subscribe() -> list[list[QueueItem]]: @@ -45,7 +45,7 @@ async def subscribe() -> list[list[QueueItem]]: await queue.push(arg) case 'remove', _: ids = [i.id for i in queue.items] - ids.append(st.integers(min_value=0)) + ids.append(data.draw(st.integers(min_value=0))) id = data.draw(st.sampled_from(ids)) success = await queue.remove(id) if not success: diff --git a/tests/queue/test_queue_imp.py b/tests/queue/test_queue_imp.py index e91ff5d..d8d963f 100644 --- a/tests/queue/test_queue_imp.py +++ b/tests/queue/test_queue_imp.py @@ -9,7 +9,7 @@ @given(queue=st_queue_imp()) -def test_queue(queue: QueueImp): +def test_queue(queue: QueueImp) -> None: # __len__ assert len(queue) == len(queue.items) @@ -18,7 +18,7 @@ def test_queue(queue: QueueImp): @given(queue=st_queue_imp(), push_arg=st_push_arg()) -def test_push(queue: QueueImp, push_arg: PushArg): +def test_push(queue: QueueImp, push_arg: PushArg) -> None: initial_len = len(queue) item = queue.push(push_arg) assert item.script == push_arg.script @@ -27,7 +27,7 @@ def test_push(queue: QueueImp, push_arg: PushArg): @given(queue=st_queue_imp()) -def test_pop(queue: QueueImp): +def test_pop(queue: QueueImp) -> None: initial_len = len(queue) if initial_len == 0: assert queue.pop() is None @@ -38,7 +38,7 @@ def test_pop(queue: QueueImp): @given(queue=st_queue_imp()) -def test_remove(queue: QueueImp): +def test_remove(queue: QueueImp) -> None: initial_len = len(queue) if initial_len == 0: assert not queue.remove(0) diff --git a/tests/schema/queries/test_queue_items.py b/tests/schema/queries/test_queue_items.py index 88dd5e1..f442ce6 100644 --- a/tests/schema/queries/test_queue_items.py +++ b/tests/schema/queries/test_queue_items.py @@ -7,7 +7,7 @@ @given(queue=st_queue()) -async def test_schema(queue: Queue, schema: Schema): +async def test_schema(queue: Queue, schema: Schema) -> None: async with queue: context_schedule = {'queue': queue} context = {'schedule': context_schedule} diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 188eb20..7c8706b 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -24,7 +24,7 @@ async def client() -> AsyncIterator[TestClient]: yield y -async def test_plugin(client: TestClient): +async def test_plugin(client: TestClient) -> None: turned_on = asyncio.Event() task = asyncio.create_task(subscribe_auto_mode_state(client, turned_on)) @@ -62,7 +62,9 @@ async def test_plugin(client: TestClient): await task -async def subscribe_auto_mode_state(client: TestClient, turned_on: asyncio.Event): +async def subscribe_auto_mode_state( + client: TestClient, turned_on: asyncio.Event +) -> list[str]: await turned_on.wait() ret = [] async for data in gql_subscribe(client, SUBSCRIBE_AUTO_MODE_STATE): @@ -74,7 +76,7 @@ async def subscribe_auto_mode_state(client: TestClient, turned_on: asyncio.Event @pytest.fixture(autouse=True) -def configure_by_envvar(monkeypatch: pytest.MonkeyPatch): +def configure_by_envvar(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv('NEXTLINE_SCHEDULE__API', 'https://example.com') monkeypatch.setenv('NEXTLINE_SCHEDULE__LENGTH_MINUTES', '60') monkeypatch.setenv('NEXTLINE_SCHEDULE__POLICY', 'test') @@ -87,5 +89,5 @@ def configure_by_envvar(monkeypatch: pytest.MonkeyPatch): @pytest.fixture(autouse=True) -def mock_httpx(httpx_mock: HTTPXMock): +def mock_httpx(httpx_mock: HTTPXMock) -> None: httpx_mock.add_response(json={'commands': COMMANDS}) diff --git a/tests/test_version.py b/tests/test_version.py index 1310435..952f69d 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,6 +1,6 @@ import nextline_schedule -def test_version(): +def test_version() -> None: '''Confirm that the version string is attached to the module''' nextline_schedule.__version__ From 79cc7b61a936daed7a99fdd59ef12a1da9ec25f4 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Tue, 10 Sep 2024 13:44:18 -0400 Subject: [PATCH 2/2] Set `disallow_untyped_defs` to `true` --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 470e503..800dcc5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,6 +97,7 @@ profile = "black" [tool.mypy] plugins = "strawberry.ext.mypy_plugin" +disallow_untyped_defs = true [[tool.mypy.overrides]] module = ["dynaconf.*", "async_asgi_testclient.*", "apluggy.*", "deepdiff.*"]