Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/helpers.py: 31%
140 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-09 06:08 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-09 06:08 +0000
1from __future__ import annotations
3import importlib.util
4import os
5import socket
6import sys
7import typing as t
8import warnings
9from datetime import datetime
10from functools import lru_cache
11from functools import update_wrapper
12from threading import RLock
14import werkzeug.utils
15from werkzeug.exceptions import abort as _wz_abort
16from werkzeug.utils import redirect as _wz_redirect
18from .globals import _cv_request
19from .globals import current_app
20from .globals import request
21from .globals import request_ctx
22from .globals import session
23from .signals import message_flashed
25if t.TYPE_CHECKING: # pragma: no cover
26 from werkzeug.wrappers import Response as BaseResponse
27 from .wrappers import Response
30def get_debug_flag() -> bool:
31 """Get whether debug mode should be enabled for the app, indicated by the
32 :envvar:`FLASK_DEBUG` environment variable. The default is ``False``.
33 """
34 val = os.environ.get("FLASK_DEBUG")
35 return bool(val and val.lower() not in {"0", "false", "no"})
38def get_load_dotenv(default: bool = True) -> bool:
39 """Get whether the user has disabled loading default dotenv files by
40 setting :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load
41 the files.
43 :param default: What to return if the env var isn't set.
44 """
45 val = os.environ.get("FLASK_SKIP_DOTENV")
47 if not val:
48 return default
50 return val.lower() in ("0", "false", "no")
53def stream_with_context(
54 generator_or_function: (
55 t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]]
56 )
57) -> t.Iterator[t.AnyStr]:
58 """Request contexts disappear when the response is started on the server.
59 This is done for efficiency reasons and to make it less likely to encounter
60 memory leaks with badly written WSGI middlewares. The downside is that if
61 you are using streamed responses, the generator cannot access request bound
62 information any more.
64 This function however can help you keep the context around for longer::
66 from flask import stream_with_context, request, Response
68 @app.route('/stream')
69 def streamed_response():
70 @stream_with_context
71 def generate():
72 yield 'Hello '
73 yield request.args['name']
74 yield '!'
75 return Response(generate())
77 Alternatively it can also be used around a specific generator::
79 from flask import stream_with_context, request, Response
81 @app.route('/stream')
82 def streamed_response():
83 def generate():
84 yield 'Hello '
85 yield request.args['name']
86 yield '!'
87 return Response(stream_with_context(generate()))
89 .. versionadded:: 0.9
90 """
91 try:
92 gen = iter(generator_or_function) # type: ignore
93 except TypeError:
95 def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any:
96 gen = generator_or_function(*args, **kwargs) # type: ignore
97 return stream_with_context(gen)
99 return update_wrapper(decorator, generator_or_function) # type: ignore
101 def generator() -> t.Generator:
102 ctx = _cv_request.get(None)
103 if ctx is None:
104 raise RuntimeError(
105 "'stream_with_context' can only be used when a request"
106 " context is active, such as in a view function."
107 )
108 with ctx:
109 # Dummy sentinel. Has to be inside the context block or we're
110 # not actually keeping the context around.
111 yield None
113 # The try/finally is here so that if someone passes a WSGI level
114 # iterator in we're still running the cleanup logic. Generators
115 # don't need that because they are closed on their destruction
116 # automatically.
117 try:
118 yield from gen
119 finally:
120 if hasattr(gen, "close"):
121 gen.close()
123 # The trick is to start the generator. Then the code execution runs until
124 # the first dummy None is yielded at which point the context was already
125 # pushed. This item is discarded. Then when the iteration continues the
126 # real generator is executed.
127 wrapped_g = generator()
128 next(wrapped_g)
129 return wrapped_g
132def make_response(*args: t.Any) -> Response:
133 """Sometimes it is necessary to set additional headers in a view. Because
134 views do not have to return response objects but can return a value that
135 is converted into a response object by Flask itself, it becomes tricky to
136 add headers to it. This function can be called instead of using a return
137 and you will get a response object which you can use to attach headers.
139 If view looked like this and you want to add a new header::
141 def index():
142 return render_template('index.html', foo=42)
144 You can now do something like this::
146 def index():
147 response = make_response(render_template('index.html', foo=42))
148 response.headers['X-Parachutes'] = 'parachutes are cool'
149 return response
151 This function accepts the very same arguments you can return from a
152 view function. This for example creates a response with a 404 error
153 code::
155 response = make_response(render_template('not_found.html'), 404)
157 The other use case of this function is to force the return value of a
158 view function into a response which is helpful with view
159 decorators::
161 response = make_response(view_function())
162 response.headers['X-Parachutes'] = 'parachutes are cool'
164 Internally this function does the following things:
166 - if no arguments are passed, it creates a new response argument
167 - if one argument is passed, :meth:`flask.Flask.make_response`
168 is invoked with it.
169 - if more than one argument is passed, the arguments are passed
170 to the :meth:`flask.Flask.make_response` function as tuple.
172 .. versionadded:: 0.6
173 """
174 if not args:
175 return current_app.response_class()
176 if len(args) == 1:
177 args = args[0]
178 return current_app.make_response(args) # type: ignore
181def url_for(
182 endpoint: str,
183 *,
184 _anchor: str | None = None,
185 _method: str | None = None,
186 _scheme: str | None = None,
187 _external: bool | None = None,
188 **values: t.Any,
189) -> str:
190 """Generate a URL to the given endpoint with the given values.
192 This requires an active request or application context, and calls
193 :meth:`current_app.url_for() <flask.Flask.url_for>`. See that method
194 for full documentation.
196 :param endpoint: The endpoint name associated with the URL to
197 generate. If this starts with a ``.``, the current blueprint
198 name (if any) will be used.
199 :param _anchor: If given, append this as ``#anchor`` to the URL.
200 :param _method: If given, generate the URL associated with this
201 method for the endpoint.
202 :param _scheme: If given, the URL will have this scheme if it is
203 external.
204 :param _external: If given, prefer the URL to be internal (False) or
205 require it to be external (True). External URLs include the
206 scheme and domain. When not in an active request, URLs are
207 external by default.
208 :param values: Values to use for the variable parts of the URL rule.
209 Unknown keys are appended as query string arguments, like
210 ``?a=b&c=d``.
212 .. versionchanged:: 2.2
213 Calls ``current_app.url_for``, allowing an app to override the
214 behavior.
216 .. versionchanged:: 0.10
217 The ``_scheme`` parameter was added.
219 .. versionchanged:: 0.9
220 The ``_anchor`` and ``_method`` parameters were added.
222 .. versionchanged:: 0.9
223 Calls ``app.handle_url_build_error`` on build errors.
224 """
225 return current_app.url_for(
226 endpoint,
227 _anchor=_anchor,
228 _method=_method,
229 _scheme=_scheme,
230 _external=_external,
231 **values,
232 )
235def redirect(
236 location: str, code: int = 302, Response: type[BaseResponse] | None = None
237) -> BaseResponse:
238 """Create a redirect response object.
240 If :data:`~flask.current_app` is available, it will use its
241 :meth:`~flask.Flask.redirect` method, otherwise it will use
242 :func:`werkzeug.utils.redirect`.
244 :param location: The URL to redirect to.
245 :param code: The status code for the redirect.
246 :param Response: The response class to use. Not used when
247 ``current_app`` is active, which uses ``app.response_class``.
249 .. versionadded:: 2.2
250 Calls ``current_app.redirect`` if available instead of always
251 using Werkzeug's default ``redirect``.
252 """
253 if current_app:
254 return current_app.redirect(location, code=code)
256 return _wz_redirect(location, code=code, Response=Response)
259def abort(code: int | BaseResponse, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
260 """Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given
261 status code.
263 If :data:`~flask.current_app` is available, it will call its
264 :attr:`~flask.Flask.aborter` object, otherwise it will use
265 :func:`werkzeug.exceptions.abort`.
267 :param code: The status code for the exception, which must be
268 registered in ``app.aborter``.
269 :param args: Passed to the exception.
270 :param kwargs: Passed to the exception.
272 .. versionadded:: 2.2
273 Calls ``current_app.aborter`` if available instead of always
274 using Werkzeug's default ``abort``.
275 """
276 if current_app:
277 current_app.aborter(code, *args, **kwargs)
279 _wz_abort(code, *args, **kwargs)
282def get_template_attribute(template_name: str, attribute: str) -> t.Any:
283 """Loads a macro (or variable) a template exports. This can be used to
284 invoke a macro from within Python code. If you for example have a
285 template named :file:`_cider.html` with the following contents:
287 .. sourcecode:: html+jinja
289 {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
291 You can access this from Python code like this::
293 hello = get_template_attribute('_cider.html', 'hello')
294 return hello('World')
296 .. versionadded:: 0.2
298 :param template_name: the name of the template
299 :param attribute: the name of the variable of macro to access
300 """
301 return getattr(current_app.jinja_env.get_template(template_name).module, attribute)
304def flash(message: str, category: str = "message") -> None:
305 """Flashes a message to the next request. In order to remove the
306 flashed message from the session and to display it to the user,
307 the template has to call :func:`get_flashed_messages`.
309 .. versionchanged:: 0.3
310 `category` parameter added.
312 :param message: the message to be flashed.
313 :param category: the category for the message. The following values
314 are recommended: ``'message'`` for any kind of message,
315 ``'error'`` for errors, ``'info'`` for information
316 messages and ``'warning'`` for warnings. However any
317 kind of string can be used as category.
318 """
319 # Original implementation:
320 #
321 # session.setdefault('_flashes', []).append((category, message))
322 #
323 # This assumed that changes made to mutable structures in the session are
324 # always in sync with the session object, which is not true for session
325 # implementations that use external storage for keeping their keys/values.
326 flashes = session.get("_flashes", [])
327 flashes.append((category, message))
328 session["_flashes"] = flashes
329 app = current_app._get_current_object() # type: ignore
330 message_flashed.send(
331 app,
332 _async_wrapper=app.ensure_sync,
333 message=message,
334 category=category,
335 )
338def get_flashed_messages(
339 with_categories: bool = False, category_filter: t.Iterable[str] = ()
340) -> list[str] | list[tuple[str, str]]:
341 """Pulls all flashed messages from the session and returns them.
342 Further calls in the same request to the function will return
343 the same messages. By default just the messages are returned,
344 but when `with_categories` is set to ``True``, the return value will
345 be a list of tuples in the form ``(category, message)`` instead.
347 Filter the flashed messages to one or more categories by providing those
348 categories in `category_filter`. This allows rendering categories in
349 separate html blocks. The `with_categories` and `category_filter`
350 arguments are distinct:
352 * `with_categories` controls whether categories are returned with message
353 text (``True`` gives a tuple, where ``False`` gives just the message text).
354 * `category_filter` filters the messages down to only those matching the
355 provided categories.
357 See :doc:`/patterns/flashing` for examples.
359 .. versionchanged:: 0.3
360 `with_categories` parameter added.
362 .. versionchanged:: 0.9
363 `category_filter` parameter added.
365 :param with_categories: set to ``True`` to also receive categories.
366 :param category_filter: filter of categories to limit return values. Only
367 categories in the list will be returned.
368 """
369 flashes = request_ctx.flashes
370 if flashes is None:
371 flashes = session.pop("_flashes") if "_flashes" in session else []
372 request_ctx.flashes = flashes
373 if category_filter:
374 flashes = list(filter(lambda f: f[0] in category_filter, flashes))
375 if not with_categories:
376 return [x[1] for x in flashes]
377 return flashes
380def _prepare_send_file_kwargs(**kwargs: t.Any) -> dict[str, t.Any]:
381 if kwargs.get("max_age") is None:
382 kwargs["max_age"] = current_app.get_send_file_max_age
384 kwargs.update(
385 environ=request.environ,
386 use_x_sendfile=current_app.config["USE_X_SENDFILE"],
387 response_class=current_app.response_class,
388 _root_path=current_app.root_path, # type: ignore
389 )
390 return kwargs
393def send_file(
394 path_or_file: os.PathLike | str | t.BinaryIO,
395 mimetype: str | None = None,
396 as_attachment: bool = False,
397 download_name: str | None = None,
398 conditional: bool = True,
399 etag: bool | str = True,
400 last_modified: datetime | int | float | None = None,
401 max_age: None | (int | t.Callable[[str | None], int | None]) = None,
402) -> Response:
403 """Send the contents of a file to the client.
405 The first argument can be a file path or a file-like object. Paths
406 are preferred in most cases because Werkzeug can manage the file and
407 get extra information from the path. Passing a file-like object
408 requires that the file is opened in binary mode, and is mostly
409 useful when building a file in memory with :class:`io.BytesIO`.
411 Never pass file paths provided by a user. The path is assumed to be
412 trusted, so a user could craft a path to access a file you didn't
413 intend. Use :func:`send_from_directory` to safely serve
414 user-requested paths from within a directory.
416 If the WSGI server sets a ``file_wrapper`` in ``environ``, it is
417 used, otherwise Werkzeug's built-in wrapper is used. Alternatively,
418 if the HTTP server supports ``X-Sendfile``, configuring Flask with
419 ``USE_X_SENDFILE = True`` will tell the server to send the given
420 path, which is much more efficient than reading it in Python.
422 :param path_or_file: The path to the file to send, relative to the
423 current working directory if a relative path is given.
424 Alternatively, a file-like object opened in binary mode. Make
425 sure the file pointer is seeked to the start of the data.
426 :param mimetype: The MIME type to send for the file. If not
427 provided, it will try to detect it from the file name.
428 :param as_attachment: Indicate to a browser that it should offer to
429 save the file instead of displaying it.
430 :param download_name: The default name browsers will use when saving
431 the file. Defaults to the passed file name.
432 :param conditional: Enable conditional and range responses based on
433 request headers. Requires passing a file path and ``environ``.
434 :param etag: Calculate an ETag for the file, which requires passing
435 a file path. Can also be a string to use instead.
436 :param last_modified: The last modified time to send for the file,
437 in seconds. If not provided, it will try to detect it from the
438 file path.
439 :param max_age: How long the client should cache the file, in
440 seconds. If set, ``Cache-Control`` will be ``public``, otherwise
441 it will be ``no-cache`` to prefer conditional caching.
443 .. versionchanged:: 2.0
444 ``download_name`` replaces the ``attachment_filename``
445 parameter. If ``as_attachment=False``, it is passed with
446 ``Content-Disposition: inline`` instead.
448 .. versionchanged:: 2.0
449 ``max_age`` replaces the ``cache_timeout`` parameter.
450 ``conditional`` is enabled and ``max_age`` is not set by
451 default.
453 .. versionchanged:: 2.0
454 ``etag`` replaces the ``add_etags`` parameter. It can be a
455 string to use instead of generating one.
457 .. versionchanged:: 2.0
458 Passing a file-like object that inherits from
459 :class:`~io.TextIOBase` will raise a :exc:`ValueError` rather
460 than sending an empty file.
462 .. versionadded:: 2.0
463 Moved the implementation to Werkzeug. This is now a wrapper to
464 pass some Flask-specific arguments.
466 .. versionchanged:: 1.1
467 ``filename`` may be a :class:`~os.PathLike` object.
469 .. versionchanged:: 1.1
470 Passing a :class:`~io.BytesIO` object supports range requests.
472 .. versionchanged:: 1.0.3
473 Filenames are encoded with ASCII instead of Latin-1 for broader
474 compatibility with WSGI servers.
476 .. versionchanged:: 1.0
477 UTF-8 filenames as specified in :rfc:`2231` are supported.
479 .. versionchanged:: 0.12
480 The filename is no longer automatically inferred from file
481 objects. If you want to use automatic MIME and etag support,
482 pass a filename via ``filename_or_fp`` or
483 ``attachment_filename``.
485 .. versionchanged:: 0.12
486 ``attachment_filename`` is preferred over ``filename`` for MIME
487 detection.
489 .. versionchanged:: 0.9
490 ``cache_timeout`` defaults to
491 :meth:`Flask.get_send_file_max_age`.
493 .. versionchanged:: 0.7
494 MIME guessing and etag support for file-like objects was
495 deprecated because it was unreliable. Pass a filename if you are
496 able to, otherwise attach an etag yourself.
498 .. versionchanged:: 0.5
499 The ``add_etags``, ``cache_timeout`` and ``conditional``
500 parameters were added. The default behavior is to add etags.
502 .. versionadded:: 0.2
503 """
504 return werkzeug.utils.send_file( # type: ignore[return-value]
505 **_prepare_send_file_kwargs(
506 path_or_file=path_or_file,
507 environ=request.environ,
508 mimetype=mimetype,
509 as_attachment=as_attachment,
510 download_name=download_name,
511 conditional=conditional,
512 etag=etag,
513 last_modified=last_modified,
514 max_age=max_age,
515 )
516 )
519def send_from_directory(
520 directory: os.PathLike | str,
521 path: os.PathLike | str,
522 **kwargs: t.Any,
523) -> Response:
524 """Send a file from within a directory using :func:`send_file`.
526 .. code-block:: python
528 @app.route("/uploads/<path:name>")
529 def download_file(name):
530 return send_from_directory(
531 app.config['UPLOAD_FOLDER'], name, as_attachment=True
532 )
534 This is a secure way to serve files from a folder, such as static
535 files or uploads. Uses :func:`~werkzeug.security.safe_join` to
536 ensure the path coming from the client is not maliciously crafted to
537 point outside the specified directory.
539 If the final path does not point to an existing regular file,
540 raises a 404 :exc:`~werkzeug.exceptions.NotFound` error.
542 :param directory: The directory that ``path`` must be located under,
543 relative to the current application's root path.
544 :param path: The path to the file to send, relative to
545 ``directory``.
546 :param kwargs: Arguments to pass to :func:`send_file`.
548 .. versionchanged:: 2.0
549 ``path`` replaces the ``filename`` parameter.
551 .. versionadded:: 2.0
552 Moved the implementation to Werkzeug. This is now a wrapper to
553 pass some Flask-specific arguments.
555 .. versionadded:: 0.5
556 """
557 return werkzeug.utils.send_from_directory( # type: ignore[return-value]
558 directory, path, **_prepare_send_file_kwargs(**kwargs)
559 )
562def get_root_path(import_name: str) -> str:
563 """Find the root path of a package, or the path that contains a
564 module. If it cannot be found, returns the current working
565 directory.
567 Not to be confused with the value returned by :func:`find_package`.
569 :meta private:
570 """
571 # Module already imported and has a file attribute. Use that first.
572 mod = sys.modules.get(import_name)
574 if mod is not None and hasattr(mod, "__file__") and mod.__file__ is not None:
575 return os.path.dirname(os.path.abspath(mod.__file__))
577 # Next attempt: check the loader.
578 spec = importlib.util.find_spec(import_name)
579 loader = spec.loader if spec is not None else None
581 # Loader does not exist or we're referring to an unloaded main
582 # module or a main module without path (interactive sessions), go
583 # with the current working directory.
584 if loader is None or import_name == "__main__":
585 return os.getcwd()
587 if hasattr(loader, "get_filename"):
588 filepath = loader.get_filename(import_name)
589 else:
590 # Fall back to imports.
591 __import__(import_name)
592 mod = sys.modules[import_name]
593 filepath = getattr(mod, "__file__", None)
595 # If we don't have a file path it might be because it is a
596 # namespace package. In this case pick the root path from the
597 # first module that is contained in the package.
598 if filepath is None:
599 raise RuntimeError(
600 "No root path can be found for the provided module"
601 f" {import_name!r}. This can happen because the module"
602 " came from an import hook that does not provide file"
603 " name information or because it's a namespace package."
604 " In this case the root path needs to be explicitly"
605 " provided."
606 )
608 # filepath is import_name.py for a module, or __init__.py for a package.
609 return os.path.dirname(os.path.abspath(filepath))
612class locked_cached_property(werkzeug.utils.cached_property):
613 """A :func:`property` that is only evaluated once. Like
614 :class:`werkzeug.utils.cached_property` except access uses a lock
615 for thread safety.
617 .. deprecated:: 2.3
618 Will be removed in Flask 2.4. Use a lock inside the decorated function if
619 locking is needed.
621 .. versionchanged:: 2.0
622 Inherits from Werkzeug's ``cached_property`` (and ``property``).
623 """
625 def __init__(
626 self,
627 fget: t.Callable[[t.Any], t.Any],
628 name: str | None = None,
629 doc: str | None = None,
630 ) -> None:
631 import warnings
633 warnings.warn(
634 "'locked_cached_property' is deprecated and will be removed in Flask 2.4."
635 " Use a lock inside the decorated function if locking is needed.",
636 DeprecationWarning,
637 stacklevel=2,
638 )
639 super().__init__(fget, name=name, doc=doc)
640 self.lock = RLock()
642 def __get__(self, obj: object, type: type = None) -> t.Any: # type: ignore
643 if obj is None:
644 return self
646 with self.lock:
647 return super().__get__(obj, type=type)
649 def __set__(self, obj: object, value: t.Any) -> None:
650 with self.lock:
651 super().__set__(obj, value)
653 def __delete__(self, obj: object) -> None:
654 with self.lock:
655 super().__delete__(obj)
658def is_ip(value: str) -> bool:
659 """Determine if the given string is an IP address.
661 :param value: value to check
662 :type value: str
664 :return: True if string is an IP address
665 :rtype: bool
667 .. deprecated:: 2.3
668 Will be removed in Flask 2.4.
669 """
670 warnings.warn(
671 "The 'is_ip' function is deprecated and will be removed in Flask 2.4.",
672 DeprecationWarning,
673 stacklevel=2,
674 )
676 for family in (socket.AF_INET, socket.AF_INET6):
677 try:
678 socket.inet_pton(family, value)
679 except OSError:
680 pass
681 else:
682 return True
684 return False
687@lru_cache(maxsize=None)
688def _split_blueprint_path(name: str) -> list[str]:
689 out: list[str] = [name]
691 if "." in name:
692 out.extend(_split_blueprint_path(name.rpartition(".")[0]))
694 return out