aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Lord <davidism@gmail.com>2020-03-30 10:11:22 -0700
committerDavid Lord <davidism@gmail.com>2020-03-30 10:11:22 -0700
commite2357fad71cee5ae93cb66ca27deb9dd67ccefa5 (patch)
tree1e6bd2b2dc121117340c8056645e4d619985607b /src
parentcf53e2254dd316327a4d54f6f11801714c5af3a7 (diff)
downloadjinja-e2357fad71cee5ae93cb66ca27deb9dd67ccefa5.tar.gz
Revert "PackageLoader doesn't depend on setuptools"
This reverts commit 4b6077a8c0a0f56bb8dbac37f8f9027130b4091c.
Diffstat (limited to 'src')
-rw-r--r--src/jinja2/loaders.py131
1 files changed, 47 insertions, 84 deletions
diff --git a/src/jinja2/loaders.py b/src/jinja2/loaders.py
index d057ad5f..457c4b59 100644
--- a/src/jinja2/loaders.py
+++ b/src/jinja2/loaders.py
@@ -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