Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run MyPy on CI #48

Merged
merged 13 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ matrix:
env: ENV=flake8
script:
- dephell venv run --env=$ENV

- python: "3.7"
env: ENV=typing
script:
- dephell venv run --env=$ENV
4 changes: 2 additions & 2 deletions deal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
pre, pure, raises, reason, require, safe, silent,
)
from ._exceptions import * # noQA
from ._imports import activate, module_load
from ._schemes import Scheme
from ._state import reset, switch
from ._imports import module_load, activate
from ._testing import TestCase, cases


Expand All @@ -46,14 +46,14 @@
'offline',
'post',
'pre',
'pure',
'raises',
'reason',
'safe',
'silent',

# aliases
'invariant',
'pure',
'require',

# module level
Expand Down
11 changes: 7 additions & 4 deletions deal/_decorators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
from asyncio import iscoroutinefunction
from contextlib import suppress
from functools import update_wrapper
from typing import Callable, Type
from typing import Callable

# external
import vaa

# app
Expand All @@ -16,8 +17,8 @@
class Base:
exception: ExceptionType = ContractError

def __init__(self, validator: Callable, *, message: str = None,
exception: Type[ExceptionType] = None, debug: bool = False):
def __init__(self, validator, *, message: str = None,
exception: ExceptionType = None, debug: bool = False):
"""
Step 1. Set contract (validator).
"""
Expand All @@ -29,7 +30,9 @@ def __init__(self, validator: Callable, *, message: str = None,
self.exception = self.exception(message) # type: ignore

@staticmethod
def _make_validator(validator, message: str):
def _make_validator(validator, message: str = None):
if validator is None:
return None
# implicitly wrap in vaa all external validators
with suppress(TypeError):
return vaa.wrap(validator, simple=False)
Expand Down
6 changes: 3 additions & 3 deletions deal/_decorators/inv.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def __setattr__(self, name: str, value):
class Invariant(Base):
exception: ExceptionType = InvContractError

def validate(self, obj) -> None:
def validate(self, obj) -> None: # type: ignore
"""
Step 6. Process contract (validator)
"""
Expand All @@ -81,15 +81,15 @@ def validate_chain(self, *args, **kwargs) -> None:
self.validate(*args, **kwargs)
self.child_validator(*args, **kwargs)

def __call__(self, _class: type):
def __call__(self, _class: type): # type: ignore
"""
Step 2. Return wrapped class.
"""
# patch class parents and add method for validation

# if already invarianted
if hasattr(_class, '_validate_base'):
self.child_validator = _class._validate_base
self.child_validator = _class._validate_base # type: ignore
patched_class = type(
_class.__name__,
(_class, ),
Expand Down
2 changes: 1 addition & 1 deletion deal/_decorators/offline.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, *, message: str = None, exception: ExceptionType = None, debu
Step 1. Init params.
"""
super().__init__(
validator=None,
validator=None, # type: ignore
message=message,
exception=exception,
debug=debug,
Expand Down
2 changes: 1 addition & 1 deletion deal/_decorators/raises.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def __init__(self, *exceptions, message: str = None, exception: ExceptionType =
"""
self.exceptions: Tuple[Type[Exception], ...] = exceptions
super().__init__(
validator=None,
validator=None, # type: ignore
message=message,
exception=exception,
debug=debug,
Expand Down
25 changes: 14 additions & 11 deletions deal/_imports.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# built-in
import ast
import sys
from _frozen_importlib_external import PathFinder
from types import ModuleType
from typing import Callable, Optional, List
from typing import Any, Callable, List, Optional

from .linter._extractors.common import get_name
# project
from _frozen_importlib_external import PathFinder

# app
from . import _aliases
from ._state import state
from .linter._extractors.common import get_name


def _enabled(debug: bool = False) -> bool:
Expand Down Expand Up @@ -60,10 +63,10 @@ def exec_module(self, module: ModuleType) -> None:

@staticmethod
def _get_contracts(tree: ast.Module) -> List[ast.AST]:
for node in tree.body:
if not type(node) is ast.Expr:
for node in tree.body: # type: Any
if type(node) is not ast.Expr:
continue
if not type(node.value) is ast.Call:
if type(node.value) is not ast.Call:
continue
if get_name(node.value.func) != 'deal.module_load':
continue
Expand All @@ -74,12 +77,12 @@ def _get_contracts(tree: ast.Module) -> List[ast.AST]:
def _exec_contract(cls, node: ast.AST) -> Optional[Callable]:
"""Get AST node and return a contract function
"""
if type(node) is ast.Call and not node.args:
return cls._exec_contract(node.func)
if type(node) is ast.Call and not node.args: # type: ignore
return cls._exec_contract(node.func) # type: ignore

if not isinstance(node, ast.Attribute):
return None
if node.value.id != 'deal':
if node.value.id != 'deal': # type: ignore
return None
contract = getattr(_aliases, node.attr, None)
if contract is None:
Expand Down Expand Up @@ -109,7 +112,7 @@ def activate(debug: bool = False) -> bool:
if DealFinder in sys.meta_path:
return False
index = sys.meta_path.index(PathFinder)
sys.meta_path[index] = DealFinder
sys.meta_path[index] = DealFinder # type: ignore
return True


Expand All @@ -118,6 +121,6 @@ def deactivate() -> bool:
"""
if DealFinder not in sys.meta_path:
return False
index = sys.meta_path.index(DealFinder)
index = sys.meta_path.index(DealFinder) # type: ignore
sys.meta_path[index] = PathFinder
return True
1 change: 1 addition & 0 deletions deal/linter/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# app
from ._checker import Checker


Expand Down
3 changes: 3 additions & 0 deletions deal/linter/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# built-in
import sys

# app
from ._cli import main


if __name__ == '__main__':
exit(main(sys.argv[1:]))
29 changes: 20 additions & 9 deletions deal/linter/_checker.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
# built-in
import ast
import typing
from pathlib import Path

from astroid import AstroidSyntaxError

# app
from ._error import Error
from ._func import Func
from ._rules import rules, Required
from ._rules import Required, rules


class Checker:
name = 'deal'
version = '1.0.0'
_tree = None
_rules = rules

def __init__(self, tree: ast.AST, file_tokens=None, filename: str = 'stdin'):
def __init__(self, tree: ast.Module, file_tokens=None, filename: str = 'stdin'):
self._tree = tree
self._filename = filename

@property
def version(self):
import deal

return deal.__version__

def run(self) -> typing.Iterator[tuple]:
for error in self.get_errors():
yield tuple(error) + (type(self),) # type: ignore

def get_errors(self) -> typing.Iterator[Error]:
def get_funcs(self) -> typing.List['Func']:
if self._filename == 'stdin':
funcs = Func.from_ast(tree=self._tree)
else:
funcs = Func.from_path(path=Path(self._filename))
return Func.from_ast(tree=self._tree)
try:
return Func.from_path(path=Path(self._filename))
except AstroidSyntaxError:
return Func.from_ast(tree=self._tree)

for func in funcs:
def get_errors(self) -> typing.Iterator[Error]:
for func in self.get_funcs():
for rule in self._rules:
if rule.required != Required.FUNC:
continue
Expand Down
6 changes: 4 additions & 2 deletions deal/linter/_cli.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# built-in
import ast
import json
from argparse import ArgumentParser
from pathlib import Path
from textwrap import dedent, indent
from typing import Iterable, Iterator, Union
from typing import Iterable, Iterator, Sequence, Union

# app
from ._checker import Checker


Expand Down Expand Up @@ -65,7 +67,7 @@ def get_parser() -> ArgumentParser:
return parser


def main(argv: Iterable) -> int:
def main(argv: Sequence[str]) -> int:
parser = get_parser()
args = parser.parse_args(argv)
prev = None
Expand Down
3 changes: 3 additions & 0 deletions deal/linter/_contract.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# built-in
import ast
import builtins
import enum

# external
import astroid

# app
from ._extractors import get_name


Expand Down
2 changes: 2 additions & 0 deletions deal/linter/_error.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# built-in
import typing


ERROR_FORMAT = 'DEAL{code:03d}: {text}'


Expand Down
1 change: 1 addition & 0 deletions deal/linter/_extractors/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# app
from .common import get_name
from .contracts import get_contracts
from .exceptions import get_exceptions
Expand Down
12 changes: 10 additions & 2 deletions deal/linter/_extractors/common.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# built-in
import ast
from types import SimpleNamespace
from typing import Optional

# external
import astroid


Expand Down Expand Up @@ -57,8 +59,14 @@ def get_name(expr) -> Optional[str]:
return expr.name

if isinstance(expr, astroid.Attribute):
return get_name(expr.expr) + '.' + expr.attrname
left = get_name(expr.expr)
if left is None:
return None
return left + '.' + expr.attrname
if isinstance(expr, ast.Attribute):
return get_name(expr.value) + '.' + expr.attr
left = get_name(expr.value)
if left is None:
return None
return left + '.' + expr.attr

return None
7 changes: 5 additions & 2 deletions deal/linter/_extractors/contracts.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from typing import Tuple, Iterator
# built-in
from typing import Iterator, Tuple

# external
import astroid

from .common import get_name, TOKENS
# app
from .common import TOKENS, get_name


SUPPORTED_CONTRACTS = {'deal.post', 'deal.raises', 'deal.silent'}
Expand Down
12 changes: 9 additions & 3 deletions deal/linter/_extractors/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# built-in
import ast
import builtins
from typing import Iterator

# external
import astroid

from .common import traverse, Token, TOKENS, get_name
# app
from .common import TOKENS, Token, get_name, traverse


def get_exceptions(body: list, *, dive: bool = True) -> Iterator[Token]:
Expand All @@ -19,8 +22,11 @@ def get_exceptions(body: list, *, dive: bool = True) -> Iterator[Token]:
# explicit raise
if isinstance(expr, TOKENS.RAISE):
name = get_name(expr.exc)
# raise instance
if not name and isinstance(expr.exc, TOKENS.CALL):
if not name:
# raised a value, too tricky
if not isinstance(expr.exc, TOKENS.CALL):
continue
# raised an instance of an exception
name = get_name(expr.exc.func)
if not name or name[0].islower():
continue
Expand Down
5 changes: 4 additions & 1 deletion deal/linter/_extractors/imports.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# built-in
import ast
from typing import Iterator

# external
import astroid

from .common import traverse, Token
# app
from .common import Token, traverse


def get_imports(body: list) -> Iterator[Token]:
Expand Down
5 changes: 4 additions & 1 deletion deal/linter/_extractors/prints.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# built-in
import ast
from typing import Iterator

# external
import astroid

from .common import Token, get_name, traverse, TOKENS
# app
from .common import TOKENS, Token, get_name, traverse


def get_prints(body: list) -> Iterator[Token]:
Expand Down