codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import os4import typing as t5from datetime import timedelta6 7from .cli import AppGroup8from .globals import current_app9from .helpers import send_from_directory10from .sansio.blueprints import Blueprint as SansioBlueprint11from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState # noqa12from .sansio.scaffold import _sentinel13 14if t.TYPE_CHECKING: # pragma: no cover15 from .wrappers import Response16 17 18class 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: ignore31 ) -> 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 this46 #: object. The commands are available from the ``flask`` command47 #: once the application has been discovered and blueprints have48 #: been registered.49 self.cli = AppGroup()50 51 # Set the name of the Click group in case someone wants to add52 # the app's commands to another CLI tool.53 self.cli.name = self.name54 55 def get_send_file_max_age(self, filename: str | None) -> int | None:56 """Used by :func:`send_file` to determine the ``max_age`` cache57 value for a given file path if it wasn't passed.58 59 By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from60 the configuration of :data:`~flask.current_app`. This defaults61 to ``None``, which tells the browser to use conditional requests62 instead of a timed cache, which is usually preferable.63 64 Note this is a duplicate of the same method in the Flask65 class.66 67 .. versionchanged:: 2.068 The default configuration is ``None`` instead of 12 hours.69 70 .. versionadded:: 0.971 """72 value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"]73 74 if value is None:75 return None76 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 from84 :attr:`static_folder`. A route is automatically registered for85 this view at :attr:`static_url_path` if :attr:`static_folder` is86 set.87 88 Note this is a duplicate of the same method in the Flask89 class.90 91 .. versionadded:: 0.592 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_age102 )103 104 def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:105 """Open a resource file relative to :attr:`root_path` for106 reading.107 108 For example, if the file ``schema.sql`` is next to the file109 ``app.py`` where the ``Flask`` app is defined, it can be opened110 with:111 112 .. code-block:: python113 114 with app.open_resource("schema.sql") as f:115 conn.executescript(f.read())116 117 :param resource: Path to the resource relative to118 :attr:`root_path`.119 :param mode: Open the file in this mode. Only reading is120 supported, valid values are "r" (or "rt") and "rb".121 122 Note this is a duplicate of the same method in the Flask123 class.124 125 """126 if mode not in {"r", "rt", "rb"}:127 raise ValueError("Resources can only be opened for reading.")128 129 return open(os.path.join(self.root_path, resource), mode)130 