diff --git a/CI_REQUIREMENTS.txt b/CI_REQUIREMENTS.txt index 5d1ee86..ec7b2d2 100644 --- a/CI_REQUIREMENTS.txt +++ b/CI_REQUIREMENTS.txt @@ -1,5 +1,6 @@ typing >= 3.6 ; python_version < "3.8" mock; python_version == "2.7" futures>=3.1; python_version == "2.7" +greenlet <= 0.4.13 gevent >= 1.2, <1.3.0 ; platform_python_implementation == "PyPy" gevent >= 1.2 ; platform_python_implementation != "PyPy" diff --git a/threaded/__init__.py b/threaded/__init__.py index 141030d..f0b77da 100644 --- a/threaded/__init__.py +++ b/threaded/__init__.py @@ -46,7 +46,7 @@ try: # pragma: no cover from ._gthreadpooled2 import GThreadPooled, gthreadpooled except ImportError: # pragma: no cover - GThreadPooled = gthreadpooled = None # type: ignore + GThreadPooled = gthreadpooled = None # pylint: enable=no-name-in-module diff --git a/threaded/_base_gthreadpooled.py b/threaded/_base_gthreadpooled.py index c05ed4b..52ad620 100644 --- a/threaded/_base_gthreadpooled.py +++ b/threaded/_base_gthreadpooled.py @@ -39,7 +39,7 @@ class BaseGThreadPooled(_base_threaded.APIPooled): # pylint: disable=arguments-differ @classmethod def configure( - cls, + cls, # type: typing.Type[BaseGThreadPooled] max_workers=None, # type: typing.Optional[int] hub=None # type: typing.Optional[gevent.hub.Hub] ): # type: (...) -> None diff --git a/threaded/_base_gthreadpooled.pyi b/threaded/_base_gthreadpooled.pyi index b61fb09..c648e0b 100644 --- a/threaded/_base_gthreadpooled.pyi +++ b/threaded/_base_gthreadpooled.pyi @@ -6,7 +6,7 @@ from . import _base_threaded class BaseGThreadPooled(_base_threaded.APIPooled): @classmethod def configure( - cls, + cls: typing.Type[BaseGThreadPooled], max_workers: typing.Optional[int]=..., hub: typing.Optional[gevent.hub.Hub]=... ) -> None: ... diff --git a/threaded/_base_threaded.py b/threaded/_base_threaded.py index 5943695..d486676 100644 --- a/threaded/_base_threaded.py +++ b/threaded/_base_threaded.py @@ -16,6 +16,7 @@ from __future__ import absolute_import +import abc # noinspection PyCompatibility import concurrent.futures import threading @@ -43,7 +44,7 @@ def cpu_count(): # type: () -> int ) -class APIPooled(_class_decorator.BaseDecorator): +class APIPooled(six.with_metaclass(abc.ABCMeta, _class_decorator.BaseDecorator)): """API description for pooled.""" __slots__ = () @@ -52,7 +53,7 @@ class APIPooled(_class_decorator.BaseDecorator): @classmethod def configure( - cls, + cls, # type: typing.Type[APIPooled] max_workers=None, # type: typing.Optional[int] ): # type: (...) -> None """Pool executor create and configure. @@ -63,7 +64,7 @@ def configure( raise NotImplementedError() # pragma: no cover @classmethod - def shutdown(cls): # type: () -> None + def shutdown(cls): # type: (typing.Type[APIPooled]) -> None """Shutdown executor.""" raise NotImplementedError() # pragma: no cover @@ -82,7 +83,7 @@ class BasePooled(APIPooled): @classmethod def configure( - cls, + cls, # type: typing.Type[BasePooled] max_workers=None, # type: typing.Optional[int] ): # type: (...) -> None """Pool executor create and configure. @@ -100,7 +101,7 @@ def configure( ) @classmethod - def shutdown(cls): # type: () -> None + def shutdown(cls): # type: (typing.Type[BasePooled]) -> None """Shutdown executor.""" if cls.__executor is not None: cls.__executor.shutdown() diff --git a/threaded/_base_threaded.pyi b/threaded/_base_threaded.pyi index 44c1496..f46c796 100644 --- a/threaded/_base_threaded.pyi +++ b/threaded/_base_threaded.pyi @@ -1,3 +1,4 @@ +import abc import concurrent.futures import threading import typing @@ -5,22 +6,22 @@ from . import _class_decorator def cpu_count() -> int: ... -class APIPooled(_class_decorator.BaseDecorator): +class APIPooled(_class_decorator.BaseDecorator, metaclass=abc.ABCMeta): @classmethod - def configure(cls, max_workers: typing.Optional[int]=...) -> None: ... + def configure(cls: typing.Type[APIPooled], max_workers: typing.Optional[int]=...) -> None: ... @classmethod - def shutdown(cls) -> None: ... + def shutdown(cls: typing.Type[APIPooled]) -> None: ... @property def executor(self) -> typing.Any: ... class BasePooled(APIPooled): @classmethod - def configure(cls, max_workers: typing.Optional[int]=...) -> None: ... + def configure(cls: typing.Type[BasePooled], max_workers: typing.Optional[int]=...) -> None: ... @classmethod - def shutdown(cls) -> None: ... + def shutdown(cls: typing.Type[BasePooled]) -> None: ... @property def executor(self) -> ThreadPoolExecutor: ... diff --git a/threaded/_class_decorator.py b/threaded/_class_decorator.py index 6dadbe8..ac18778 100644 --- a/threaded/_class_decorator.py +++ b/threaded/_class_decorator.py @@ -21,13 +21,12 @@ import abc import functools -import sys import typing # noqa # pylint: disable=unused-import -PY3 = sys.version_info[:2] > (3, 0) # type: bool +import six -class BaseDecorator(object): +class BaseDecorator(six.with_metaclass(abc.ABCMeta, object)): """Base class for decorators. Implements wrapping and __call__, wrapper getter is abstract. @@ -81,7 +80,7 @@ def __init__( self.__func = func # type: typing.Optional[typing.Callable] if self.__func is not None: functools.update_wrapper(self, self.__func) - if not PY3: # pragma: no cover + if not six.PY3: # pragma: no cover self.__wrapped__ = self.__func # type: typing.Callable # pylint: enable=assigning-non-slot # noinspection PyArgumentList diff --git a/threaded/_class_decorator.pyi b/threaded/_class_decorator.pyi index 0dc4410..fe35d2f 100644 --- a/threaded/_class_decorator.pyi +++ b/threaded/_class_decorator.pyi @@ -1,9 +1,8 @@ import abc import typing -PY3: bool -class BaseDecorator: +class BaseDecorator(object, metaclass=abc.ABCMeta): __wrapped__: typing.Optional[typing.Callable] = ... def __init__(self, func: typing.Optional[typing.Callable]=...) -> None: ... diff --git a/threaded/_gthreadpooled2.py b/threaded/_gthreadpooled2.py index 91ddd82..779ac6b 100644 --- a/threaded/_gthreadpooled2.py +++ b/threaded/_gthreadpooled2.py @@ -19,7 +19,7 @@ from __future__ import absolute_import -import typing +import typing # noqa # pylint: disable=unused-import import gevent.event # noqa # pylint: disable=unused-import @@ -37,24 +37,8 @@ class GThreadPooled(_base_gthreadpooled.BaseGThreadPooled): __slots__ = () -# pylint: disable=unused-argument, function-redefined -@typing.overload -def gthreadpooled( - func=None # type: None -): # type: (...) -> GThreadPooled - """Post function to gevent.threadpool.ThreadPool.""" - - -@typing.overload # noqa: F811 -def gthreadpooled( - func # type: typing.Callable -): # type: (...) -> typing.Callable[..., gevent.event.AsyncResult] - """Post function to gevent.threadpool.ThreadPool.""" -# pylint: enable=unused-argument - - # pylint: disable=unexpected-keyword-arg, no-value-for-parameter -def gthreadpooled( # noqa: F811 +def gthreadpooled( func=None # type: typing.Optional[typing.Callable] ): # type: (...) -> typing.Union[GThreadPooled, typing.Callable[..., gevent.event.AsyncResult]] """Post function to gevent.threadpool.ThreadPool. @@ -66,4 +50,4 @@ def gthreadpooled( # noqa: F811 if func is None: return GThreadPooled(func=func) return GThreadPooled(func=None)(func) -# pylint: enable=unexpected-keyword-arg, no-value-for-parameter, function-redefined +# pylint: enable=unexpected-keyword-arg, no-value-for-parameter diff --git a/threaded/_gthreadpooled2.pyi b/threaded/_gthreadpooled2.pyi index 1f05d4c..84d9427 100644 --- a/threaded/_gthreadpooled2.pyi +++ b/threaded/_gthreadpooled2.pyi @@ -4,4 +4,9 @@ from . import _base_gthreadpooled class GThreadPooled(_base_gthreadpooled.BaseGThreadPooled): ... -def gthreadpooled(func: typing.Optional[typing.Callable]=...) -> typing.Union[GThreadPooled, typing.Callable[..., gevent.event.AsyncResult]]: ... + +@typing.overload +def gthreadpooled(func: typing.Callable) -> typing.Callable[..., gevent.event.AsyncResult]: ... + +@typing.overload +def gthreadpooled(func: None=...) -> GThreadPooled: ... diff --git a/threaded/_gthreadpooled3.py b/threaded/_gthreadpooled3.py index fa8ea37..1fad00d 100644 --- a/threaded/_gthreadpooled3.py +++ b/threaded/_gthreadpooled3.py @@ -64,24 +64,8 @@ def wrapper( return wrapper -# pylint: disable=unused-argument, function-redefined -@typing.overload -def gthreadpooled( - func=None # type: None -): # type: (...) -> GThreadPooled - """Post function to gevent.threadpool.ThreadPool.""" - - -@typing.overload # noqa: F811 -def gthreadpooled( - func # type: typing.Callable -): # type: (...) -> typing.Callable[..., gevent.event.AsyncResult] - """Post function to gevent.threadpool.ThreadPool.""" -# pylint: enable=unused-argument - - # pylint: disable=unexpected-keyword-arg, no-value-for-parameter -def gthreadpooled( # noqa: F811 +def gthreadpooled( func: typing.Optional[typing.Callable] = None ) -> typing.Union[GThreadPooled, typing.Callable[..., gevent.event.AsyncResult]]: """Post function to gevent.threadpool.ThreadPool. @@ -93,4 +77,4 @@ def gthreadpooled( # noqa: F811 if func is None: return GThreadPooled(func=func) return GThreadPooled(func=None)(func) -# pylint: enable=unexpected-keyword-arg, no-value-for-parameter, function-redefined +# pylint: enable=unexpected-keyword-arg, no-value-for-parameter diff --git a/threaded/_gthreadpooled3.pyi b/threaded/_gthreadpooled3.pyi index 1f05d4c..84d9427 100644 --- a/threaded/_gthreadpooled3.pyi +++ b/threaded/_gthreadpooled3.pyi @@ -4,4 +4,9 @@ from . import _base_gthreadpooled class GThreadPooled(_base_gthreadpooled.BaseGThreadPooled): ... -def gthreadpooled(func: typing.Optional[typing.Callable]=...) -> typing.Union[GThreadPooled, typing.Callable[..., gevent.event.AsyncResult]]: ... + +@typing.overload +def gthreadpooled(func: typing.Callable) -> typing.Callable[..., gevent.event.AsyncResult]: ... + +@typing.overload +def gthreadpooled(func: None=...) -> GThreadPooled: ... diff --git a/threaded/_py3_helpers.py b/threaded/_py3_helpers.py index 1ca4f56..0b741aa 100644 --- a/threaded/_py3_helpers.py +++ b/threaded/_py3_helpers.py @@ -25,8 +25,8 @@ def get_loop( - self, - *args, **kwargs + self: typing.Any, + *args: typing.Tuple, **kwargs: typing.Dict ) -> typing.Optional[asyncio.AbstractEventLoop]: """Get event loop in decorator class.""" if callable(self.loop_getter): diff --git a/threaded/_py3_helpers.pyi b/threaded/_py3_helpers.pyi index c4537dc..9262a45 100644 --- a/threaded/_py3_helpers.pyi +++ b/threaded/_py3_helpers.pyi @@ -1,5 +1,5 @@ import asyncio import typing -def get_loop(self, *args, **kwargs) -> typing.Optional[asyncio.AbstractEventLoop]: ... +def get_loop(self: typing.Any, *args: typing.Tuple, **kwargs: typing.Dict) -> typing.Optional[asyncio.AbstractEventLoop]: ... def await_if_required(target: typing.Callable) -> typing.Callable[..., typing.Any]: ... diff --git a/threaded/_threaded2.py b/threaded/_threaded2.py index ef441a6..ec3d58d 100644 --- a/threaded/_threaded2.py +++ b/threaded/_threaded2.py @@ -21,7 +21,7 @@ import concurrent.futures # noqa # pylint: disable=unused-import import threading # noqa # pylint: disable=unused-import -import typing +import typing # noqa # pylint: disable=unused-import from . import _base_threaded @@ -45,24 +45,8 @@ class Threaded(_base_threaded.BaseThreaded): __slots__ = () -# pylint: disable=unused-argument, function-redefined -@typing.overload -def threadpooled( - func=None # type: None -): # type: (...) -> ThreadPooled - """Post function to ThreadPoolExecutor.""" - - -@typing.overload # noqa: F811 -def threadpooled( - func # type: typing.Callable -): # type: (...) -> typing.Callable[..., concurrent.futures.Future] - """Post function to ThreadPoolExecutor.""" -# pylint: enable=unused-argument - - # pylint: disable=unexpected-keyword-arg, no-value-for-parameter -def threadpooled( # noqa: F811 +def threadpooled( func=None # type: typing.Optional[typing.Callable] ): # type: (...) -> typing.Union[ThreadPooled, typing.Callable[..., concurrent.futures.Future]] """Post function to ThreadPoolExecutor. @@ -76,27 +60,7 @@ def threadpooled( # noqa: F811 return ThreadPooled(func=None)(func) -# pylint: disable=unused-argument -@typing.overload -def threaded( - name=None, # type: typing.Optional[str] - daemon=False, # type: bool - started=False # type: bool -): # type: (...) -> Threaded - """Run function in separate thread.""" - - -@typing.overload # noqa: F811 def threaded( - name, # type: typing.Callable - daemon=False, # type: bool - started=False # type: bool -): # type: (...) -> typing.Callable[..., threading.Thread] - """Run function in separate thread.""" -# pylint: enable=unused-argument - - -def threaded( # noqa: F811 name=None, # type: typing.Optional[typing.Union[str, typing.Callable]] daemon=False, # type: bool started=False # type: bool @@ -120,4 +84,4 @@ def threaded( # noqa: F811 ) return Threaded(name=name, daemon=daemon, started=started)(func) return Threaded(name=name, daemon=daemon, started=started) -# pylint: enable=unexpected-keyword-arg, no-value-for-parameter, function-redefined +# pylint: enable=unexpected-keyword-arg, no-value-for-parameter diff --git a/threaded/_threaded2.pyi b/threaded/_threaded2.pyi index 2a1b3f3..cd52769 100644 --- a/threaded/_threaded2.pyi +++ b/threaded/_threaded2.pyi @@ -6,10 +6,18 @@ from . import _base_threaded class ThreadPooled(_base_threaded.BasePooled): ... class Threaded(_base_threaded.BaseThreaded): ... -def threadpooled(func: typing.Optional[typing.Callable]=...) -> typing.Union[ThreadPooled, typing.Callable[..., concurrent.futures.Future]]: ... -def threaded( - name: typing.Optional[typing.Union[str, typing.Callable]]=..., - daemon: bool=..., - started: bool=... -) -> typing.Union[Threaded, typing.Callable[..., threading.Thread]]: ... + + +@typing.overload +def threadpooled(func: typing.Callable) -> typing.Callable[..., concurrent.futures.Future]: ... + +@typing.overload +def threadpooled(func: None=...) -> ThreadPooled: ... + + +@typing.overload +def threaded(name: typing.Callable, daemon: bool=..., started: bool=...) -> typing.Callable[..., threading.Thread]: ... + +@typing.overload +def threaded(name: typing.Optional[str]=..., daemon: bool=..., started: bool=...) -> Threaded: ... diff --git a/threaded/_threaded3.py b/threaded/_threaded3.py index 1eb59bb..5e60811 100644 --- a/threaded/_threaded3.py +++ b/threaded/_threaded3.py @@ -309,49 +309,8 @@ def __repr__(self): ) # pragma: no cover -# pylint: disable=unused-argument, function-redefined -@typing.overload -def threadpooled( - func: None = None, - *, - loop_getter: typing.Union[ - None, - typing.Callable[..., asyncio.AbstractEventLoop], - asyncio.AbstractEventLoop - ]=None, - loop_getter_need_context: bool = False -) -> ThreadPooled: - """Post function to ThreadPoolExecutor.""" - - -@typing.overload # noqa: F811 -def threadpooled( - func: typing.Callable, - *, - loop_getter: None = None, - loop_getter_need_context: bool = False - -) -> typing.Callable[..., concurrent.futures.Future]: - """Post function to ThreadPoolExecutor.""" - - -@typing.overload # noqa: F811 -def threadpooled( - func: typing.Callable, - *, - loop_getter: typing.Union[ - typing.Callable[..., asyncio.AbstractEventLoop], - asyncio.AbstractEventLoop - ], - loop_getter_need_context: bool = False - -) -> typing.Callable[..., asyncio.Task]: - """Post function to ThreadPoolExecutor.""" -# pylint: enable=unused-argument - - # pylint: disable=unexpected-keyword-arg, no-value-for-parameter -def threadpooled( # noqa: F811 +def threadpooled( func: typing.Optional[typing.Callable] = None, *, loop_getter: typing.Union[ @@ -388,27 +347,7 @@ def threadpooled( # noqa: F811 )(func) -# pylint: disable=unused-argument -@typing.overload def threaded( - name: typing.Optional[str] = None, - daemon: bool = False, - started: bool = False -): # type: (...) -> Threaded - """Run function in separate thread.""" - - -@typing.overload # noqa: F811 -def threaded( - name: typing.Callable, - daemon: bool = False, - started: bool = False -) -> typing.Callable[..., threading.Thread]: - """Run function in separate thread.""" -# pylint: enable=unused-argument - - -def threaded( # noqa: F811 name: typing.Optional[typing.Union[str, typing.Callable]] = None, daemon: bool = False, started: bool = False @@ -434,35 +373,7 @@ def threaded( # noqa: F811 return Threaded(name=name, daemon=daemon, started=started) -# pylint: disable=unused-argument -@typing.overload -def asynciotask( - func=None, - *, - loop_getter: typing.Union[ - typing.Callable[..., asyncio.AbstractEventLoop], - asyncio.AbstractEventLoop - ]=asyncio.get_event_loop, - loop_getter_need_context: bool = False -) -> AsyncIOTask: - """Wrap function in future and return.""" - - -@typing.overload # noqa: F811 def asynciotask( - func: typing.Callable, - *, - loop_getter: typing.Union[ - typing.Callable[..., asyncio.AbstractEventLoop], - asyncio.AbstractEventLoop - ]=asyncio.get_event_loop, - loop_getter_need_context: bool = False -) -> typing.Callable[..., asyncio.Task]: - """Wrap function in future and return.""" -# pylint: enable=unused-argument - - -def asynciotask( # noqa: F811 func: typing.Optional[typing.Callable] = None, *, loop_getter: typing.Union[ @@ -495,4 +406,4 @@ def asynciotask( # noqa: F811 loop_getter=loop_getter, loop_getter_need_context=loop_getter_need_context )(func) -# pylint: enable=unexpected-keyword-arg, no-value-for-parameter, function-redefined +# pylint: enable=unexpected-keyword-arg, no-value-for-parameter diff --git a/threaded/_threaded3.pyi b/threaded/_threaded3.pyi index 7853302..12c7f6f 100644 --- a/threaded/_threaded3.pyi +++ b/threaded/_threaded3.pyi @@ -41,22 +41,66 @@ class AsyncIOTask(_class_decorator.BaseDecorator): def _get_function_wrapper(self, func: typing.Callable) -> typing.Callable[..., asyncio.Task]: ... + +@typing.overload +def threadpooled( + func: typing.Callable, + *, + loop_getter: None = ..., + loop_getter_need_context: bool = ... + +) -> typing.Callable[..., concurrent.futures.Future]: ... + +@typing.overload def threadpooled( - func: typing.Optional[typing.Callable]=..., + func: typing.Callable, *, - loop_getter: typing.Union[None, typing.Callable[..., asyncio.AbstractEventLoop], asyncio.AbstractEventLoop]=..., - loop_getter_need_context: bool=... -) -> typing.Union[ThreadPooled, typing.Callable[..., typing.Union[concurrent.futures.Future, asyncio.Task]]]: ... + loop_getter: typing.Union[ + typing.Callable[..., asyncio.AbstractEventLoop], + asyncio.AbstractEventLoop + ], + loop_getter_need_context: bool = ... + +) -> typing.Callable[..., asyncio.Task]: ... -def threaded( - name: typing.Optional[typing.Union[str, typing.Callable]]=..., - daemon: bool=..., - started: bool=... -) -> typing.Union[Threaded, typing.Callable[..., threading.Thread]]: ... +@typing.overload +def threadpooled( + func: None = ..., + *, + loop_getter: typing.Union[ + None, + typing.Callable[..., asyncio.AbstractEventLoop], + asyncio.AbstractEventLoop + ]=..., + loop_getter_need_context: bool = ... +) -> ThreadPooled: ... + + +@typing.overload +def threaded(name: typing.Callable, daemon: bool = ..., started: bool = ...) -> typing.Callable[..., threading.Thread]: ... + +@typing.overload +def threaded(name: typing.Optional[str] = ..., daemon: bool = ..., started: bool = ...) -> Threaded: ... + + +@typing.overload +def asynciotask( + func: None= ..., + *, + loop_getter: typing.Union[ + typing.Callable[..., asyncio.AbstractEventLoop], + asyncio.AbstractEventLoop + ]=asyncio.get_event_loop, + loop_getter_need_context: bool = ... +) -> AsyncIOTask: ... +@typing.overload def asynciotask( - func: typing.Optional[typing.Callable]=..., + func: typing.Callable, *, - loop_getter: typing.Union[typing.Callable[..., asyncio.AbstractEventLoop], asyncio.AbstractEventLoop]=..., - loop_getter_need_context: bool=... -) -> typing.Union[AsyncIOTask, typing.Callable[..., asyncio.Task]]: ... + loop_getter: typing.Union[ + typing.Callable[..., asyncio.AbstractEventLoop], + asyncio.AbstractEventLoop + ]=asyncio.get_event_loop, + loop_getter_need_context: bool = ... +) -> typing.Callable[..., asyncio.Task]: ... diff --git a/tox.ini b/tox.ini index 300cdb4..9ada10a 100644 --- a/tox.ini +++ b/tox.ini @@ -86,7 +86,7 @@ commands = pip install ./ -vvv -U [testenv:pylint] deps = - pylint + pylint < 2.0 -r{toxinidir}/CI_REQUIREMENTS.txt commands = pylint threaded @@ -129,3 +129,9 @@ deps = .[gevent] pipdeptree commands = pipdeptree + +[testenv:mypy] +deps = + mypy>=0.620 + -r{toxinidir}/CI_REQUIREMENTS.txt +commands = mypy --strict threaded