Format: text/x-python
⬇ Download File (4.4 KB)
   1  from __future__ import annotations
   2  
   3  import os
   4  import typing as t
   5  from datetime import timedelta
   6  
   7  from .cli import AppGroup
   8  from .globals import current_app
   9  from .helpers import send_from_directory
  10  from .sansio.blueprints import Blueprint as SansioBlueprint
  11  from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState  # noqa
  12  from .sansio.scaffold import _sentinel
  13  
  14  if t.TYPE_CHECKING:  # pragma: no cover
  15      from .wrappers import Response
  16  
  17  
  18  class Blueprint(SansioBlueprint):
  19      def __init__(
  20          self,
  21          name: str,
  22          import_name: str,
  23          static_folder: str | os.PathLike[str] | None = None,
  24          static_url_path: str | None = None,
  25          template_folder: str | os.PathLike[str] | None = None,
  26          url_prefix: str | None = None,
  27          subdomain: str | None = None,
  28          url_defaults: dict[str, t.Any] | None = None,
  29          root_path: str | None = None,
  30          cli_group: str | None = _sentinel,  # type: ignore
  31      ) -> None:
  32          super().__init__(
  33              name,
  34              import_name,
  35              static_folder,
  36              static_url_path,
  37              template_folder,
  38              url_prefix,
  39              subdomain,
  40              url_defaults,
  41              root_path,
  42              cli_group,
  43          )
  44  
  45          #: The Click command group for registering CLI commands for this
  46          #: object. The commands are available from the ``flask`` command
  47          #: once the application has been discovered and blueprints have
  48          #: been registered.
  49          self.cli = AppGroup()
  50  
  51          # Set the name of the Click group in case someone wants to add
  52          # the app's commands to another CLI tool.
  53          self.cli.name = self.name
  54  
  55      def get_send_file_max_age(self, filename: str | None) -> int | None:
  56          """Used by :func:`send_file` to determine the ``max_age`` cache
  57          value for a given file path if it wasn't passed.
  58  
  59          By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from
  60          the configuration of :data:`~flask.current_app`. This defaults
  61          to ``None``, which tells the browser to use conditional requests
  62          instead of a timed cache, which is usually preferable.
  63  
  64          Note this is a duplicate of the same method in the Flask
  65          class.
  66  
  67          .. versionchanged:: 2.0
  68              The default configuration is ``None`` instead of 12 hours.
  69  
  70          .. versionadded:: 0.9
  71          """
  72          value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"]
  73  
  74          if value is None:
  75              return None
  76  
  77          if isinstance(value, timedelta):
  78              return int(value.total_seconds())
  79  
  80          return value  # type: ignore[no-any-return]
  81  
  82      def send_static_file(self, filename: str) -> Response:
  83          """The view function used to serve files from
  84          :attr:`static_folder`. A route is automatically registered for
  85          this view at :attr:`static_url_path` if :attr:`static_folder` is
  86          set.
  87  
  88          Note this is a duplicate of the same method in the Flask
  89          class.
  90  
  91          .. versionadded:: 0.5
  92  
  93          """
  94          if not self.has_static_folder:
  95              raise RuntimeError("'static_folder' must be set to serve static_files.")
  96  
  97          # send_file only knows to call get_send_file_max_age on the app,
  98          # call it here so it works for blueprints too.
  99          max_age = self.get_send_file_max_age(filename)
 100          return send_from_directory(
 101              t.cast(str, self.static_folder), filename, max_age=max_age
 102          )
 103  
 104      def open_resource(
 105          self, resource: str, mode: str = "rb", encoding: str | None = "utf-8"
 106      ) -> t.IO[t.AnyStr]:
 107          """Open a resource file relative to :attr:`root_path` for reading. The
 108          blueprint-relative equivalent of the app's :meth:`~.Flask.open_resource`
 109          method.
 110  
 111          :param resource: Path to the resource relative to :attr:`root_path`.
 112          :param mode: Open the file in this mode. Only reading is supported,
 113              valid values are ``"r"`` (or ``"rt"``) and ``"rb"``.
 114          :param encoding: Open the file with this encoding when opening in text
 115              mode. This is ignored when opening in binary mode.
 116  
 117          .. versionchanged:: 3.1
 118              Added the ``encoding`` parameter.
 119          """
 120          if mode not in {"r", "rt", "rb"}:
 121              raise ValueError("Resources can only be opened for reading.")
 122  
 123          path = os.path.join(self.root_path, resource)
 124  
 125          if mode == "rb":
 126              return open(path, mode)  # pyright: ignore
 127  
 128          return open(path, mode, encoding=encoding)
 129