diff --git a/.travis.yml b/.travis.yml index 032054674..acdee4e81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,6 +60,16 @@ jobs: - make flake - <<: *FLAKE python: "3.7" + - &MYPY + name: mypy + cache: false + python: "3.6" + install: + - pip install -r tests/requirements.txt + script: + - make mypy + - <<: *MYPY + python: "3.7" # Run examples Python 3.6, 3.7 and system Redis - &EXAMPLES stage: examples diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index d3a89d09d..dd1efbbc0 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -27,6 +27,7 @@ Marijn Giesen Martin Michael Käufl Nickolai Novik +Oleg Butuzov Pau Freixes Paul Colomiets Samuel Colvin diff --git a/Makefile b/Makefile index 22b9d310f..9a7740d38 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ PYTHON ?= python3 FLAKE ?= flake8 PYTEST ?= pytest +MYPY ?= mypy REDIS_VERSION ?= "$(shell redis-cli INFO SERVER | sed -n 2p)" REDIS_TAGS ?= 2.6.17 2.8.22 3.0.7 3.2.8 4.0.11 5.0.1 @@ -34,6 +35,9 @@ flake: @echo "Job is not configured to run on $(PYTHON_IMPL); skipped." endif +mypy: + $(MYPY) aioredis --ignore-missing-imports + test: $(PYTEST) diff --git a/aioredis/errors.py b/aioredis/errors.py index b73e2e424..3a41a25d4 100644 --- a/aioredis/errors.py +++ b/aioredis/errors.py @@ -1,3 +1,5 @@ +from typing import Optional, Sequence + __all__ = [ 'RedisError', 'ProtocolError', @@ -28,7 +30,7 @@ class ProtocolError(RedisError): class ReplyError(RedisError): """Raised for redis error replies (-ERR).""" - MATCH_REPLY = None + MATCH_REPLY = None # type: Optional[Sequence[str]] def __new__(cls, msg, *args): for klass in cls.__subclasses__(): @@ -37,6 +39,7 @@ def __new__(cls, msg, *args): return super().__new__(cls, msg, *args) + class MaxClientsError(ReplyError): """Raised for redis server when the maximum number of client has been reached.""" diff --git a/aioredis/parser.py b/aioredis/parser.py index 99699e5fd..c3f05d9ce 100644 --- a/aioredis/parser.py +++ b/aioredis/parser.py @@ -1,23 +1,24 @@ from .errors import ProtocolError, ReplyError +from typing import Optional, Generator, Callable, Iterator __all__ = [ 'Reader', 'PyReader', ] - class PyReader: """Pure-Python Redis protocol parser that follows hiredis.Reader interface (except setmaxbuf/getmaxbuf). """ - def __init__(self, protocolError=ProtocolError, replyError=ReplyError, - encoding=None): + def __init__(self, protocolError: Callable = ProtocolError, + replyError: Callable = ReplyError, + encoding: Optional[str] = None): if not callable(protocolError): raise TypeError("Expected a callable") if not callable(replyError): raise TypeError("Expected a callable") self._parser = Parser(protocolError, replyError, encoding) - def feed(self, data, o=0, l=-1): + def feed(self, data, o: int = 0, l: int = -1): """Feed data to parser.""" if l == -1: l = len(data) - o @@ -35,31 +36,33 @@ def gets(self): """ return self._parser.parse_one() - def setmaxbuf(self, size): + def setmaxbuf(self, size: Optional[int] ) -> None: """No-op.""" pass - def getmaxbuf(self): + def getmaxbuf(self) -> int: """No-op.""" return 0 class Parser: - def __init__(self, protocolError, replyError, encoding): - self.buf = bytearray() - self.pos = 0 - self.protocolError = protocolError - self.replyError = replyError - self.encoding = encoding + def __init__(self, protocolError: Callable, + replyError: Callable, encoding: Optional[str] ): + + self.buf = bytearray() # type: bytearray + self.pos = 0 # type: int + self.protocolError = protocolError # type: Callable + self.replyError = replyError # type: Callable + self.encoding = encoding # type: Optional[str] self._err = None - self._gen = None + self._gen = None # type: Optional[Generator] - def waitsome(self, size): + def waitsome(self, size: int) -> Iterator[bool]: # keep yielding false until at least `size` bytes added to buf. while len(self.buf) < self.pos+size: yield False - def waitany(self): + def waitany(self) -> Iterator[bool]: yield from self.waitsome(len(self.buf) + 1) def readone(self): @@ -69,7 +72,7 @@ def readone(self): self.pos += 1 return val - def readline(self, size=None): + def readline(self, size: Optional[int] = None): if size is not None: if len(self.buf) < size + 2 + self.pos: yield from self.waitsome(size + 2) @@ -96,7 +99,7 @@ def error(self, msg): self._err = self.protocolError(msg) return self._err - def parse(self, is_bulk=False): + def parse(self, is_bulk: bool = False): if self._err is not None: raise self._err ctl = yield from self.readone() @@ -156,7 +159,6 @@ def parse_one(self): else: return False - try: import hiredis Reader = hiredis.Reader diff --git a/tests/requirements.txt b/tests/requirements.txt index a88d2f006..7b820b33d 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -5,3 +5,4 @@ pytest==4.1.1 pytest-cov==2.6.1 pytest-xdist==1.26.0 async-timeout==3.0.1 +mypy