diff options
author | David Lord <davidism@gmail.com> | 2019-12-05 07:25:11 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-05 07:25:11 -0800 |
commit | 9176fbd8183ac491bfa9a69eb6d42b485c2749c6 (patch) | |
tree | ef4fdd259fadaefa7e599d73db5991fa485020dd | |
parent | 28f12c020ea9628f72e13a658a9a6846743fa9c8 (diff) | |
parent | d2e0e78afe7c6ae864a20c2cc29e80936cff47af (diff) | |
download | jinja2-9176fbd8183ac491bfa9a69eb6d42b485c2749c6.tar.gz |
Merge pull request #1113 from pallets/namespace-loader
PackageLoader understands namespace packages
-rw-r--r-- | CHANGES.rst | 6 | ||||
-rw-r--r-- | jinja2/loaders.py | 47 |
2 files changed, 43 insertions, 10 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 103dea5..945e57f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -55,8 +55,10 @@ Unreleased ``revindex`` work for async iterators. :pr:`1101` - In async environments, values from attribute/property access will be awaited if needed. :pr:`1101` -- ``PackageLoader`` doesn't depend on setuptools or pkg_resources. - :issue:`970` +- :class:`~loader.PackageLoader` doesn't depend on setuptools or + pkg_resources. :issue:`970` +- ``PackageLoader`` has limited support for :pep:`420` namespace + packages. :issue:`1097` - Support :class:`os.PathLike` objects in :class:`~loader.FileSystemLoader` and :class:`~loader.ModuleLoader`. :issue:`870` diff --git a/jinja2/loaders.py b/jinja2/loaders.py index 3101bc3..cc269be 100644 --- a/jinja2/loaders.py +++ b/jinja2/loaders.py @@ -12,6 +12,7 @@ import os import pkgutil import sys import weakref +from importlib import import_module from types import ModuleType from os import path from hashlib import sha1 @@ -231,8 +232,16 @@ class PackageLoader(BaseLoader): introspecting data in packages is too limited to support other installation methods the way this loader requires. + There is limited support for :pep:`420` namespace packages. The + template directory is assumed to only be in one namespace + contributor. Zip files contributing to a namespace are not + supported. + .. versionchanged:: 2.11.0 No longer uses ``setuptools`` as a dependency. + + .. versionchanged:: 2.11.0 + Limited PEP 420 namespace package support. """ def __init__(self, package_name, package_path="templates", encoding="utf-8"): @@ -241,18 +250,40 @@ class PackageLoader(BaseLoader): elif package_path[:2] == os.path.curdir + os.path.sep: package_path = package_path[2:] - package_path = os.path.normpath(package_path) - - self.package_name = package_name + package_path = os.path.normpath(package_path).rstrip(os.path.sep) self.package_path = package_path + self.package_name = package_name self.encoding = encoding - self._loader = pkgutil.get_loader(package_name) + # Make sure the package exists. This also makes namespace + # packages work, otherwise get_loader returns None. + import_module(package_name) + self._loader = loader = pkgutil.get_loader(package_name) + # Zip loader's archive attribute points at the zip. - self._archive = getattr(self._loader, "archive", None) - self._template_root = os.path.join( - os.path.dirname(self._loader.get_filename(package_name)), package_path - ).rstrip(os.path.sep) + self._archive = getattr(loader, "archive", None) + self._template_root = None + + if hasattr(loader, "get_filename"): + # A standard directory package, or a zip package. + self._template_root = os.path.join( + os.path.dirname(loader.get_filename(package_name)), package_path + ) + elif hasattr(loader, "_path"): + # A namespace package, limited support. Find the first + # contributor with the template directory. + for root in loader._path: + root = os.path.join(root, package_path) + + if os.path.isdir(root): + self._template_root = root + break + + if self._template_root is None: + raise ValueError( + "The %r package was not installed in a way that" + " PackageLoader understands." % package_name + ) def get_source(self, environment, template): p = os.path.join(self._template_root, *split_template_path(template)) |