]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
Revert "PackageLoader doesn't depend on setuptools"
authorDavid Lord <davidism@gmail.com>
Mon, 30 Mar 2020 17:11:22 +0000 (10:11 -0700)
committerDavid Lord <davidism@gmail.com>
Mon, 30 Mar 2020 17:11:22 +0000 (10:11 -0700)
This reverts commit 4b6077a8c0a0f56bb8dbac37f8f9027130b4091c.

src/jinja2/loaders.py
tests/res/package.zip [deleted file]
tests/test_loader.py

index d057ad5fb4bbbd2a3c5231e517a5b859df9fdd0f..457c4b59a72173f726a6aa9e980d2d321ce1e183 100644 (file)
@@ -3,7 +3,6 @@
 sources.
 """
 import os
-import pkgutil
 import sys
 import weakref
 from hashlib import sha1
@@ -216,111 +215,75 @@ class FileSystemLoader(BaseLoader):
 
 
 class PackageLoader(BaseLoader):
-    """Load templates from a directory in a Python package.
+    """Load templates from python eggs or packages.  It is constructed with
+    the name of the python package and the path to the templates in that
+    package::
 
-    :param package_name: Import name of the package that contains the
-        template directory.
-    :param package_path: Directory within the imported package that
-        contains the templates.
-    :param encoding: Encoding of template files.
+        loader = PackageLoader('mypackage', 'views')
 
-    The following example looks up templates in the ``pages`` directory
-    within the ``project.ui`` package.
+    If the package path is not given, ``'templates'`` is assumed.
 
-    .. code-block:: python
-
-        loader = PackageLoader("project.ui", "pages")
-
-    Only packages installed as directories (standard pip behavior) or
-    zip/egg files (less common) are supported. The Python API for
-    introspecting data in packages is too limited to support other
-    installation methods the way this loader requires.
-
-    .. versionchanged:: 2.11.0
-        No longer uses ``setuptools`` as a dependency.
+    Per default the template encoding is ``'utf-8'`` which can be changed
+    by setting the `encoding` parameter to something else.  Due to the nature
+    of eggs it's only possible to reload templates if the package was loaded
+    from the file system and not a zip file.
     """
 
     def __init__(self, package_name, package_path="templates", encoding="utf-8"):
-        if package_path == os.path.curdir:
-            package_path = ""
-        elif package_path[:2] == os.path.curdir + os.path.sep:
-            package_path = package_path[2:]
-
-        package_path = os.path.normpath(package_path)
+        from pkg_resources import DefaultProvider
+        from pkg_resources import get_provider
+        from pkg_resources import ResourceManager
 
-        self.package_name = package_name
-        self.package_path = package_path
+        provider = get_provider(package_name)
         self.encoding = encoding
-
-        self._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.manager = ResourceManager()
+        self.filesystem_bound = isinstance(provider, DefaultProvider)
+        self.provider = provider
+        self.package_path = package_path
 
     def get_source(self, environment, template):
-        p = os.path.join(self._template_root, *split_template_path(template))
+        pieces = split_template_path(template)
+        p = "/".join((self.package_path,) + tuple(pieces))
 
-        if self._archive is None:
-            # Package is a directory.
-            if not os.path.isfile(p):
-                raise TemplateNotFound(template)
+        if not self.provider.has_resource(p):
+            raise TemplateNotFound(template)
 
-            with open(p, "rb") as f:
-                source = f.read()
+        filename = uptodate = None
 
-            mtime = os.path.getmtime(p)
+        if self.filesystem_bound:
+            filename = self.provider.get_resource_filename(self.manager, p)
+            mtime = path.getmtime(filename)
 
-            def up_to_date():
-                return os.path.isfile(p) and os.path.getmtime(p) == mtime
+            def uptodate():
+                try:
+                    return path.getmtime(filename) == mtime
+                except OSError:
+                    return False
 
-        else:
-            # Package is a zip file.
-            try:
-                source = self._loader.get_data(p)
-            except OSError:
-                raise TemplateNotFound(template)
+        source = self.provider.get_resource_string(self.manager, p)
+        return source.decode(self.encoding), filename, uptodate
 
-            # Could use the zip's mtime for all template mtimes, but
-            # would need to safely reload the module if it's out of
-            # date, so just report it as always current.
-            up_to_date = None
+    def list_templates(self):
+        path = self.package_path
 
-        return source.decode(self.encoding), p, up_to_date
+        if path[:2] == "./":
+            path = path[2:]
+        elif path == ".":
+            path = ""
 
-    def list_templates(self):
+        offset = len(path)
         results = []
 
-        if self._archive is None:
-            # Package is a directory.
-            offset = len(self._template_root)
-
-            for dirpath, _, filenames in os.walk(self._template_root):
-                dirpath = dirpath[offset:].lstrip(os.path.sep)
-                results.extend(
-                    os.path.join(dirpath, name).replace(os.path.sep, "/")
-                    for name in filenames
-                )
-        else:
-            if not hasattr(self._loader, "_files"):
-                raise TypeError(
-                    "This zip import does not have the required"
-                    " metadata to list templates."
-                )
-
-            # Package is a zip file.
-            prefix = (
-                self._template_root[len(self._archive) :].lstrip(os.path.sep)
-                + os.path.sep
-            )
-            offset = len(prefix)
+        def _walk(path):
+            for filename in self.provider.resource_listdir(path):
+                fullname = path + "/" + filename
 
-            for name in self._loader._files.keys():
-                # Find names under the templates directory that aren't directories.
-                if name.startswith(prefix) and name[-1] != os.path.sep:
-                    results.append(name[offset:].replace(os.path.sep, "/"))
+                if self.provider.resource_isdir(fullname):
+                    _walk(fullname)
+                else:
+                    results.append(fullname[offset:].lstrip("/"))
 
+        _walk(path)
         results.sort()
         return results
 
diff --git a/tests/res/package.zip b/tests/res/package.zip
deleted file mode 100644 (file)
index d4c9ce9..0000000
Binary files a/tests/res/package.zip and /dev/null differ
index 71e00b253f041496dbb3c9338fd794ef18758319..f10f7563078b41833c2113369985dbc2eb4e865d 100644 (file)
@@ -10,7 +10,6 @@ import pytest
 
 from jinja2 import Environment
 from jinja2 import loaders
-from jinja2 import PackageLoader
 from jinja2._compat import PY2
 from jinja2._compat import PYPY
 from jinja2.exceptions import TemplateNotFound
@@ -321,52 +320,3 @@ class TestModuleLoader(object):
         self.mod_env = Environment(loader=mod_loader)
 
         self._test_common()
-
-
-@pytest.fixture()
-def package_dir_loader(monkeypatch):
-    monkeypatch.syspath_prepend(os.path.dirname(__file__))
-    return PackageLoader("res")
-
-
-@pytest.mark.parametrize(
-    ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")]
-)
-def test_package_dir_source(package_dir_loader, template, expect):
-    source, name, up_to_date = package_dir_loader.get_source(None, template)
-    assert source.rstrip() == expect
-    assert name.endswith(os.path.join(*split_template_path(template)))
-    assert up_to_date()
-
-
-def test_package_dir_list(package_dir_loader):
-    templates = package_dir_loader.list_templates()
-    assert "foo/test.html" in templates
-    assert "test.html" in templates
-
-
-@pytest.fixture()
-def package_zip_loader(monkeypatch):
-    monkeypatch.syspath_prepend(
-        os.path.join(os.path.dirname(__file__), "res", "package.zip")
-    )
-    return PackageLoader("t_pack")
-
-
-@pytest.mark.parametrize(
-    ("template", "expect"), [("foo/test.html", "FOO"), ("test.html", "BAR")]
-)
-def test_package_zip_source(package_zip_loader, template, expect):
-    source, name, up_to_date = package_zip_loader.get_source(None, template)
-    assert source.rstrip() == expect
-    assert name.endswith(os.path.join(*split_template_path(template)))
-    assert up_to_date is None
-
-
-@pytest.mark.xfail(
-    PYPY,
-    reason="PyPy's zipimporter doesn't have a _files attribute.",
-    raises=TypeError,
-)
-def test_package_zip_list(package_zip_loader):
-    assert package_zip_loader.list_templates() == ["foo/test.html", "test.html"]