class_docstr: Load templates from a directory in a Python 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.
  
  The following example looks up templates in the ``pages`` directory
  within the ``project.ui`` package.
  
  .. 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.
  
  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:: 3.0
      No longer uses ``setuptools`` as a dependency.
  
  .. versionchanged:: 3.0
      Limited PEP 420 namespace package support.
class_docstr_tok: ["Load","templates","from","a","directory","in","a","Python","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",".","The","following","example","looks","up","templates","in","the","``","pages","``","directory","within","the","``","project.ui","``","package",".","..","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",".","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",":",":","3.0","No","longer","uses","``","setuptools","``","as","a","dependency",".","..","versionchanged",":",":","3.0","Limited","PEP","420","namespace","package","support","."]
code: def get_source(
      self, environment: "Environment", template: str
  ) -> t.Tuple[str, str, t.Optional[t.Callable[[], bool]]]:
      # Use posixpath even on Windows to avoid "drive:" or UNC
      # segments breaking out of the search directory. Use normpath to
      # convert Windows altsep to sep.
      p = os.path.normpath(
          posixpath.join(
              self._template_root, *split_template_path(template)
          )
      )
      up_to_date: t.Optional[t.Callable[[], bool]]
  
      if self._archive is None and hasattr(self, "_package"):
          try:
              source = pkgutil.get_data(
                  self.package_name,
                  os.path.join(
                      self.package_path, *split_template_path(template)
                  ),
              )
          except OSError:
              raise TemplateNotFound(template)
  
          def up_to_date() -> bool:
              return True
  
      elif self._archive is None:
          # Package is a directory.
          if not os.path.isfile(p):
              raise TemplateNotFound(template)
  
          with open(p, "rb") as f:
              source = f.read()
  
          mtime = os.path.getmtime(p)
  
          def up_to_date() -> bool:
              return os.path.isfile(p) and os.path.getmtime(p) == mtime
  
      else:
          # Package is a zip file.
          try:
              source = self._loader.get_data(p)  # type: ignore
          except OSError as e:
              raise TemplateNotFound(template) from e
  
          # 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
  
      return source.decode(self.encoding), p, up_to_date
  
imports: import importlib.util
  import os
  import posixpath
  import sys
  import pkgutil
  import typing
  import weakref
  import zipimport
  from collections import abc
  from hashlib import sha1
  from importlib import import_module
  from types import ModuleType
  from .exceptions import TemplateNotFound
  from .utils import internalcode
  from .utils import open_if_exists
  import __res
label_desc: Use image node_id 2 for calling the PackageLoader obj's underlying member method code with example usage: obj.get_source(environment, template) and returns: source,  p,  up_to_date, True, unknown