"Fossies" - the Fresh Open Source Software Archive

Member "flask-1.1.2/src/flask/helpers.py" (3 Apr 2020, 43074 Bytes) of package /linux/www/flask-1.1.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "helpers.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.1.1_vs_1.1.2.

    1 # -*- coding: utf-8 -*-
    2 """
    3     flask.helpers
    4     ~~~~~~~~~~~~~
    5 
    6     Implements various helpers.
    7 
    8     :copyright: 2010 Pallets
    9     :license: BSD-3-Clause
   10 """
   11 import io
   12 import mimetypes
   13 import os
   14 import pkgutil
   15 import posixpath
   16 import socket
   17 import sys
   18 import unicodedata
   19 from functools import update_wrapper
   20 from threading import RLock
   21 from time import time
   22 from zlib import adler32
   23 
   24 from jinja2 import FileSystemLoader
   25 from werkzeug.datastructures import Headers
   26 from werkzeug.exceptions import BadRequest
   27 from werkzeug.exceptions import NotFound
   28 from werkzeug.exceptions import RequestedRangeNotSatisfiable
   29 from werkzeug.routing import BuildError
   30 from werkzeug.urls import url_quote
   31 from werkzeug.wsgi import wrap_file
   32 
   33 from ._compat import fspath
   34 from ._compat import PY2
   35 from ._compat import string_types
   36 from ._compat import text_type
   37 from .globals import _app_ctx_stack
   38 from .globals import _request_ctx_stack
   39 from .globals import current_app
   40 from .globals import request
   41 from .globals import session
   42 from .signals import message_flashed
   43 
   44 # sentinel
   45 _missing = object()
   46 
   47 
   48 # what separators does this operating system provide that are not a slash?
   49 # this is used by the send_from_directory function to ensure that nobody is
   50 # able to access files from outside the filesystem.
   51 _os_alt_seps = list(
   52     sep for sep in [os.path.sep, os.path.altsep] if sep not in (None, "/")
   53 )
   54 
   55 
   56 def get_env():
   57     """Get the environment the app is running in, indicated by the
   58     :envvar:`FLASK_ENV` environment variable. The default is
   59     ``'production'``.
   60     """
   61     return os.environ.get("FLASK_ENV") or "production"
   62 
   63 
   64 def get_debug_flag():
   65     """Get whether debug mode should be enabled for the app, indicated
   66     by the :envvar:`FLASK_DEBUG` environment variable. The default is
   67     ``True`` if :func:`.get_env` returns ``'development'``, or ``False``
   68     otherwise.
   69     """
   70     val = os.environ.get("FLASK_DEBUG")
   71 
   72     if not val:
   73         return get_env() == "development"
   74 
   75     return val.lower() not in ("0", "false", "no")
   76 
   77 
   78 def get_load_dotenv(default=True):
   79     """Get whether the user has disabled loading dotenv files by setting
   80     :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the
   81     files.
   82 
   83     :param default: What to return if the env var isn't set.
   84     """
   85     val = os.environ.get("FLASK_SKIP_DOTENV")
   86 
   87     if not val:
   88         return default
   89 
   90     return val.lower() in ("0", "false", "no")
   91 
   92 
   93 def _endpoint_from_view_func(view_func):
   94     """Internal helper that returns the default endpoint for a given
   95     function.  This always is the function name.
   96     """
   97     assert view_func is not None, "expected view func if endpoint is not provided."
   98     return view_func.__name__
   99 
  100 
  101 def stream_with_context(generator_or_function):
  102     """Request contexts disappear when the response is started on the server.
  103     This is done for efficiency reasons and to make it less likely to encounter
  104     memory leaks with badly written WSGI middlewares.  The downside is that if
  105     you are using streamed responses, the generator cannot access request bound
  106     information any more.
  107 
  108     This function however can help you keep the context around for longer::
  109 
  110         from flask import stream_with_context, request, Response
  111 
  112         @app.route('/stream')
  113         def streamed_response():
  114             @stream_with_context
  115             def generate():
  116                 yield 'Hello '
  117                 yield request.args['name']
  118                 yield '!'
  119             return Response(generate())
  120 
  121     Alternatively it can also be used around a specific generator::
  122 
  123         from flask import stream_with_context, request, Response
  124 
  125         @app.route('/stream')
  126         def streamed_response():
  127             def generate():
  128                 yield 'Hello '
  129                 yield request.args['name']
  130                 yield '!'
  131             return Response(stream_with_context(generate()))
  132 
  133     .. versionadded:: 0.9
  134     """
  135     try:
  136         gen = iter(generator_or_function)
  137     except TypeError:
  138 
  139         def decorator(*args, **kwargs):
  140             gen = generator_or_function(*args, **kwargs)
  141             return stream_with_context(gen)
  142 
  143         return update_wrapper(decorator, generator_or_function)
  144 
  145     def generator():
  146         ctx = _request_ctx_stack.top
  147         if ctx is None:
  148             raise RuntimeError(
  149                 "Attempted to stream with context but "
  150                 "there was no context in the first place to keep around."
  151             )
  152         with ctx:
  153             # Dummy sentinel.  Has to be inside the context block or we're
  154             # not actually keeping the context around.
  155             yield None
  156 
  157             # The try/finally is here so that if someone passes a WSGI level
  158             # iterator in we're still running the cleanup logic.  Generators
  159             # don't need that because they are closed on their destruction
  160             # automatically.
  161             try:
  162                 for item in gen:
  163                     yield item
  164             finally:
  165                 if hasattr(gen, "close"):
  166                     gen.close()
  167 
  168     # The trick is to start the generator.  Then the code execution runs until
  169     # the first dummy None is yielded at which point the context was already
  170     # pushed.  This item is discarded.  Then when the iteration continues the
  171     # real generator is executed.
  172     wrapped_g = generator()
  173     next(wrapped_g)
  174     return wrapped_g
  175 
  176 
  177 def make_response(*args):
  178     """Sometimes it is necessary to set additional headers in a view.  Because
  179     views do not have to return response objects but can return a value that
  180     is converted into a response object by Flask itself, it becomes tricky to
  181     add headers to it.  This function can be called instead of using a return
  182     and you will get a response object which you can use to attach headers.
  183 
  184     If view looked like this and you want to add a new header::
  185 
  186         def index():
  187             return render_template('index.html', foo=42)
  188 
  189     You can now do something like this::
  190 
  191         def index():
  192             response = make_response(render_template('index.html', foo=42))
  193             response.headers['X-Parachutes'] = 'parachutes are cool'
  194             return response
  195 
  196     This function accepts the very same arguments you can return from a
  197     view function.  This for example creates a response with a 404 error
  198     code::
  199 
  200         response = make_response(render_template('not_found.html'), 404)
  201 
  202     The other use case of this function is to force the return value of a
  203     view function into a response which is helpful with view
  204     decorators::
  205 
  206         response = make_response(view_function())
  207         response.headers['X-Parachutes'] = 'parachutes are cool'
  208 
  209     Internally this function does the following things:
  210 
  211     -   if no arguments are passed, it creates a new response argument
  212     -   if one argument is passed, :meth:`flask.Flask.make_response`
  213         is invoked with it.
  214     -   if more than one argument is passed, the arguments are passed
  215         to the :meth:`flask.Flask.make_response` function as tuple.
  216 
  217     .. versionadded:: 0.6
  218     """
  219     if not args:
  220         return current_app.response_class()
  221     if len(args) == 1:
  222         args = args[0]
  223     return current_app.make_response(args)
  224 
  225 
  226 def url_for(endpoint, **values):
  227     """Generates a URL to the given endpoint with the method provided.
  228 
  229     Variable arguments that are unknown to the target endpoint are appended
  230     to the generated URL as query arguments.  If the value of a query argument
  231     is ``None``, the whole pair is skipped.  In case blueprints are active
  232     you can shortcut references to the same blueprint by prefixing the
  233     local endpoint with a dot (``.``).
  234 
  235     This will reference the index function local to the current blueprint::
  236 
  237         url_for('.index')
  238 
  239     For more information, head over to the :ref:`Quickstart <url-building>`.
  240 
  241     Configuration values ``APPLICATION_ROOT`` and ``SERVER_NAME`` are only used when
  242     generating URLs outside of a request context.
  243 
  244     To integrate applications, :class:`Flask` has a hook to intercept URL build
  245     errors through :attr:`Flask.url_build_error_handlers`.  The `url_for`
  246     function results in a :exc:`~werkzeug.routing.BuildError` when the current
  247     app does not have a URL for the given endpoint and values.  When it does, the
  248     :data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if
  249     it is not ``None``, which can return a string to use as the result of
  250     `url_for` (instead of `url_for`'s default to raise the
  251     :exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
  252     An example::
  253 
  254         def external_url_handler(error, endpoint, values):
  255             "Looks up an external URL when `url_for` cannot build a URL."
  256             # This is an example of hooking the build_error_handler.
  257             # Here, lookup_url is some utility function you've built
  258             # which looks up the endpoint in some external URL registry.
  259             url = lookup_url(endpoint, **values)
  260             if url is None:
  261                 # External lookup did not have a URL.
  262                 # Re-raise the BuildError, in context of original traceback.
  263                 exc_type, exc_value, tb = sys.exc_info()
  264                 if exc_value is error:
  265                     raise exc_type, exc_value, tb
  266                 else:
  267                     raise error
  268             # url_for will use this result, instead of raising BuildError.
  269             return url
  270 
  271         app.url_build_error_handlers.append(external_url_handler)
  272 
  273     Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
  274     `endpoint` and `values` are the arguments passed into `url_for`.  Note
  275     that this is for building URLs outside the current application, and not for
  276     handling 404 NotFound errors.
  277 
  278     .. versionadded:: 0.10
  279        The `_scheme` parameter was added.
  280 
  281     .. versionadded:: 0.9
  282        The `_anchor` and `_method` parameters were added.
  283 
  284     .. versionadded:: 0.9
  285        Calls :meth:`Flask.handle_build_error` on
  286        :exc:`~werkzeug.routing.BuildError`.
  287 
  288     :param endpoint: the endpoint of the URL (name of the function)
  289     :param values: the variable arguments of the URL rule
  290     :param _external: if set to ``True``, an absolute URL is generated. Server
  291       address can be changed via ``SERVER_NAME`` configuration variable which
  292       falls back to the `Host` header, then to the IP and port of the request.
  293     :param _scheme: a string specifying the desired URL scheme. The `_external`
  294       parameter must be set to ``True`` or a :exc:`ValueError` is raised. The default
  295       behavior uses the same scheme as the current request, or
  296       ``PREFERRED_URL_SCHEME`` from the :ref:`app configuration <config>` if no
  297       request context is available. As of Werkzeug 0.10, this also can be set
  298       to an empty string to build protocol-relative URLs.
  299     :param _anchor: if provided this is added as anchor to the URL.
  300     :param _method: if provided this explicitly specifies an HTTP method.
  301     """
  302     appctx = _app_ctx_stack.top
  303     reqctx = _request_ctx_stack.top
  304 
  305     if appctx is None:
  306         raise RuntimeError(
  307             "Attempted to generate a URL without the application context being"
  308             " pushed. This has to be executed when application context is"
  309             " available."
  310         )
  311 
  312     # If request specific information is available we have some extra
  313     # features that support "relative" URLs.
  314     if reqctx is not None:
  315         url_adapter = reqctx.url_adapter
  316         blueprint_name = request.blueprint
  317 
  318         if endpoint[:1] == ".":
  319             if blueprint_name is not None:
  320                 endpoint = blueprint_name + endpoint
  321             else:
  322                 endpoint = endpoint[1:]
  323 
  324         external = values.pop("_external", False)
  325 
  326     # Otherwise go with the url adapter from the appctx and make
  327     # the URLs external by default.
  328     else:
  329         url_adapter = appctx.url_adapter
  330 
  331         if url_adapter is None:
  332             raise RuntimeError(
  333                 "Application was not able to create a URL adapter for request"
  334                 " independent URL generation. You might be able to fix this by"
  335                 " setting the SERVER_NAME config variable."
  336             )
  337 
  338         external = values.pop("_external", True)
  339 
  340     anchor = values.pop("_anchor", None)
  341     method = values.pop("_method", None)
  342     scheme = values.pop("_scheme", None)
  343     appctx.app.inject_url_defaults(endpoint, values)
  344 
  345     # This is not the best way to deal with this but currently the
  346     # underlying Werkzeug router does not support overriding the scheme on
  347     # a per build call basis.
  348     old_scheme = None
  349     if scheme is not None:
  350         if not external:
  351             raise ValueError("When specifying _scheme, _external must be True")
  352         old_scheme = url_adapter.url_scheme
  353         url_adapter.url_scheme = scheme
  354 
  355     try:
  356         try:
  357             rv = url_adapter.build(
  358                 endpoint, values, method=method, force_external=external
  359             )
  360         finally:
  361             if old_scheme is not None:
  362                 url_adapter.url_scheme = old_scheme
  363     except BuildError as error:
  364         # We need to inject the values again so that the app callback can
  365         # deal with that sort of stuff.
  366         values["_external"] = external
  367         values["_anchor"] = anchor
  368         values["_method"] = method
  369         values["_scheme"] = scheme
  370         return appctx.app.handle_url_build_error(error, endpoint, values)
  371 
  372     if anchor is not None:
  373         rv += "#" + url_quote(anchor)
  374     return rv
  375 
  376 
  377 def get_template_attribute(template_name, attribute):
  378     """Loads a macro (or variable) a template exports.  This can be used to
  379     invoke a macro from within Python code.  If you for example have a
  380     template named :file:`_cider.html` with the following contents:
  381 
  382     .. sourcecode:: html+jinja
  383 
  384        {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
  385 
  386     You can access this from Python code like this::
  387 
  388         hello = get_template_attribute('_cider.html', 'hello')
  389         return hello('World')
  390 
  391     .. versionadded:: 0.2
  392 
  393     :param template_name: the name of the template
  394     :param attribute: the name of the variable of macro to access
  395     """
  396     return getattr(current_app.jinja_env.get_template(template_name).module, attribute)
  397 
  398 
  399 def flash(message, category="message"):
  400     """Flashes a message to the next request.  In order to remove the
  401     flashed message from the session and to display it to the user,
  402     the template has to call :func:`get_flashed_messages`.
  403 
  404     .. versionchanged:: 0.3
  405        `category` parameter added.
  406 
  407     :param message: the message to be flashed.
  408     :param category: the category for the message.  The following values
  409                      are recommended: ``'message'`` for any kind of message,
  410                      ``'error'`` for errors, ``'info'`` for information
  411                      messages and ``'warning'`` for warnings.  However any
  412                      kind of string can be used as category.
  413     """
  414     # Original implementation:
  415     #
  416     #     session.setdefault('_flashes', []).append((category, message))
  417     #
  418     # This assumed that changes made to mutable structures in the session are
  419     # always in sync with the session object, which is not true for session
  420     # implementations that use external storage for keeping their keys/values.
  421     flashes = session.get("_flashes", [])
  422     flashes.append((category, message))
  423     session["_flashes"] = flashes
  424     message_flashed.send(
  425         current_app._get_current_object(), message=message, category=category
  426     )
  427 
  428 
  429 def get_flashed_messages(with_categories=False, category_filter=()):
  430     """Pulls all flashed messages from the session and returns them.
  431     Further calls in the same request to the function will return
  432     the same messages.  By default just the messages are returned,
  433     but when `with_categories` is set to ``True``, the return value will
  434     be a list of tuples in the form ``(category, message)`` instead.
  435 
  436     Filter the flashed messages to one or more categories by providing those
  437     categories in `category_filter`.  This allows rendering categories in
  438     separate html blocks.  The `with_categories` and `category_filter`
  439     arguments are distinct:
  440 
  441     * `with_categories` controls whether categories are returned with message
  442       text (``True`` gives a tuple, where ``False`` gives just the message text).
  443     * `category_filter` filters the messages down to only those matching the
  444       provided categories.
  445 
  446     See :ref:`message-flashing-pattern` for examples.
  447 
  448     .. versionchanged:: 0.3
  449        `with_categories` parameter added.
  450 
  451     .. versionchanged:: 0.9
  452         `category_filter` parameter added.
  453 
  454     :param with_categories: set to ``True`` to also receive categories.
  455     :param category_filter: whitelist of categories to limit return values
  456     """
  457     flashes = _request_ctx_stack.top.flashes
  458     if flashes is None:
  459         _request_ctx_stack.top.flashes = flashes = (
  460             session.pop("_flashes") if "_flashes" in session else []
  461         )
  462     if category_filter:
  463         flashes = list(filter(lambda f: f[0] in category_filter, flashes))
  464     if not with_categories:
  465         return [x[1] for x in flashes]
  466     return flashes
  467 
  468 
  469 def send_file(
  470     filename_or_fp,
  471     mimetype=None,
  472     as_attachment=False,
  473     attachment_filename=None,
  474     add_etags=True,
  475     cache_timeout=None,
  476     conditional=False,
  477     last_modified=None,
  478 ):
  479     """Sends the contents of a file to the client.  This will use the
  480     most efficient method available and configured.  By default it will
  481     try to use the WSGI server's file_wrapper support.  Alternatively
  482     you can set the application's :attr:`~Flask.use_x_sendfile` attribute
  483     to ``True`` to directly emit an ``X-Sendfile`` header.  This however
  484     requires support of the underlying webserver for ``X-Sendfile``.
  485 
  486     By default it will try to guess the mimetype for you, but you can
  487     also explicitly provide one.  For extra security you probably want
  488     to send certain files as attachment (HTML for instance).  The mimetype
  489     guessing requires a `filename` or an `attachment_filename` to be
  490     provided.
  491 
  492     ETags will also be attached automatically if a `filename` is provided. You
  493     can turn this off by setting `add_etags=False`.
  494 
  495     If `conditional=True` and `filename` is provided, this method will try to
  496     upgrade the response stream to support range requests.  This will allow
  497     the request to be answered with partial content response.
  498 
  499     Please never pass filenames to this function from user sources;
  500     you should use :func:`send_from_directory` instead.
  501 
  502     .. versionadded:: 0.2
  503 
  504     .. versionadded:: 0.5
  505        The `add_etags`, `cache_timeout` and `conditional` parameters were
  506        added.  The default behavior is now to attach etags.
  507 
  508     .. versionchanged:: 0.7
  509        mimetype guessing and etag support for file objects was
  510        deprecated because it was unreliable.  Pass a filename if you are
  511        able to, otherwise attach an etag yourself.  This functionality
  512        will be removed in Flask 1.0
  513 
  514     .. versionchanged:: 0.9
  515        cache_timeout pulls its default from application config, when None.
  516 
  517     .. versionchanged:: 0.12
  518        The filename is no longer automatically inferred from file objects. If
  519        you want to use automatic mimetype and etag support, pass a filepath via
  520        `filename_or_fp` or `attachment_filename`.
  521 
  522     .. versionchanged:: 0.12
  523        The `attachment_filename` is preferred over `filename` for MIME-type
  524        detection.
  525 
  526     .. versionchanged:: 1.0
  527         UTF-8 filenames, as specified in `RFC 2231`_, are supported.
  528 
  529     .. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4
  530 
  531     .. versionchanged:: 1.0.3
  532         Filenames are encoded with ASCII instead of Latin-1 for broader
  533         compatibility with WSGI servers.
  534 
  535     .. versionchanged:: 1.1
  536         Filename may be a :class:`~os.PathLike` object.
  537 
  538     .. versionadded:: 1.1
  539         Partial content supports :class:`~io.BytesIO`.
  540 
  541     :param filename_or_fp: the filename of the file to send.
  542                            This is relative to the :attr:`~Flask.root_path`
  543                            if a relative path is specified.
  544                            Alternatively a file object might be provided in
  545                            which case ``X-Sendfile`` might not work and fall
  546                            back to the traditional method.  Make sure that the
  547                            file pointer is positioned at the start of data to
  548                            send before calling :func:`send_file`.
  549     :param mimetype: the mimetype of the file if provided. If a file path is
  550                      given, auto detection happens as fallback, otherwise an
  551                      error will be raised.
  552     :param as_attachment: set to ``True`` if you want to send this file with
  553                           a ``Content-Disposition: attachment`` header.
  554     :param attachment_filename: the filename for the attachment if it
  555                                 differs from the file's filename.
  556     :param add_etags: set to ``False`` to disable attaching of etags.
  557     :param conditional: set to ``True`` to enable conditional responses.
  558 
  559     :param cache_timeout: the timeout in seconds for the headers. When ``None``
  560                           (default), this value is set by
  561                           :meth:`~Flask.get_send_file_max_age` of
  562                           :data:`~flask.current_app`.
  563     :param last_modified: set the ``Last-Modified`` header to this value,
  564         a :class:`~datetime.datetime` or timestamp.
  565         If a file was passed, this overrides its mtime.
  566     """
  567     mtime = None
  568     fsize = None
  569 
  570     if hasattr(filename_or_fp, "__fspath__"):
  571         filename_or_fp = fspath(filename_or_fp)
  572 
  573     if isinstance(filename_or_fp, string_types):
  574         filename = filename_or_fp
  575         if not os.path.isabs(filename):
  576             filename = os.path.join(current_app.root_path, filename)
  577         file = None
  578         if attachment_filename is None:
  579             attachment_filename = os.path.basename(filename)
  580     else:
  581         file = filename_or_fp
  582         filename = None
  583 
  584     if mimetype is None:
  585         if attachment_filename is not None:
  586             mimetype = (
  587                 mimetypes.guess_type(attachment_filename)[0]
  588                 or "application/octet-stream"
  589             )
  590 
  591         if mimetype is None:
  592             raise ValueError(
  593                 "Unable to infer MIME-type because no filename is available. "
  594                 "Please set either `attachment_filename`, pass a filepath to "
  595                 "`filename_or_fp` or set your own MIME-type via `mimetype`."
  596             )
  597 
  598     headers = Headers()
  599     if as_attachment:
  600         if attachment_filename is None:
  601             raise TypeError("filename unavailable, required for sending as attachment")
  602 
  603         if not isinstance(attachment_filename, text_type):
  604             attachment_filename = attachment_filename.decode("utf-8")
  605 
  606         try:
  607             attachment_filename = attachment_filename.encode("ascii")
  608         except UnicodeEncodeError:
  609             filenames = {
  610                 "filename": unicodedata.normalize("NFKD", attachment_filename).encode(
  611                     "ascii", "ignore"
  612                 ),
  613                 "filename*": "UTF-8''%s" % url_quote(attachment_filename, safe=b""),
  614             }
  615         else:
  616             filenames = {"filename": attachment_filename}
  617 
  618         headers.add("Content-Disposition", "attachment", **filenames)
  619 
  620     if current_app.use_x_sendfile and filename:
  621         if file is not None:
  622             file.close()
  623         headers["X-Sendfile"] = filename
  624         fsize = os.path.getsize(filename)
  625         headers["Content-Length"] = fsize
  626         data = None
  627     else:
  628         if file is None:
  629             file = open(filename, "rb")
  630             mtime = os.path.getmtime(filename)
  631             fsize = os.path.getsize(filename)
  632             headers["Content-Length"] = fsize
  633         elif isinstance(file, io.BytesIO):
  634             try:
  635                 fsize = file.getbuffer().nbytes
  636             except AttributeError:
  637                 # Python 2 doesn't have getbuffer
  638                 fsize = len(file.getvalue())
  639             headers["Content-Length"] = fsize
  640         data = wrap_file(request.environ, file)
  641 
  642     rv = current_app.response_class(
  643         data, mimetype=mimetype, headers=headers, direct_passthrough=True
  644     )
  645 
  646     if last_modified is not None:
  647         rv.last_modified = last_modified
  648     elif mtime is not None:
  649         rv.last_modified = mtime
  650 
  651     rv.cache_control.public = True
  652     if cache_timeout is None:
  653         cache_timeout = current_app.get_send_file_max_age(filename)
  654     if cache_timeout is not None:
  655         rv.cache_control.max_age = cache_timeout
  656         rv.expires = int(time() + cache_timeout)
  657 
  658     if add_etags and filename is not None:
  659         from warnings import warn
  660 
  661         try:
  662             rv.set_etag(
  663                 "%s-%s-%s"
  664                 % (
  665                     os.path.getmtime(filename),
  666                     os.path.getsize(filename),
  667                     adler32(
  668                         filename.encode("utf-8")
  669                         if isinstance(filename, text_type)
  670                         else filename
  671                     )
  672                     & 0xFFFFFFFF,
  673                 )
  674             )
  675         except OSError:
  676             warn(
  677                 "Access %s failed, maybe it does not exist, so ignore etags in "
  678                 "headers" % filename,
  679                 stacklevel=2,
  680             )
  681 
  682     if conditional:
  683         try:
  684             rv = rv.make_conditional(request, accept_ranges=True, complete_length=fsize)
  685         except RequestedRangeNotSatisfiable:
  686             if file is not None:
  687                 file.close()
  688             raise
  689         # make sure we don't send x-sendfile for servers that
  690         # ignore the 304 status code for x-sendfile.
  691         if rv.status_code == 304:
  692             rv.headers.pop("x-sendfile", None)
  693     return rv
  694 
  695 
  696 def safe_join(directory, *pathnames):
  697     """Safely join `directory` and zero or more untrusted `pathnames`
  698     components.
  699 
  700     Example usage::
  701 
  702         @app.route('/wiki/<path:filename>')
  703         def wiki_page(filename):
  704             filename = safe_join(app.config['WIKI_FOLDER'], filename)
  705             with open(filename, 'rb') as fd:
  706                 content = fd.read()  # Read and process the file content...
  707 
  708     :param directory: the trusted base directory.
  709     :param pathnames: the untrusted pathnames relative to that directory.
  710     :raises: :class:`~werkzeug.exceptions.NotFound` if one or more passed
  711             paths fall out of its boundaries.
  712     """
  713 
  714     parts = [directory]
  715 
  716     for filename in pathnames:
  717         if filename != "":
  718             filename = posixpath.normpath(filename)
  719 
  720         if (
  721             any(sep in filename for sep in _os_alt_seps)
  722             or os.path.isabs(filename)
  723             or filename == ".."
  724             or filename.startswith("../")
  725         ):
  726             raise NotFound()
  727 
  728         parts.append(filename)
  729 
  730     return posixpath.join(*parts)
  731 
  732 
  733 def send_from_directory(directory, filename, **options):
  734     """Send a file from a given directory with :func:`send_file`.  This
  735     is a secure way to quickly expose static files from an upload folder
  736     or something similar.
  737 
  738     Example usage::
  739 
  740         @app.route('/uploads/<path:filename>')
  741         def download_file(filename):
  742             return send_from_directory(app.config['UPLOAD_FOLDER'],
  743                                        filename, as_attachment=True)
  744 
  745     .. admonition:: Sending files and Performance
  746 
  747        It is strongly recommended to activate either ``X-Sendfile`` support in
  748        your webserver or (if no authentication happens) to tell the webserver
  749        to serve files for the given path on its own without calling into the
  750        web application for improved performance.
  751 
  752     .. versionadded:: 0.5
  753 
  754     :param directory: the directory where all the files are stored.
  755     :param filename: the filename relative to that directory to
  756                      download.
  757     :param options: optional keyword arguments that are directly
  758                     forwarded to :func:`send_file`.
  759     """
  760     filename = fspath(filename)
  761     directory = fspath(directory)
  762     filename = safe_join(directory, filename)
  763     if not os.path.isabs(filename):
  764         filename = os.path.join(current_app.root_path, filename)
  765     try:
  766         if not os.path.isfile(filename):
  767             raise NotFound()
  768     except (TypeError, ValueError):
  769         raise BadRequest()
  770     options.setdefault("conditional", True)
  771     return send_file(filename, **options)
  772 
  773 
  774 def get_root_path(import_name):
  775     """Returns the path to a package or cwd if that cannot be found.  This
  776     returns the path of a package or the folder that contains a module.
  777 
  778     Not to be confused with the package path returned by :func:`find_package`.
  779     """
  780     # Module already imported and has a file attribute.  Use that first.
  781     mod = sys.modules.get(import_name)
  782     if mod is not None and hasattr(mod, "__file__"):
  783         return os.path.dirname(os.path.abspath(mod.__file__))
  784 
  785     # Next attempt: check the loader.
  786     loader = pkgutil.get_loader(import_name)
  787 
  788     # Loader does not exist or we're referring to an unloaded main module
  789     # or a main module without path (interactive sessions), go with the
  790     # current working directory.
  791     if loader is None or import_name == "__main__":
  792         return os.getcwd()
  793 
  794     # For .egg, zipimporter does not have get_filename until Python 2.7.
  795     # Some other loaders might exhibit the same behavior.
  796     if hasattr(loader, "get_filename"):
  797         filepath = loader.get_filename(import_name)
  798     else:
  799         # Fall back to imports.
  800         __import__(import_name)
  801         mod = sys.modules[import_name]
  802         filepath = getattr(mod, "__file__", None)
  803 
  804         # If we don't have a filepath it might be because we are a
  805         # namespace package.  In this case we pick the root path from the
  806         # first module that is contained in our package.
  807         if filepath is None:
  808             raise RuntimeError(
  809                 "No root path can be found for the provided "
  810                 'module "%s".  This can happen because the '
  811                 "module came from an import hook that does "
  812                 "not provide file name information or because "
  813                 "it's a namespace package.  In this case "
  814                 "the root path needs to be explicitly "
  815                 "provided." % import_name
  816             )
  817 
  818     # filepath is import_name.py for a module, or __init__.py for a package.
  819     return os.path.dirname(os.path.abspath(filepath))
  820 
  821 
  822 def _matching_loader_thinks_module_is_package(loader, mod_name):
  823     """Given the loader that loaded a module and the module this function
  824     attempts to figure out if the given module is actually a package.
  825     """
  826     # If the loader can tell us if something is a package, we can
  827     # directly ask the loader.
  828     if hasattr(loader, "is_package"):
  829         return loader.is_package(mod_name)
  830     # importlib's namespace loaders do not have this functionality but
  831     # all the modules it loads are packages, so we can take advantage of
  832     # this information.
  833     elif (
  834         loader.__class__.__module__ == "_frozen_importlib"
  835         and loader.__class__.__name__ == "NamespaceLoader"
  836     ):
  837         return True
  838     # Otherwise we need to fail with an error that explains what went
  839     # wrong.
  840     raise AttributeError(
  841         (
  842             "%s.is_package() method is missing but is required by Flask of "
  843             "PEP 302 import hooks.  If you do not use import hooks and "
  844             "you encounter this error please file a bug against Flask."
  845         )
  846         % loader.__class__.__name__
  847     )
  848 
  849 
  850 def _find_package_path(root_mod_name):
  851     """Find the path where the module's root exists in"""
  852     if sys.version_info >= (3, 4):
  853         import importlib.util
  854 
  855         try:
  856             spec = importlib.util.find_spec(root_mod_name)
  857             if spec is None:
  858                 raise ValueError("not found")
  859         # ImportError: the machinery told us it does not exist
  860         # ValueError:
  861         #    - the module name was invalid
  862         #    - the module name is __main__
  863         #    - *we* raised `ValueError` due to `spec` being `None`
  864         except (ImportError, ValueError):
  865             pass  # handled below
  866         else:
  867             # namespace package
  868             if spec.origin in {"namespace", None}:
  869                 return os.path.dirname(next(iter(spec.submodule_search_locations)))
  870             # a package (with __init__.py)
  871             elif spec.submodule_search_locations:
  872                 return os.path.dirname(os.path.dirname(spec.origin))
  873             # just a normal module
  874             else:
  875                 return os.path.dirname(spec.origin)
  876 
  877     # we were unable to find the `package_path` using PEP 451 loaders
  878     loader = pkgutil.get_loader(root_mod_name)
  879     if loader is None or root_mod_name == "__main__":
  880         # import name is not found, or interactive/main module
  881         return os.getcwd()
  882     else:
  883         # For .egg, zipimporter does not have get_filename until Python 2.7.
  884         if hasattr(loader, "get_filename"):
  885             filename = loader.get_filename(root_mod_name)
  886         elif hasattr(loader, "archive"):
  887             # zipimporter's loader.archive points to the .egg or .zip
  888             # archive filename is dropped in call to dirname below.
  889             filename = loader.archive
  890         else:
  891             # At least one loader is missing both get_filename and archive:
  892             # Google App Engine's HardenedModulesHook
  893             #
  894             # Fall back to imports.
  895             __import__(root_mod_name)
  896             filename = sys.modules[root_mod_name].__file__
  897         package_path = os.path.abspath(os.path.dirname(filename))
  898 
  899         # In case the root module is a package we need to chop of the
  900         # rightmost part.  This needs to go through a helper function
  901         # because of python 3.3 namespace packages.
  902         if _matching_loader_thinks_module_is_package(loader, root_mod_name):
  903             package_path = os.path.dirname(package_path)
  904 
  905     return package_path
  906 
  907 
  908 def find_package(import_name):
  909     """Finds a package and returns the prefix (or None if the package is
  910     not installed) as well as the folder that contains the package or
  911     module as a tuple.  The package path returned is the module that would
  912     have to be added to the pythonpath in order to make it possible to
  913     import the module.  The prefix is the path below which a UNIX like
  914     folder structure exists (lib, share etc.).
  915     """
  916     root_mod_name, _, _ = import_name.partition(".")
  917     package_path = _find_package_path(root_mod_name)
  918     site_parent, site_folder = os.path.split(package_path)
  919     py_prefix = os.path.abspath(sys.prefix)
  920     if package_path.startswith(py_prefix):
  921         return py_prefix, package_path
  922     elif site_folder.lower() == "site-packages":
  923         parent, folder = os.path.split(site_parent)
  924         # Windows like installations
  925         if folder.lower() == "lib":
  926             base_dir = parent
  927         # UNIX like installations
  928         elif os.path.basename(parent).lower() == "lib":
  929             base_dir = os.path.dirname(parent)
  930         else:
  931             base_dir = site_parent
  932         return base_dir, package_path
  933     return None, package_path
  934 
  935 
  936 class locked_cached_property(object):
  937     """A decorator that converts a function into a lazy property.  The
  938     function wrapped is called the first time to retrieve the result
  939     and then that calculated result is used the next time you access
  940     the value.  Works like the one in Werkzeug but has a lock for
  941     thread safety.
  942     """
  943 
  944     def __init__(self, func, name=None, doc=None):
  945         self.__name__ = name or func.__name__
  946         self.__module__ = func.__module__
  947         self.__doc__ = doc or func.__doc__
  948         self.func = func
  949         self.lock = RLock()
  950 
  951     def __get__(self, obj, type=None):
  952         if obj is None:
  953             return self
  954         with self.lock:
  955             value = obj.__dict__.get(self.__name__, _missing)
  956             if value is _missing:
  957                 value = self.func(obj)
  958                 obj.__dict__[self.__name__] = value
  959             return value
  960 
  961 
  962 class _PackageBoundObject(object):
  963     #: The name of the package or module that this app belongs to. Do not
  964     #: change this once it is set by the constructor.
  965     import_name = None
  966 
  967     #: Location of the template files to be added to the template lookup.
  968     #: ``None`` if templates should not be added.
  969     template_folder = None
  970 
  971     #: Absolute path to the package on the filesystem. Used to look up
  972     #: resources contained in the package.
  973     root_path = None
  974 
  975     def __init__(self, import_name, template_folder=None, root_path=None):
  976         self.import_name = import_name
  977         self.template_folder = template_folder
  978 
  979         if root_path is None:
  980             root_path = get_root_path(self.import_name)
  981 
  982         self.root_path = root_path
  983         self._static_folder = None
  984         self._static_url_path = None
  985 
  986         # circular import
  987         from .cli import AppGroup
  988 
  989         #: The Click command group for registration of CLI commands
  990         #: on the application and associated blueprints. These commands
  991         #: are accessible via the :command:`flask` command once the
  992         #: application has been discovered and blueprints registered.
  993         self.cli = AppGroup()
  994 
  995     @property
  996     def static_folder(self):
  997         """The absolute path to the configured static folder."""
  998         if self._static_folder is not None:
  999             return os.path.join(self.root_path, self._static_folder)
 1000 
 1001     @static_folder.setter
 1002     def static_folder(self, value):
 1003         if value is not None:
 1004             value = value.rstrip("/\\")
 1005         self._static_folder = value
 1006 
 1007     @property
 1008     def static_url_path(self):
 1009         """The URL prefix that the static route will be accessible from.
 1010 
 1011         If it was not configured during init, it is derived from
 1012         :attr:`static_folder`.
 1013         """
 1014         if self._static_url_path is not None:
 1015             return self._static_url_path
 1016 
 1017         if self.static_folder is not None:
 1018             basename = os.path.basename(self.static_folder)
 1019             return ("/" + basename).rstrip("/")
 1020 
 1021     @static_url_path.setter
 1022     def static_url_path(self, value):
 1023         if value is not None:
 1024             value = value.rstrip("/")
 1025 
 1026         self._static_url_path = value
 1027 
 1028     @property
 1029     def has_static_folder(self):
 1030         """This is ``True`` if the package bound object's container has a
 1031         folder for static files.
 1032 
 1033         .. versionadded:: 0.5
 1034         """
 1035         return self.static_folder is not None
 1036 
 1037     @locked_cached_property
 1038     def jinja_loader(self):
 1039         """The Jinja loader for this package bound object.
 1040 
 1041         .. versionadded:: 0.5
 1042         """
 1043         if self.template_folder is not None:
 1044             return FileSystemLoader(os.path.join(self.root_path, self.template_folder))
 1045 
 1046     def get_send_file_max_age(self, filename):
 1047         """Provides default cache_timeout for the :func:`send_file` functions.
 1048 
 1049         By default, this function returns ``SEND_FILE_MAX_AGE_DEFAULT`` from
 1050         the configuration of :data:`~flask.current_app`.
 1051 
 1052         Static file functions such as :func:`send_from_directory` use this
 1053         function, and :func:`send_file` calls this function on
 1054         :data:`~flask.current_app` when the given cache_timeout is ``None``. If a
 1055         cache_timeout is given in :func:`send_file`, that timeout is used;
 1056         otherwise, this method is called.
 1057 
 1058         This allows subclasses to change the behavior when sending files based
 1059         on the filename.  For example, to set the cache timeout for .js files
 1060         to 60 seconds::
 1061 
 1062             class MyFlask(flask.Flask):
 1063                 def get_send_file_max_age(self, name):
 1064                     if name.lower().endswith('.js'):
 1065                         return 60
 1066                     return flask.Flask.get_send_file_max_age(self, name)
 1067 
 1068         .. versionadded:: 0.9
 1069         """
 1070         return total_seconds(current_app.send_file_max_age_default)
 1071 
 1072     def send_static_file(self, filename):
 1073         """Function used internally to send static files from the static
 1074         folder to the browser.
 1075 
 1076         .. versionadded:: 0.5
 1077         """
 1078         if not self.has_static_folder:
 1079             raise RuntimeError("No static folder for this object")
 1080         # Ensure get_send_file_max_age is called in all cases.
 1081         # Here, we ensure get_send_file_max_age is called for Blueprints.
 1082         cache_timeout = self.get_send_file_max_age(filename)
 1083         return send_from_directory(
 1084             self.static_folder, filename, cache_timeout=cache_timeout
 1085         )
 1086 
 1087     def open_resource(self, resource, mode="rb"):
 1088         """Opens a resource from the application's resource folder.  To see
 1089         how this works, consider the following folder structure::
 1090 
 1091             /myapplication.py
 1092             /schema.sql
 1093             /static
 1094                 /style.css
 1095             /templates
 1096                 /layout.html
 1097                 /index.html
 1098 
 1099         If you want to open the :file:`schema.sql` file you would do the
 1100         following::
 1101 
 1102             with app.open_resource('schema.sql') as f:
 1103                 contents = f.read()
 1104                 do_something_with(contents)
 1105 
 1106         :param resource: the name of the resource.  To access resources within
 1107                          subfolders use forward slashes as separator.
 1108         :param mode: Open file in this mode. Only reading is supported,
 1109             valid values are "r" (or "rt") and "rb".
 1110         """
 1111         if mode not in {"r", "rt", "rb"}:
 1112             raise ValueError("Resources can only be opened for reading")
 1113 
 1114         return open(os.path.join(self.root_path, resource), mode)
 1115 
 1116 
 1117 def total_seconds(td):
 1118     """Returns the total seconds from a timedelta object.
 1119 
 1120     :param timedelta td: the timedelta to be converted in seconds
 1121 
 1122     :returns: number of seconds
 1123     :rtype: int
 1124     """
 1125     return td.days * 60 * 60 * 24 + td.seconds
 1126 
 1127 
 1128 def is_ip(value):
 1129     """Determine if the given string is an IP address.
 1130 
 1131     Python 2 on Windows doesn't provide ``inet_pton``, so this only
 1132     checks IPv4 addresses in that environment.
 1133 
 1134     :param value: value to check
 1135     :type value: str
 1136 
 1137     :return: True if string is an IP address
 1138     :rtype: bool
 1139     """
 1140     if PY2 and os.name == "nt":
 1141         try:
 1142             socket.inet_aton(value)
 1143             return True
 1144         except socket.error:
 1145             return False
 1146 
 1147     for family in (socket.AF_INET, socket.AF_INET6):
 1148         try:
 1149             socket.inet_pton(family, value)
 1150         except socket.error:
 1151             pass
 1152         else:
 1153             return True
 1154 
 1155     return False