* 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
- {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:
htmlcov
.pytest_cache/
/.vscode/
+.mypy_cache
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
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
import weakref
from functools import partial
from functools import reduce
+from typing import Any
from markupsafe import Markup
#: :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,
import pprint
import re
from sys import version_info
+from typing import Set
from markupsafe import Markup
"""
#: 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
# 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
from ast import literal_eval
from itertools import chain
from itertools import islice
+from typing import Any
from . import nodes
from .compiler import CodeGenerator
"""An environment that renders templates to native Python types."""
code_generator_class = NativeCodeGenerator
+ template_class: Any
class NativeTemplate(Template):
"""
import operator
from collections import deque
+from typing import Any
+from typing import Tuple as TupleType
from markupsafe import Markup
all nodes automatically.
"""
- fields = ()
+ fields: TupleType = ()
attributes = ("lineno", "environment")
abstract = True
"""Baseclass for all binary expressions."""
fields = ("left", "right")
- operator = None
+ operator: Any = None
abstract = True
def as_const(self, eval_ctx=None):
"""Baseclass for all unary expressions."""
fields = ("node",)
- operator = None
+ operator: Any = None
abstract = True
def as_const(self, eval_ctx=None):
raise TypeError("can't create custom node types")
-NodeType.__new__ = staticmethod(_failing_new)
+NodeType.__new__ = staticmethod(_failing_new) # type: ignore
del _failing_new
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)()
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):
"""
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
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"}
#: 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
#: 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
# 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()
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),
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):
class DerivedExampleExtension(ExampleExtension):
- context_reference_node_cls = nodes.DerivedContextReference
+ context_reference_node_cls = nodes.DerivedContextReference # type: ignore
class PreprocessorExtension(Extension):
py{38,37,36,py3}
style
docs
+ mypy
skip_missing_interpreters = true
[testenv]
[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