diff --git a/.bumpversion.cfg b/.bumpversion.cfg index d6aa7c29..df600288 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -12,4 +12,3 @@ serialize = [bumpversion:file:docs/includes/introduction.txt] [bumpversion:file:README.rst] - diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 54f27622..aca083c3 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -26,4 +26,3 @@ Paste the full traceback (if there is any) * Python version * Mode version * Operating system - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..25e434e3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,30 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + + - repo: https://github.com/ambv/black + rev: 21.9b0 + hooks: + - id: black + + - repo: https://github.com/pycqa/isort + rev: 5.9.3 + hooks: + - id: isort + name: isort (python) + + - repo: local + hooks: + - id: flake8 + name: flake8 + stages: [commit] + language: python + entry: flake8 + types: [python] diff --git a/.travis.yml b/.travis.yml index 7377e46f..2563f7ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,27 +15,27 @@ matrix: - python: 3.8.6 env: TOXENV=flake8 os: linux - dist: focal + dist: bionic stage: lint - python: 3.8.6 env: TOXENV=typecheck os: linux - dist: focal + dist: bionic stage: lint - python: 3.8.6 env: TOXENV=apicheck os: linux - dist: focal + dist: bionic stage: lint - python: 3.8.6 env: TOXENV=docstyle os: linux - dist: focal + dist: bionic stage: lint - python: 3.8.6 env: TOXENV=bandit os: linux - dist: focal + dist: bionic stage: lint - python: 3.6.3 env: TOXENV=3.6 RUN_SUITE=y @@ -142,10 +142,17 @@ matrix: os: linux dist: focal stage: test - - python: 3.9.1 - env: TOXENV=3.9 IDENT="3.9.1" RUN_SUITE=y + - name: Latest python-3.9 + python: 3.9 + env: TOXENV=3.9 IDENT="3.9" RUN_SUITE=y os: linux - dist: xenial + dist: bionic + stage: test + - name: Latest python-3.10 + python: 3.10.0 + env: TOXENV=3.10 IDENT="3.10" RUN_SUITE=y + os: linux + dist: bionic stage: test before_install: diff --git a/CODE_OF_CONDUCT.rst b/CODE_OF_CONDUCT.rst index d59514a5..7141c712 100644 --- a/CODE_OF_CONDUCT.rst +++ b/CODE_OF_CONDUCT.rst @@ -41,4 +41,3 @@ reported by opening an issue or contacting one or more of the project maintainer This Code of Conduct is adapted from the Contributor Covenant, version 1.2.0 available at http://contributor-covenant.org/version/1/2/0/. - diff --git a/README.rst b/README.rst index dfcf4e03..f12a9d30 100644 --- a/README.rst +++ b/README.rst @@ -484,4 +484,3 @@ version 1.2.0 available at http://contributor-covenant.org/version/1/2/0/. .. |pyimp| image:: https://img.shields.io/pypi/implementation/mode-streaming.svg :alt: Supported Python implementations. :target: http://pypi.org/project/mode-streaming/ - diff --git a/docs/index.rst b/docs/index.rst index ded15b38..156eb339 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,4 +30,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/docs/reference/mode.loop.eventlet.rst b/docs/reference/mode.loop.eventlet.rst index 5c8eaccc..e2cac7f0 100644 --- a/docs/reference/mode.loop.eventlet.rst +++ b/docs/reference/mode.loop.eventlet.rst @@ -10,4 +10,3 @@ Importing this module directly will set the global event loop. See :mod:`faust.loop` for more information. - diff --git a/docs/reference/mode.loop.gevent.rst b/docs/reference/mode.loop.gevent.rst index d86d01ad..6f50695f 100644 --- a/docs/reference/mode.loop.gevent.rst +++ b/docs/reference/mode.loop.gevent.rst @@ -10,4 +10,3 @@ Importing this module directly will set the global event loop. See :mod:`faust.loop` for more information. - diff --git a/docs/reference/mode.loop.uvloop.rst b/docs/reference/mode.loop.uvloop.rst index 181a1ede..72fe42a5 100644 --- a/docs/reference/mode.loop.uvloop.rst +++ b/docs/reference/mode.loop.uvloop.rst @@ -10,4 +10,3 @@ Importing this module directly will set the global event loop. See :mod:`faust.loop` for more information. - diff --git a/extra/bandit/baseline.json b/extra/bandit/baseline.json index 68afae14..f05fb96b 100644 --- a/extra/bandit/baseline.json +++ b/extra/bandit/baseline.json @@ -537,4 +537,4 @@ "test_name": "try_except_pass" } ] -} \ No newline at end of file +} diff --git a/extra/bandit/config.yml b/extra/bandit/config.yml index ee7ec19c..38fba5ae 100644 --- a/extra/bandit/config.yml +++ b/extra/bandit/config.yml @@ -395,4 +395,3 @@ weak_cryptographic_key: weak_key_size_ec_medium: 224 weak_key_size_rsa_high: 1024 weak_key_size_rsa_medium: 2048 - diff --git a/mode/__init__.py b/mode/__init__.py index 7cd7a335..0fa7dda8 100644 --- a/mode/__init__.py +++ b/mode/__init__.py @@ -6,6 +6,10 @@ import re import sys import typing + +# Lazy loading. +# - See werkzeug/__init__.py for the rationale behind this. +from types import ModuleType # noqa from typing import Any, Mapping, NamedTuple, Sequence __version__ = "0.1.0" @@ -89,10 +93,6 @@ class version_info_t(NamedTuple): ] -# Lazy loading. -# - See werkzeug/__init__.py for the rationale behind this. -from types import ModuleType # noqa - all_by_module: Mapping[str, Sequence[str]] = { "mode.services": ["Service", "task", "timer"], "mode.signals": ["BaseSignal", "Signal", "SyncSignal"], diff --git a/mode/loop/eventlet.py b/mode/loop/eventlet.py index 2a66e68a..1f77a24a 100644 --- a/mode/loop/eventlet.py +++ b/mode/loop/eventlet.py @@ -1,4 +1,5 @@ """Enable :pypi:`eventlet` support for :mod:`asyncio`.""" +import asyncio # noqa: E402,I100,I202 import os os.environ["GEVENT_LOOP"] = "mode.loop._gevent_loop.Loop" @@ -18,7 +19,6 @@ "Eventlet loop requires the aioeventlet library: " "pip install aioeventlet" ) from None -import asyncio # noqa: E402,I100,I202 if asyncio._get_running_loop() is not None: raise RuntimeError("Event loop created before importing eventlet loop!") diff --git a/mode/loop/gevent.py b/mode/loop/gevent.py index 835696a6..14d28fa7 100644 --- a/mode/loop/gevent.py +++ b/mode/loop/gevent.py @@ -1,6 +1,8 @@ """Enable :pypi:`gevent` support for :mod:`asyncio`.""" +import asyncio # noqa: E402,I100,I202 import os import warnings +from typing import Optional, cast # noqa: F401,E402 os.environ["GEVENT_LOOP"] = "mode.loop._gevent_loop.Loop" try: @@ -11,7 +13,6 @@ "Gevent loop requires the gevent library: " "pip install gevent" ) from None gevent.monkey.patch_all() -from typing import Optional, cast # noqa: F401,E402 try: import psycopg2 # noqa: F401 @@ -33,7 +34,6 @@ "Gevent loop requires the aiogevent library: " "pip install aiogevent" ) from None -import asyncio # noqa: E402,I100,I202 if asyncio._get_running_loop() is not None: raise RuntimeError("Event loop created before importing gevent loop!") diff --git a/mode/services.py b/mode/services.py index f075e080..06131980 100644 --- a/mode/services.py +++ b/mode/services.py @@ -692,7 +692,6 @@ async def sleep( await asyncio.wait_for( self._stopped.wait(), timeout=want_seconds(n), - loop=loop or self.loop, ) except asyncio.TimeoutError: pass @@ -716,7 +715,6 @@ async def wait_many( cast(Iterable[Awaitable[Any]], coros), return_when=asyncio.ALL_COMPLETED, timeout=want_seconds(timeout), - loop=self.loop, ) return await self._wait_one(coro, timeout=timeout) @@ -747,7 +745,6 @@ async def wait_first( futures.values(), return_when=asyncio.FIRST_COMPLETED, timeout=timeout, - loop=self.loop, ) for f in done: if f.done() and f.exception() is not None: @@ -784,7 +781,6 @@ async def _wait_stopped(self, timeout: Seconds = None) -> None: [stopped, crashed], return_when=asyncio.FIRST_COMPLETED, timeout=timeout, - loop=self.loop, ) for fut in done: fut.result() # propagate exceptions @@ -971,7 +967,6 @@ async def _wait_for_futures(self, *, timeout: float = None) -> None: await asyncio.wait( self._futures, return_when=asyncio.ALL_COMPLETED, - loop=self.loop, timeout=timeout, ) diff --git a/mode/supervisors.py b/mode/supervisors.py index 0c6c8ad5..98ebd4ac 100644 --- a/mode/supervisors.py +++ b/mode/supervisors.py @@ -168,7 +168,6 @@ async def stop_services(self, services: List[ServiceT]) -> None: # Stop them all simultaneously. await asyncio.gather( *[service.stop() for service in services], - loop=self.loop, ) async def restart_service(self, service: ServiceT) -> None: diff --git a/mode/threads.py b/mode/threads.py index 80e9c5f2..2d59e689 100644 --- a/mode/threads.py +++ b/mode/threads.py @@ -320,11 +320,9 @@ class MethodQueue(Service): mundane_level = "debug" - def __init__( - self, loop: asyncio.AbstractEventLoop, num_workers: int = 2, **kwargs: Any - ) -> None: + def __init__(self, num_workers: int = 2, **kwargs: Any) -> None: super().__init__(**kwargs) - self._queue = asyncio.Queue(loop=self.loop) + self._queue = asyncio.Queue() self._queue_ready = Event(loop=self.loop) self.num_workers = num_workers self._workers = [] diff --git a/mode/utils/logging.py b/mode/utils/logging.py index 1863f46c..96519f08 100644 --- a/mode/utils/logging.py +++ b/mode/utils/logging.py @@ -319,7 +319,7 @@ def format(self, record: logging.LogRecord) -> str: return super().format(record) -class ExtensionFormatter(colorlog.TTYColoredFormatter): # type: ignore +class ExtensionFormatter(colorlog.TTYColoredFormatter): """Formatter that can register callbacks to format args. Extends :pypi:`colorlog`. @@ -331,7 +331,7 @@ def __init__(self, stream: IO = None, **kwargs: Any) -> None: def format(self, record: logging.LogRecord) -> str: self._format_args(record) record.extra = _format_extra(record) # type: ignore - return cast(str, super().format(record)) + return cast(str, super().format(record)) # type: ignore def _format_args(self, record: logging.LogRecord) -> None: format_arg = self.format_arg diff --git a/mode/utils/times.py b/mode/utils/times.py index 05723f78..87e113bb 100644 --- a/mode/utils/times.py +++ b/mode/utils/times.py @@ -148,7 +148,7 @@ async def __aenter__(self) -> "Bucket": if self.raises: raise self.raises() expected_time = self.expected_time() - await asyncio.sleep(expected_time, loop=self.loop) + await asyncio.sleep(expected_time) return self async def __aexit__( diff --git a/mode/worker.py b/mode/worker.py index 367c3da7..1d98ab6c 100644 --- a/mode/worker.py +++ b/mode/worker.py @@ -331,7 +331,7 @@ def _shutdown_loop(self) -> None: raise self.crash_reason from self.crash_reason async def _sentinel_task(self) -> None: - await asyncio.sleep(1.0, loop=self.loop) + await asyncio.sleep(1.0) def _gather_all(self) -> None: # sleeps for at most 10 * 0.1s diff --git a/requirements/flakes.txt b/requirements/flakes.txt index ef1a03a5..1063c4f5 100644 --- a/requirements/flakes.txt +++ b/requirements/flakes.txt @@ -1,10 +1,9 @@ -flake8>=2.5.4 +flake8>=4.0.0 flake8-bandit flake8-bugbear flake8-builtins-unleashed flake8-comprehensions flake8-debugger -flake8-isort flake8-logging-format flake8-mock flake8-pep3101 diff --git a/setup.cfg b/setup.cfg index 09b47f9f..47caec6e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -41,6 +41,9 @@ universal = 1 ignore = N806, N802, N801, N803, I100, I201, I202, B008, W504, G200, S101, E203, E266, E501, W503 enable-extensions = G max-line-length = 88 +per-file-ignores = + t/*: S301,S403 + #[pep257] #ignore = D102,D104,D203,D105,D213 @@ -82,3 +85,4 @@ warn_unused_ignores = True [isort] profile=black +known_first_party=mode diff --git a/setup.py b/setup.py index ed70fc21..1281296b 100644 --- a/setup.py +++ b/setup.py @@ -18,11 +18,11 @@ def _pyimp(): NAME = "mode-streaming" EXTENSIONS = {"eventlet", "gevent", "uvloop"} -E_UNSUPPORTED_PYTHON = "%s 1.0 requires %%s %%s or later!" % (NAME,) +E_UNSUPPORTED_PYTHON = "%s 1.0 requires %%s %%s or later!" % (NAME,) # noqa: S001 PYIMP = _pyimp() if sys.version_info < (3, 6): - raise Exception(E_UNSUPPORTED_PYTHON % (PYIMP, "3.6")) + raise Exception(E_UNSUPPORTED_PYTHON % (PYIMP, "3.6")) # noqa: S001 from pathlib import Path # noqa diff --git a/t/functional/utils/test_queues.py b/t/functional/utils/test_queues.py index 2a172508..d44d8282 100644 --- a/t/functional/utils/test_queues.py +++ b/t/functional/utils/test_queues.py @@ -51,7 +51,7 @@ async def test_suspend_resume__clear_on_resume(self): time_now = monotonic() await queue.put(2) assert monotonic() - time_now > 0.1 - await queue.get() == 2 + assert await queue.get() == 2 @pytest.mark.asyncio async def test_suspend_resume__initially_suspended(self): diff --git a/t/unit/test_services.py b/t/unit/test_services.py index 72c4baea..ce23ee9d 100644 --- a/t/unit/test_services.py +++ b/t/unit/test_services.py @@ -1,5 +1,4 @@ import asyncio -from time import sleep from typing import ContextManager import pytest @@ -123,7 +122,7 @@ class ATaskService(Service): values = [] def __post_init__(self): - self.event = asyncio.Event(loop=self.loop) + self.event = asyncio.Event() @Service.task async def _background_task(self): @@ -343,7 +342,6 @@ async def test__wait_stopped(self, *, service): ], return_when=asyncio.FIRST_COMPLETED, timeout=1.0, - loop=service.loop, ) for fut in done: @@ -553,7 +551,6 @@ async def test_wait_many(self, *, service): [m1, m2], return_when=asyncio.ALL_COMPLETED, timeout=3.34, - loop=service.loop, ) service._wait_one.assert_called_once_with(ANY, timeout=3.34) diff --git a/t/unit/test_threads.py b/t/unit/test_threads.py index 4f7c337e..dccc1f72 100644 --- a/t/unit/test_threads.py +++ b/t/unit/test_threads.py @@ -260,7 +260,7 @@ class test_MethodQueue: @pytest.mark.asyncio async def test_call(self): loop = asyncio.get_event_loop() - queue = MethodQueue(num_workers=2, loop=loop) + queue = MethodQueue(num_workers=2) async with queue: @@ -279,8 +279,8 @@ async def myfun(x, y): @pytest.mark.asyncio async def test_call_raising(self): loop = asyncio.get_event_loop() - queue = MethodQueue(num_workers=2, loop=loop) - all_done = asyncio.Event(loop=loop) + queue = MethodQueue(num_workers=2) + all_done = asyncio.Event() calls = 0 async with queue: @@ -310,11 +310,10 @@ async def myfun(x, y): @pytest.mark.asyncio async def test_cast(self): - loop = asyncio.get_event_loop() - queue = MethodQueue(num_workers=2, loop=loop) + queue = MethodQueue(num_workers=2) calls = 0 - all_done = asyncio.Event(loop=loop) + all_done = asyncio.Event() async with queue: @@ -330,11 +329,10 @@ async def myfun(x, y): @pytest.mark.asyncio async def test_flush(self): - loop = asyncio.get_event_loop() - queue = MethodQueue(num_workers=2, loop=loop) + queue = MethodQueue(num_workers=2) calls = 0 - all_done = asyncio.Event(loop=loop) + all_done = asyncio.Event() async def myfun(x, y): nonlocal calls diff --git a/t/unit/test_worker.py b/t/unit/test_worker.py index 9a2315ed..4bee6b33 100644 --- a/t/unit/test_worker.py +++ b/t/unit/test_worker.py @@ -355,7 +355,7 @@ def patch_shutdown_loop(self, worker, is_running=False): async def test__sentinel_task(self, worker): with patch("asyncio.sleep", AsyncMock()) as sleep: await worker._sentinel_task() - sleep.coro.assert_called_once_with(1.0, loop=worker.loop) + sleep.coro.assert_called_once_with(1.0) def test__gather_all(self, worker): with patch("mode.worker.all_tasks") as all_tasks: diff --git a/tox.ini b/tox.ini index 097c91b8..e277ac51 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = 3.9,3.8,3.7,3.6,flake8,apicheck,typecheck,docstyle,bandit +envlist = 3.10,3.9,3.8,3.7,3.6,flake8,apicheck,typecheck,docstyle,bandit [testenv] deps= @@ -14,6 +14,7 @@ sitepackages = False recreate = False commands = py.test --random-order --open-files -xvv --cov=mode --cov-branch basepython = + 3.10: python3.10 3.9: python3.9 3.8,flake8,typecheck,apicheck,linkcheck,docstyle,bandit: python3.8 3.7: python3.7