From: Teymour Aldridge Date: Fri, 26 Jun 2020 14:22:21 +0000 (+0100) Subject: Setup mypy X-Git-Tag: 3.0.0rc1~67^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=50ae5b6ac7e5962d525deac5bb3ae6fd9c2c2ae0;p=thirdparty%2Fjinja.git Setup mypy * Add missing type hints (these are intended as an initial set of type hints, to be added upon and improved later) * Setup MyPy to run as a Github Action --- diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index a826fda6..6fbd2966 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -22,6 +22,7 @@ jobs: - {name: 'PyPy', python: pypy3, os: ubuntu-latest, tox: pypy3} - {name: Style, python: '3.8', os: ubuntu-latest, tox: style} - {name: Docs, python: '3.8', os: ubuntu-latest, tox: docs} + - {name: Typing, python: '3.8', os: ubuntu-latest, tox: mypy} - {name: Windows, python: '3.8', os: windows-latest, tox: py38} - {name: Mac, python: '3.8', os: macos-latest, tox: py38} steps: diff --git a/.gitignore b/.gitignore index 81752e0e..86b119b6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ venv-*/ htmlcov .pytest_cache/ /.vscode/ +.mypy_cache diff --git a/setup.cfg b/setup.cfg index 5d3d02ee..27f24beb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,3 +40,22 @@ max-line-length = 80 per-file-ignores = # __init__ module exports names src/jinja2/__init__.py: F401 + +[mypy] +allow_redefinition = True +disallow_subclassing_any = True +# disallow_untyped_defs = True +strict_equality = True +strict_optional = False +warn_redundant_casts = True +warn_unused_configs = True +warn_unused_ignores = True + +[mypy-_pytest.*] +ignore_missing_imports = True + +[mypy-pytest.*] +ignore_missing_imports = True + +[mypy-requests_unixsocket.*] +ignore_missing_imports = True diff --git a/src/jinja2/compiler.py b/src/jinja2/compiler.py index abdbe6da..251aec6e 100644 --- a/src/jinja2/compiler.py +++ b/src/jinja2/compiler.py @@ -1512,18 +1512,18 @@ class CodeGenerator(NodeVisitor): return visitor - visit_Add = binop("+") - visit_Sub = binop("-") - visit_Mul = binop("*") - visit_Div = binop("/") - visit_FloorDiv = binop("//") - visit_Pow = binop("**") - visit_Mod = binop("%") - visit_And = binop("and", interceptable=False) - visit_Or = binop("or", interceptable=False) - visit_Pos = uaop("+") - visit_Neg = uaop("-") - visit_Not = uaop("not ", interceptable=False) + visit_Add = binop("+") # type:ignore + visit_Sub = binop("-") # type:ignore + visit_Mul = binop("*") # type:ignore + visit_Div = binop("/") # type:ignore + visit_FloorDiv = binop("//") # type:ignore + visit_Pow = binop("**") # type:ignore + visit_Mod = binop("%") # type:ignore + visit_And = binop("and", interceptable=False) # type:ignore + visit_Or = binop("or", interceptable=False) # type:ignore + visit_Pos = uaop("+") # type:ignore + visit_Neg = uaop("-") # type:ignore + visit_Not = uaop("not ", interceptable=False) # type:ignore del binop, uaop @optimizeconst diff --git a/src/jinja2/environment.py b/src/jinja2/environment.py index 556f7255..5201bbc1 100644 --- a/src/jinja2/environment.py +++ b/src/jinja2/environment.py @@ -6,6 +6,7 @@ import sys import weakref from functools import partial from functools import reduce +from typing import Any from markupsafe import Markup @@ -271,10 +272,12 @@ class Environment: #: :class:`~jinja2.compiler.CodeGenerator` for more information. code_generator_class = CodeGenerator - #: the context class thatis used for templates. See + #: the context class that is used for templates. See #: :class:`~jinja2.runtime.Context` for more information. context_class = Context + template_class = Any + def __init__( self, block_start_string=BLOCK_START_STRING, diff --git a/src/jinja2/ext.py b/src/jinja2/ext.py index 533ff179..4b89cf40 100644 --- a/src/jinja2/ext.py +++ b/src/jinja2/ext.py @@ -2,6 +2,7 @@ import pprint import re from sys import version_info +from typing import Set from markupsafe import Markup @@ -61,7 +62,7 @@ class Extension(metaclass=ExtensionRegistry): """ #: if this extension parses this is the list of tags it's listening to. - tags = set() + tags: Set[str] = set() #: the priority of that extension. This is especially useful for #: extensions that preprocess values. A lower value means higher diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py index c257d4c5..7a554a0e 100644 --- a/src/jinja2/filters.py +++ b/src/jinja2/filters.py @@ -922,8 +922,8 @@ def do_round(value, precision=0, method="common"): # people start to print this out in comments or something similar for # debugging. _GroupTuple = namedtuple("_GroupTuple", ["grouper", "list"]) -_GroupTuple.__repr__ = tuple.__repr__ -_GroupTuple.__str__ = tuple.__str__ +_GroupTuple.__repr__ = tuple.__repr__ # type: ignore +_GroupTuple.__str__ = tuple.__str__ # type: ignore @environmentfilter diff --git a/src/jinja2/nativetypes.py b/src/jinja2/nativetypes.py index 5ecf72b5..c71e0336 100644 --- a/src/jinja2/nativetypes.py +++ b/src/jinja2/nativetypes.py @@ -1,6 +1,7 @@ from ast import literal_eval from itertools import chain from itertools import islice +from typing import Any from . import nodes from .compiler import CodeGenerator @@ -70,6 +71,7 @@ class NativeEnvironment(Environment): """An environment that renders templates to native Python types.""" code_generator_class = NativeCodeGenerator + template_class: Any class NativeTemplate(Template): diff --git a/src/jinja2/nodes.py b/src/jinja2/nodes.py index d5133f75..95e7b730 100644 --- a/src/jinja2/nodes.py +++ b/src/jinja2/nodes.py @@ -4,6 +4,8 @@ to normalize nodes. """ import operator from collections import deque +from typing import Any +from typing import Tuple as TupleType from markupsafe import Markup @@ -105,7 +107,7 @@ class Node(metaclass=NodeType): all nodes automatically. """ - fields = () + fields: TupleType = () attributes = ("lineno", "environment") abstract = True @@ -414,7 +416,7 @@ class BinExpr(Expr): """Baseclass for all binary expressions.""" fields = ("left", "right") - operator = None + operator: Any = None abstract = True def as_const(self, eval_ctx=None): @@ -436,7 +438,7 @@ class UnaryExpr(Expr): """Baseclass for all unary expressions.""" fields = ("node",) - operator = None + operator: Any = None abstract = True def as_const(self, eval_ctx=None): @@ -1048,5 +1050,5 @@ def _failing_new(*args, **kwargs): raise TypeError("can't create custom node types") -NodeType.__new__ = staticmethod(_failing_new) +NodeType.__new__ = staticmethod(_failing_new) # type: ignore del _failing_new diff --git a/src/jinja2/runtime.py b/src/jinja2/runtime.py index 7b5925b1..950f620d 100644 --- a/src/jinja2/runtime.py +++ b/src/jinja2/runtime.py @@ -308,6 +308,7 @@ class Context(metaclass=ContextMeta): context.blocks.update((k, list(v)) for k, v in self.blocks.items()) return context + # ignore: true def _all(meth): # noqa: B902 def proxy(self): return getattr(self.get_all(), meth)() @@ -316,9 +317,9 @@ class Context(metaclass=ContextMeta): proxy.__name__ = meth return proxy - keys = _all("keys") - values = _all("values") - items = _all("items") + keys = _all("keys") # type:ignore + values = _all("values") # type:ignore + items = _all("items") # type:ignore del _all def __contains__(self, name): diff --git a/src/jinja2/sandbox.py b/src/jinja2/sandbox.py index 5c6d0946..6311a5dd 100644 --- a/src/jinja2/sandbox.py +++ b/src/jinja2/sandbox.py @@ -3,10 +3,12 @@ Useful when the template itself comes from an untrusted source. """ import operator import types -from _string import formatter_field_name_split +from _string import formatter_field_name_split # type: ignore from collections import abc from collections import deque from string import Formatter +from typing import FrozenSet +from typing import Set from markupsafe import EscapeFormatter from markupsafe import Markup @@ -18,10 +20,10 @@ from .exceptions import SecurityError MAX_RANGE = 100000 #: Unsafe function attributes. -UNSAFE_FUNCTION_ATTRIBUTES = set() +UNSAFE_FUNCTION_ATTRIBUTES: Set = set() #: Unsafe method attributes. Function attributes are unsafe for methods too. -UNSAFE_METHOD_ATTRIBUTES = set() +UNSAFE_METHOD_ATTRIBUTES: Set = set() #: unsafe generator attributes. UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"} @@ -220,7 +222,7 @@ class SandboxedEnvironment(Environment): #: interested in. #: #: .. versionadded:: 2.6 - intercepted_binops = frozenset() + intercepted_binops: FrozenSet = frozenset() #: a set of unary operators that should be intercepted. Each operator #: that is added to this set (empty by default) is delegated to the @@ -235,7 +237,7 @@ class SandboxedEnvironment(Environment): #: interested in. #: #: .. versionadded:: 2.6 - intercepted_unops = frozenset() + intercepted_unops: FrozenSet = frozenset() def intercept_unop(self, operator): """Called during template compilation with the name of a unary diff --git a/tests/test_api.py b/tests/test_api.py index 2679e8fb..4b6e0aed 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -273,7 +273,7 @@ class TestUndefined: # function raises an AttributeError, printing the repr of the # object in the undefined message would cause a RecursionError. class Error: - @property + @property # type: ignore def __class__(self): raise AttributeError() diff --git a/tests/test_asyncfilters.py b/tests/test_asyncfilters.py index 7c737c83..524a6d9d 100644 --- a/tests/test_asyncfilters.py +++ b/tests/test_asyncfilters.py @@ -142,7 +142,7 @@ def test_bool_select(env_async, items): assert tmpl.render(items=items) == "1|2|3|4|5" -def make_users(): +def make_users(): # type: ignore User = namedtuple("User", "name,is_active") return [ User("john", True), diff --git a/tests/test_ext.py b/tests/test_ext.py index 94d20bee..61abdc5b 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -90,7 +90,9 @@ i18n_env_trimmed.globals.update( newstyle_i18n_env = Environment( loader=DictLoader(newstyle_i18n_templates), extensions=["jinja2.ext.i18n"] ) -newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True) +newstyle_i18n_env.install_gettext_callables( # type: ignore + gettext, ngettext, newstyle=True +) class ExampleExtension(Extension): @@ -121,7 +123,7 @@ class ExampleExtension(Extension): class DerivedExampleExtension(ExampleExtension): - context_reference_node_cls = nodes.DerivedContextReference + context_reference_node_cls = nodes.DerivedContextReference # type: ignore class PreprocessorExtension(Extension): diff --git a/tox.ini b/tox.ini index 9b6d4713..ba9cb4d0 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,7 @@ envlist = py{38,37,36,py3} style docs + mypy skip_missing_interpreters = true [testenv] @@ -17,3 +18,8 @@ commands = pre-commit run --all-files --show-diff-on-failure [testenv:docs] deps = -r requirements/docs.txt commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html + +[testenv:mypy] +deps = mypy +commands = + mypy src/jinja2 tests