summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lord <davidism@gmail.com>2023-01-19 18:22:50 -0800
committerGitHub <noreply@github.com>2023-01-19 18:22:50 -0800
commit05a5f8120a62f2e1da322131c632be2ba8d969fc (patch)
tree842087b49f88a8764206336c7a21fc55f7335af2
parent048a06869755877d173ec1ee4ea226139788d34c (diff)
parent8ed8e1d0eda956aa0cdc6e216270be3ebcfd4b4a (diff)
downloadjinja2-05a5f8120a62f2e1da322131c632be2ba8d969fc.tar.gz
switch to pyproject.toml (#1793)
-rw-r--r--.flake828
-rw-r--r--.github/workflows/tests.yaml2
-rw-r--r--CHANGES.rst3
-rw-r--r--pyproject.toml68
-rw-r--r--setup.cfg111
-rw-r--r--setup.py8
-rw-r--r--src/jinja2/async_utils.py2
-rw-r--r--src/jinja2/environment.py12
-rw-r--r--src/jinja2/filters.py42
-rw-r--r--src/jinja2/lexer.py2
-rw-r--r--src/jinja2/loaders.py9
-rw-r--r--src/jinja2/nodes.py2
-rw-r--r--src/jinja2/parser.py11
-rw-r--r--src/jinja2/runtime.py5
-rw-r--r--src/jinja2/sandbox.py6
-rw-r--r--src/jinja2/tests.py2
-rw-r--r--src/jinja2/utils.py6
17 files changed, 161 insertions, 158 deletions
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..705245b
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,28 @@
+[flake8]
+extend-select =
+ # bugbear
+ B
+ # bugbear opinions
+ B9
+ # implicit str concat
+ ISC
+extend-ignore =
+ # slice notation whitespace, invalid
+ E203
+ # line length, handled by bugbear B950
+ E501
+ # bare except, handled by bugbear B001
+ E722
+ # zip with strict=, requires python >= 3.10
+ B905
+ # string formatting opinion, B028 renamed to B907
+ B028
+ B907
+# up to 88 allowed by bugbear B950
+max-line-length = 80
+per-file-ignores =
+ # __init__ exports names
+ src/jinja2/__init__.py: F401
+ # not relevant to jinja's compiler
+ src/jinja2/compiler.py: B906
+ src/jinja2/idtracking.py: B906
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index b757fa0..85e748b 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -50,7 +50,7 @@ jobs:
uses: actions/cache@v3
with:
path: ./.mypy_cache
- key: mypy|${{ matrix.python }}|${{ hashFiles('setup.cfg') }}
+ key: mypy|${{ matrix.python }}|${{ hashFiles('pyproject.toml') }}
if: matrix.tox == 'typing'
- run: pip install tox
- run: tox run -e ${{ matrix.tox }}
diff --git a/CHANGES.rst b/CHANGES.rst
index 7ee75a6..d539a73 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,6 +5,9 @@ Version 3.2.0
Unreleased
+- Use modern packaging metadata with ``pyproject.toml`` instead of ``setup.cfg``.
+ :pr:`1793`
+
Version 3.1.2
-------------
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..f90576d
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,68 @@
+[project]
+name = "Jinja2"
+description = "A very fast and expressive template engine."
+readme = "README.rst"
+license = {text = "BSD-3-Clause"}
+maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
+authors = [{name = "Armin Ronacher", email = "armin.ronacher@active-4.com"}]
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: BSD License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
+ "Topic :: Text Processing :: Markup :: HTML",
+]
+requires-python = ">=3.7"
+dependencies = ["MarkupSafe>=2.0"]
+dynamic = ["version"]
+
+[project.urls]
+Donate = "https://palletsprojects.com/donate"
+Documentation = "https://jinja.palletsprojects.com/"
+Changes = "https://jinja.palletsprojects.com/changes/"
+"Source Code" = "https://github.com/pallets/jinja/"
+"Issue Tracker" = "https://github.com/pallets/jinja/issues/"
+Twitter = "https://twitter.com/PalletsTeam"
+Chat = "https://discord.gg/pallets"
+
+[project.optional-dependencies]
+i18n = ["Babel>=2.7"]
+
+[project.entry-points."babel.extractors"]
+
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools.dynamic]
+version = {attr = "jinja2.__version__"}
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+filterwarnings = ["error"]
+
+[tool.coverage.run]
+branch = true
+source = ["jinja2", "tests"]
+
+[tool.coverage.paths]
+source = ["src", "*/site-packages"]
+
+[tool.mypy]
+python_version = "3.7"
+files = ["src/jinja2"]
+show_error_codes = true
+pretty = true
+strict = true
+local_partial_types = true
+warn_unreachable = true
+
+[[tool.mypy.overrides]]
+module = [
+ "jinja2.defaults",
+ "markupsafe",
+]
+no_implicit_reexport = false
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index edfa309..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,111 +0,0 @@
-[metadata]
-name = Jinja2
-version = attr: jinja2.__version__
-url = https://palletsprojects.com/p/jinja/
-project_urls =
- Donate = https://palletsprojects.com/donate
- Documentation = https://jinja.palletsprojects.com/
- Changes = https://jinja.palletsprojects.com/changes/
- Source Code = https://github.com/pallets/jinja/
- Issue Tracker = https://github.com/pallets/jinja/issues/
- Twitter = https://twitter.com/PalletsTeam
- Chat = https://discord.gg/pallets
-license = BSD-3-Clause
-license_files = LICENSE.rst
-author = Armin Ronacher
-author_email = armin.ronacher@active-4.com
-maintainer = Pallets
-maintainer_email = contact@palletsprojects.com
-description = A very fast and expressive template engine.
-long_description = file: README.rst
-long_description_content_type = text/x-rst
-classifiers =
- Development Status :: 5 - Production/Stable
- Environment :: Web Environment
- Intended Audience :: Developers
- License :: OSI Approved :: BSD License
- Operating System :: OS Independent
- Programming Language :: Python
- Topic :: Internet :: WWW/HTTP :: Dynamic Content
- Topic :: Text Processing :: Markup :: HTML
-
-[options]
-packages = find:
-package_dir = = src
-include_package_data = True
-python_requires = >= 3.7
-# Dependencies are in setup.py for GitHub's dependency graph.
-
-[options.packages.find]
-where = src
-
-[options.entry_points]
-babel.extractors =
- jinja2 = jinja2.ext:babel_extract[i18n]
-
-[tool:pytest]
-testpaths = tests
-filterwarnings =
- error
- # Python 3.9 raises a deprecation from internal asyncio code.
- ignore:The loop argument:DeprecationWarning:asyncio[.]base_events:542
-
-[coverage:run]
-branch = True
-source =
- jinja2
- tests
-
-[coverage:paths]
-source =
- src
- */site-packages
-
-[flake8]
-# B = bugbear
-# E = pycodestyle errors
-# F = flake8 pyflakes
-# W = pycodestyle warnings
-# B9 = bugbear opinions
-# ISC = implicit str concat
-select = B, E, F, W, B9, ISC
-ignore =
- # slice notation whitespace, invalid
- E203
- # line length, handled by bugbear B950
- E501
- # bare except, handled by bugbear B001
- E722
- # bin op line break, invalid
- W503
- # zip with strict=, requires python >= 3.10
- B905
-# up to 88 allowed by bugbear B950
-max-line-length = 80
-per-file-ignores =
- # __init__ exports names
- src/jinja2/__init__.py: F401
-
-[mypy]
-files = src/jinja2
-python_version = 3.7
-show_error_codes = True
-disallow_subclassing_any = True
-disallow_untyped_calls = True
-disallow_untyped_defs = True
-disallow_incomplete_defs = True
-no_implicit_optional = True
-local_partial_types = True
-no_implicit_reexport = True
-strict_equality = True
-warn_redundant_casts = True
-warn_unused_configs = True
-warn_unused_ignores = True
-warn_return_any = True
-warn_unreachable = True
-
-[mypy-jinja2.defaults]
-no_implicit_reexport = False
-
-[mypy-markupsafe]
-no_implicit_reexport = False
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 79d0708..0000000
--- a/setup.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from setuptools import setup
-
-# Metadata goes in setup.cfg. These are here for GitHub's dependency graph.
-setup(
- name="Jinja2",
- install_requires=["MarkupSafe>=2.0"],
- extras_require={"i18n": ["Babel>=2.7"]},
-)
diff --git a/src/jinja2/async_utils.py b/src/jinja2/async_utils.py
index 715d701..e65219e 100644
--- a/src/jinja2/async_utils.py
+++ b/src/jinja2/async_utils.py
@@ -47,7 +47,7 @@ def async_variant(normal_func): # type: ignore
if need_eval_context:
wrapper = pass_eval_context(wrapper)
- wrapper.jinja_async_variant = True
+ wrapper.jinja_async_variant = True # type: ignore[attr-defined]
return wrapper
return decorator
diff --git a/src/jinja2/environment.py b/src/jinja2/environment.py
index 88b2666..29a1b4e 100644
--- a/src/jinja2/environment.py
+++ b/src/jinja2/environment.py
@@ -79,7 +79,7 @@ def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_b
def create_cache(
size: int,
-) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]:
+) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[BaseLoader]", str], "Template"]]:
"""Return the cache class for the given size."""
if size == 0:
return None
@@ -91,8 +91,10 @@ def create_cache(
def copy_cache(
- cache: t.Optional[t.MutableMapping],
-) -> t.Optional[t.MutableMapping[t.Tuple[weakref.ref, str], "Template"]]:
+ cache: t.Optional[
+ t.MutableMapping[t.Tuple["weakref.ref[BaseLoader]", str], "Template"]
+ ],
+) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[BaseLoader]", str], "Template"]]:
"""Create an empty copy of the given cache."""
if cache is None:
return None
@@ -814,7 +816,7 @@ class Environment:
def compile_templates(
self,
- target: t.Union[str, os.PathLike],
+ target: t.Union[str, "os.PathLike[str]"],
extensions: t.Optional[t.Collection[str]] = None,
filter_func: t.Optional[t.Callable[[str], bool]] = None,
zip: t.Optional[str] = "deflated",
@@ -1588,7 +1590,7 @@ class TemplateStream:
def dump(
self,
- fp: t.Union[str, t.IO],
+ fp: t.Union[str, t.IO[t.Any]],
encoding: t.Optional[str] = None,
errors: t.Optional[str] = "strict",
) -> None:
diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py
index ed07c4c..f4479ff 100644
--- a/src/jinja2/filters.py
+++ b/src/jinja2/filters.py
@@ -538,7 +538,7 @@ def do_default(
@pass_eval_context
def sync_do_join(
eval_ctx: "EvalContext",
- value: t.Iterable,
+ value: t.Iterable[t.Any],
d: str = "",
attribute: t.Optional[t.Union[str, int]] = None,
) -> str:
@@ -596,7 +596,7 @@ def sync_do_join(
@async_variant(sync_do_join) # type: ignore
async def do_join(
eval_ctx: "EvalContext",
- value: t.Union[t.AsyncIterable, t.Iterable],
+ value: t.Union[t.AsyncIterable[t.Any], t.Iterable[t.Any]],
d: str = "",
attribute: t.Optional[t.Union[str, int]] = None,
) -> str:
@@ -1146,7 +1146,7 @@ def do_round(
class _GroupTuple(t.NamedTuple):
grouper: t.Any
- list: t.List
+ list: t.List[t.Any]
# Use the regular tuple repr to hide this subclass if users print
# out the value during debugging.
@@ -1402,26 +1402,30 @@ def do_attr(
@typing.overload
def sync_do_map(
- context: "Context", value: t.Iterable, name: str, *args: t.Any, **kwargs: t.Any
-) -> t.Iterable:
+ context: "Context",
+ value: t.Iterable[t.Any],
+ name: str,
+ *args: t.Any,
+ **kwargs: t.Any,
+) -> t.Iterable[t.Any]:
...
@typing.overload
def sync_do_map(
context: "Context",
- value: t.Iterable,
+ value: t.Iterable[t.Any],
*,
attribute: str = ...,
default: t.Optional[t.Any] = None,
-) -> t.Iterable:
+) -> t.Iterable[t.Any]:
...
@pass_context
def sync_do_map(
- context: "Context", value: t.Iterable, *args: t.Any, **kwargs: t.Any
-) -> t.Iterable:
+ context: "Context", value: t.Iterable[t.Any], *args: t.Any, **kwargs: t.Any
+) -> t.Iterable[t.Any]:
"""Applies a filter on a sequence of objects or looks up an attribute.
This is useful when dealing with lists of objects but you are really
only interested in a certain value of it.
@@ -1471,32 +1475,32 @@ def sync_do_map(
@typing.overload
def do_map(
context: "Context",
- value: t.Union[t.AsyncIterable, t.Iterable],
+ value: t.Union[t.AsyncIterable[t.Any], t.Iterable[t.Any]],
name: str,
*args: t.Any,
**kwargs: t.Any,
-) -> t.Iterable:
+) -> t.Iterable[t.Any]:
...
@typing.overload
def do_map(
context: "Context",
- value: t.Union[t.AsyncIterable, t.Iterable],
+ value: t.Union[t.AsyncIterable[t.Any], t.Iterable[t.Any]],
*,
attribute: str = ...,
default: t.Optional[t.Any] = None,
-) -> t.Iterable:
+) -> t.Iterable[t.Any]:
...
@async_variant(sync_do_map) # type: ignore
async def do_map(
context: "Context",
- value: t.Union[t.AsyncIterable, t.Iterable],
+ value: t.Union[t.AsyncIterable[t.Any], t.Iterable[t.Any]],
*args: t.Any,
**kwargs: t.Any,
-) -> t.AsyncIterable:
+) -> t.AsyncIterable[t.Any]:
if value:
func = prepare_map(context, args, kwargs)
@@ -1689,7 +1693,7 @@ def do_tojson(
def prepare_map(
- context: "Context", args: t.Tuple, kwargs: t.Dict[str, t.Any]
+ context: "Context", args: t.Tuple[t.Any, ...], kwargs: t.Dict[str, t.Any]
) -> t.Callable[[t.Any], t.Any]:
if not args and "attribute" in kwargs:
attribute = kwargs.pop("attribute")
@@ -1718,7 +1722,7 @@ def prepare_map(
def prepare_select_or_reject(
context: "Context",
- args: t.Tuple,
+ args: t.Tuple[t.Any, ...],
kwargs: t.Dict[str, t.Any],
modfunc: t.Callable[[t.Any], t.Any],
lookup_attr: bool,
@@ -1753,7 +1757,7 @@ def prepare_select_or_reject(
def select_or_reject(
context: "Context",
value: "t.Iterable[V]",
- args: t.Tuple,
+ args: t.Tuple[t.Any, ...],
kwargs: t.Dict[str, t.Any],
modfunc: t.Callable[[t.Any], t.Any],
lookup_attr: bool,
@@ -1769,7 +1773,7 @@ def select_or_reject(
async def async_select_or_reject(
context: "Context",
value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]",
- args: t.Tuple,
+ args: t.Tuple[t.Any, ...],
kwargs: t.Dict[str, t.Any],
modfunc: t.Callable[[t.Any], t.Any],
lookup_attr: bool,
diff --git a/src/jinja2/lexer.py b/src/jinja2/lexer.py
index aff7e9f..16ca73e 100644
--- a/src/jinja2/lexer.py
+++ b/src/jinja2/lexer.py
@@ -447,7 +447,7 @@ def get_lexer(environment: "Environment") -> "Lexer":
return lexer
-class OptionalLStrip(tuple):
+class OptionalLStrip(tuple): # type: ignore[type-arg]
"""A special tuple for marking a point in the state that can have
lstrip applied.
"""
diff --git a/src/jinja2/loaders.py b/src/jinja2/loaders.py
index 57b5582..9b479be 100644
--- a/src/jinja2/loaders.py
+++ b/src/jinja2/loaders.py
@@ -177,7 +177,9 @@ class FileSystemLoader(BaseLoader):
def __init__(
self,
- searchpath: t.Union[str, os.PathLike, t.Sequence[t.Union[str, os.PathLike]]],
+ searchpath: t.Union[
+ str, "os.PathLike[str]", t.Sequence[t.Union[str, "os.PathLike[str]"]]
+ ],
encoding: str = "utf-8",
followlinks: bool = False,
) -> None:
@@ -598,7 +600,10 @@ class ModuleLoader(BaseLoader):
has_source_access = False
def __init__(
- self, path: t.Union[str, os.PathLike, t.Sequence[t.Union[str, os.PathLike]]]
+ self,
+ path: t.Union[
+ str, "os.PathLike[str]", t.Sequence[t.Union[str, "os.PathLike[str]"]]
+ ],
) -> None:
package_name = f"_jinja2_module_templates_{id(self):x}"
diff --git a/src/jinja2/nodes.py b/src/jinja2/nodes.py
index b2f88d9..00365ed 100644
--- a/src/jinja2/nodes.py
+++ b/src/jinja2/nodes.py
@@ -56,7 +56,7 @@ class NodeType(type):
def __new__(mcs, name, bases, d): # type: ignore
for attr in "fields", "attributes":
- storage = []
+ storage: t.List[t.Any] = []
storage.extend(getattr(bases[0] if bases else object, attr, ()))
storage.extend(d.get(attr, ()))
assert len(bases) <= 1, "multiple inheritance not allowed"
diff --git a/src/jinja2/parser.py b/src/jinja2/parser.py
index cefce2d..fb4fd0d 100644
--- a/src/jinja2/parser.py
+++ b/src/jinja2/parser.py
@@ -859,7 +859,14 @@ class Parser:
return nodes.Slice(lineno=lineno, *args)
- def parse_call_args(self) -> t.Tuple:
+ def parse_call_args(
+ self,
+ ) -> t.Tuple[
+ t.List[nodes.Expr],
+ t.List[nodes.Keyword],
+ t.Union[nodes.Expr, None],
+ t.Union[nodes.Expr, None],
+ ]:
token = self.stream.expect("lparen")
args = []
kwargs = []
@@ -950,7 +957,7 @@ class Parser:
next(self.stream)
name += "." + self.stream.expect("name").value
dyn_args = dyn_kwargs = None
- kwargs = []
+ kwargs: t.List[nodes.Keyword] = []
if self.stream.current.type == "lparen":
args, kwargs, dyn_args, dyn_kwargs = self.parse_call_args()
elif self.stream.current.type in {
diff --git a/src/jinja2/runtime.py b/src/jinja2/runtime.py
index 93e21b4..c64999d 100644
--- a/src/jinja2/runtime.py
+++ b/src/jinja2/runtime.py
@@ -259,7 +259,10 @@ class Context:
@internalcode
def call(
- __self, __obj: t.Callable, *args: t.Any, **kwargs: t.Any # noqa: B902
+ __self, # noqa: B902
+ __obj: t.Callable[..., t.Any],
+ *args: t.Any,
+ **kwargs: t.Any,
) -> t.Union[t.Any, "Undefined"]:
"""Call the callable with the arguments and keyword arguments
provided but inject the active context or environment as first
diff --git a/src/jinja2/sandbox.py b/src/jinja2/sandbox.py
index 06d7414..153f42e 100644
--- a/src/jinja2/sandbox.py
+++ b/src/jinja2/sandbox.py
@@ -37,7 +37,7 @@ UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
#: unsafe attributes on async generators
UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
-_mutable_spec: t.Tuple[t.Tuple[t.Type, t.FrozenSet[str]], ...] = (
+_mutable_spec: t.Tuple[t.Tuple[t.Type[t.Any], t.FrozenSet[str]], ...] = (
(
abc.MutableSet,
frozenset(
@@ -80,7 +80,7 @@ _mutable_spec: t.Tuple[t.Tuple[t.Type, t.FrozenSet[str]], ...] = (
)
-def inspect_format_method(callable: t.Callable) -> t.Optional[str]:
+def inspect_format_method(callable: t.Callable[..., t.Any]) -> t.Optional[str]:
if not isinstance(
callable, (types.MethodType, types.BuiltinMethodType)
) or callable.__name__ not in ("format", "format_map"):
@@ -350,7 +350,7 @@ class SandboxedEnvironment(Environment):
s: str,
args: t.Tuple[t.Any, ...],
kwargs: t.Dict[str, t.Any],
- format_func: t.Optional[t.Callable] = None,
+ format_func: t.Optional[t.Callable[..., t.Any]] = None,
) -> str:
"""If a format call is detected, then this is routed through this
method so that our safety sandbox can be used for it.
diff --git a/src/jinja2/tests.py b/src/jinja2/tests.py
index a467cf0..0d29f94 100644
--- a/src/jinja2/tests.py
+++ b/src/jinja2/tests.py
@@ -204,7 +204,7 @@ def test_escaped(value: t.Any) -> bool:
return hasattr(value, "__html__")
-def test_in(value: t.Any, seq: t.Container) -> bool:
+def test_in(value: t.Any, seq: t.Container[t.Any]) -> bool:
"""Check if value is in seq.
.. versionadded:: 2.10
diff --git a/src/jinja2/utils.py b/src/jinja2/utils.py
index 9b5f5a5..852d082 100644
--- a/src/jinja2/utils.py
+++ b/src/jinja2/utils.py
@@ -152,7 +152,7 @@ def import_string(import_name: str, silent: bool = False) -> t.Any:
raise
-def open_if_exists(filename: str, mode: str = "rb") -> t.Optional[t.IO]:
+def open_if_exists(filename: str, mode: str = "rb") -> t.Optional[t.IO[t.Any]]:
"""Returns a file descriptor for the filename if that file exists,
otherwise ``None``.
"""
@@ -450,7 +450,9 @@ class LRUCache:
self.__dict__.update(d)
self._postinit()
- def __getnewargs__(self) -> t.Tuple:
+ def __getnewargs__(
+ self,
+ ) -> t.Tuple[int,]:
return (self.capacity,)
def copy(self) -> "LRUCache":