mirror of
https://github.com/onionshare/onionshare.git
synced 2025-06-07 14:23:01 -04:00
bundling required python dependencies, to make it easier on Tails users
This commit is contained in:
parent
18fd65acd7
commit
8ffa569094
224 changed files with 52588 additions and 0 deletions
849
lib/flask/helpers.py
Normal file
849
lib/flask/helpers.py
Normal file
|
@ -0,0 +1,849 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.helpers
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Implements various helpers.
|
||||
|
||||
:copyright: (c) 2011 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pkgutil
|
||||
import posixpath
|
||||
import mimetypes
|
||||
from time import time
|
||||
from zlib import adler32
|
||||
from threading import RLock
|
||||
from werkzeug.routing import BuildError
|
||||
from functools import update_wrapper
|
||||
|
||||
try:
|
||||
from werkzeug.urls import url_quote
|
||||
except ImportError:
|
||||
from urlparse import quote as url_quote
|
||||
|
||||
from werkzeug.datastructures import Headers
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
# this was moved in 0.7
|
||||
try:
|
||||
from werkzeug.wsgi import wrap_file
|
||||
except ImportError:
|
||||
from werkzeug.utils import wrap_file
|
||||
|
||||
from jinja2 import FileSystemLoader
|
||||
|
||||
from .signals import message_flashed
|
||||
from .globals import session, _request_ctx_stack, _app_ctx_stack, \
|
||||
current_app, request
|
||||
from ._compat import string_types, text_type
|
||||
|
||||
|
||||
# sentinel
|
||||
_missing = object()
|
||||
|
||||
|
||||
# what separators does this operating system provide that are not a slash?
|
||||
# this is used by the send_from_directory function to ensure that nobody is
|
||||
# able to access files from outside the filesystem.
|
||||
_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
|
||||
if sep not in (None, '/'))
|
||||
|
||||
|
||||
def _endpoint_from_view_func(view_func):
|
||||
"""Internal helper that returns the default endpoint for a given
|
||||
function. This always is the function name.
|
||||
"""
|
||||
assert view_func is not None, 'expected view func if endpoint ' \
|
||||
'is not provided.'
|
||||
return view_func.__name__
|
||||
|
||||
|
||||
def stream_with_context(generator_or_function):
|
||||
"""Request contexts disappear when the response is started on the server.
|
||||
This is done for efficiency reasons and to make it less likely to encounter
|
||||
memory leaks with badly written WSGI middlewares. The downside is that if
|
||||
you are using streamed responses, the generator cannot access request bound
|
||||
information any more.
|
||||
|
||||
This function however can help you keep the context around for longer::
|
||||
|
||||
from flask import stream_with_context, request, Response
|
||||
|
||||
@app.route('/stream')
|
||||
def streamed_response():
|
||||
@stream_with_context
|
||||
def generate():
|
||||
yield 'Hello '
|
||||
yield request.args['name']
|
||||
yield '!'
|
||||
return Response(generate())
|
||||
|
||||
Alternatively it can also be used around a specific generator::
|
||||
|
||||
from flask import stream_with_context, request, Response
|
||||
|
||||
@app.route('/stream')
|
||||
def streamed_response():
|
||||
def generate():
|
||||
yield 'Hello '
|
||||
yield request.args['name']
|
||||
yield '!'
|
||||
return Response(stream_with_context(generate()))
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
try:
|
||||
gen = iter(generator_or_function)
|
||||
except TypeError:
|
||||
def decorator(*args, **kwargs):
|
||||
gen = generator_or_function()
|
||||
return stream_with_context(gen)
|
||||
return update_wrapper(decorator, generator_or_function)
|
||||
|
||||
def generator():
|
||||
ctx = _request_ctx_stack.top
|
||||
if ctx is None:
|
||||
raise RuntimeError('Attempted to stream with context but '
|
||||
'there was no context in the first place to keep around.')
|
||||
with ctx:
|
||||
# Dummy sentinel. Has to be inside the context block or we're
|
||||
# not actually keeping the context around.
|
||||
yield None
|
||||
|
||||
# The try/finally is here so that if someone passes a WSGI level
|
||||
# iterator in we're still running the cleanup logic. Generators
|
||||
# don't need that because they are closed on their destruction
|
||||
# automatically.
|
||||
try:
|
||||
for item in gen:
|
||||
yield item
|
||||
finally:
|
||||
if hasattr(gen, 'close'):
|
||||
gen.close()
|
||||
|
||||
# The trick is to start the generator. Then the code execution runs until
|
||||
# the first dummy None is yielded at which point the context was already
|
||||
# pushed. This item is discarded. Then when the iteration continues the
|
||||
# real generator is executed.
|
||||
wrapped_g = generator()
|
||||
next(wrapped_g)
|
||||
return wrapped_g
|
||||
|
||||
|
||||
def make_response(*args):
|
||||
"""Sometimes it is necessary to set additional headers in a view. Because
|
||||
views do not have to return response objects but can return a value that
|
||||
is converted into a response object by Flask itself, it becomes tricky to
|
||||
add headers to it. This function can be called instead of using a return
|
||||
and you will get a response object which you can use to attach headers.
|
||||
|
||||
If view looked like this and you want to add a new header::
|
||||
|
||||
def index():
|
||||
return render_template('index.html', foo=42)
|
||||
|
||||
You can now do something like this::
|
||||
|
||||
def index():
|
||||
response = make_response(render_template('index.html', foo=42))
|
||||
response.headers['X-Parachutes'] = 'parachutes are cool'
|
||||
return response
|
||||
|
||||
This function accepts the very same arguments you can return from a
|
||||
view function. This for example creates a response with a 404 error
|
||||
code::
|
||||
|
||||
response = make_response(render_template('not_found.html'), 404)
|
||||
|
||||
The other use case of this function is to force the return value of a
|
||||
view function into a response which is helpful with view
|
||||
decorators::
|
||||
|
||||
response = make_response(view_function())
|
||||
response.headers['X-Parachutes'] = 'parachutes are cool'
|
||||
|
||||
Internally this function does the following things:
|
||||
|
||||
- if no arguments are passed, it creates a new response argument
|
||||
- if one argument is passed, :meth:`flask.Flask.make_response`
|
||||
is invoked with it.
|
||||
- if more than one argument is passed, the arguments are passed
|
||||
to the :meth:`flask.Flask.make_response` function as tuple.
|
||||
|
||||
.. versionadded:: 0.6
|
||||
"""
|
||||
if not args:
|
||||
return current_app.response_class()
|
||||
if len(args) == 1:
|
||||
args = args[0]
|
||||
return current_app.make_response(args)
|
||||
|
||||
|
||||
def url_for(endpoint, **values):
|
||||
"""Generates a URL to the given endpoint with the method provided.
|
||||
|
||||
Variable arguments that are unknown to the target endpoint are appended
|
||||
to the generated URL as query arguments. If the value of a query argument
|
||||
is `None`, the whole pair is skipped. In case blueprints are active
|
||||
you can shortcut references to the same blueprint by prefixing the
|
||||
local endpoint with a dot (``.``).
|
||||
|
||||
This will reference the index function local to the current blueprint::
|
||||
|
||||
url_for('.index')
|
||||
|
||||
For more information, head over to the :ref:`Quickstart <url-building>`.
|
||||
|
||||
To integrate applications, :class:`Flask` has a hook to intercept URL build
|
||||
errors through :attr:`Flask.build_error_handler`. The `url_for` function
|
||||
results in a :exc:`~werkzeug.routing.BuildError` when the current app does
|
||||
not have a URL for the given endpoint and values. When it does, the
|
||||
:data:`~flask.current_app` calls its :attr:`~Flask.build_error_handler` if
|
||||
it is not `None`, which can return a string to use as the result of
|
||||
`url_for` (instead of `url_for`'s default to raise the
|
||||
:exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
|
||||
An example::
|
||||
|
||||
def external_url_handler(error, endpoint, **values):
|
||||
"Looks up an external URL when `url_for` cannot build a URL."
|
||||
# This is an example of hooking the build_error_handler.
|
||||
# Here, lookup_url is some utility function you've built
|
||||
# which looks up the endpoint in some external URL registry.
|
||||
url = lookup_url(endpoint, **values)
|
||||
if url is None:
|
||||
# External lookup did not have a URL.
|
||||
# Re-raise the BuildError, in context of original traceback.
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
if exc_value is error:
|
||||
raise exc_type, exc_value, tb
|
||||
else:
|
||||
raise error
|
||||
# url_for will use this result, instead of raising BuildError.
|
||||
return url
|
||||
|
||||
app.build_error_handler = external_url_handler
|
||||
|
||||
Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
|
||||
`endpoint` and `**values` are the arguments passed into `url_for`. Note
|
||||
that this is for building URLs outside the current application, and not for
|
||||
handling 404 NotFound errors.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
The `_scheme` parameter was added.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
The `_anchor` and `_method` parameters were added.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
Calls :meth:`Flask.handle_build_error` on
|
||||
:exc:`~werkzeug.routing.BuildError`.
|
||||
|
||||
:param endpoint: the endpoint of the URL (name of the function)
|
||||
:param values: the variable arguments of the URL rule
|
||||
:param _external: if set to `True`, an absolute URL is generated. Server
|
||||
address can be changed via `SERVER_NAME` configuration variable which
|
||||
defaults to `localhost`.
|
||||
:param _scheme: a string specifying the desired URL scheme. The `_external`
|
||||
parameter must be set to `True` or a `ValueError` is raised.
|
||||
:param _anchor: if provided this is added as anchor to the URL.
|
||||
:param _method: if provided this explicitly specifies an HTTP method.
|
||||
"""
|
||||
appctx = _app_ctx_stack.top
|
||||
reqctx = _request_ctx_stack.top
|
||||
if appctx is None:
|
||||
raise RuntimeError('Attempted to generate a URL without the '
|
||||
'application context being pushed. This has to be '
|
||||
'executed when application context is available.')
|
||||
|
||||
# If request specific information is available we have some extra
|
||||
# features that support "relative" urls.
|
||||
if reqctx is not None:
|
||||
url_adapter = reqctx.url_adapter
|
||||
blueprint_name = request.blueprint
|
||||
if not reqctx.request._is_old_module:
|
||||
if endpoint[:1] == '.':
|
||||
if blueprint_name is not None:
|
||||
endpoint = blueprint_name + endpoint
|
||||
else:
|
||||
endpoint = endpoint[1:]
|
||||
else:
|
||||
# TODO: get rid of this deprecated functionality in 1.0
|
||||
if '.' not in endpoint:
|
||||
if blueprint_name is not None:
|
||||
endpoint = blueprint_name + '.' + endpoint
|
||||
elif endpoint.startswith('.'):
|
||||
endpoint = endpoint[1:]
|
||||
external = values.pop('_external', False)
|
||||
|
||||
# Otherwise go with the url adapter from the appctx and make
|
||||
# the urls external by default.
|
||||
else:
|
||||
url_adapter = appctx.url_adapter
|
||||
if url_adapter is None:
|
||||
raise RuntimeError('Application was not able to create a URL '
|
||||
'adapter for request independent URL generation. '
|
||||
'You might be able to fix this by setting '
|
||||
'the SERVER_NAME config variable.')
|
||||
external = values.pop('_external', True)
|
||||
|
||||
anchor = values.pop('_anchor', None)
|
||||
method = values.pop('_method', None)
|
||||
scheme = values.pop('_scheme', None)
|
||||
appctx.app.inject_url_defaults(endpoint, values)
|
||||
|
||||
if scheme is not None:
|
||||
if not external:
|
||||
raise ValueError('When specifying _scheme, _external must be True')
|
||||
url_adapter.url_scheme = scheme
|
||||
|
||||
try:
|
||||
rv = url_adapter.build(endpoint, values, method=method,
|
||||
force_external=external)
|
||||
except BuildError as error:
|
||||
# We need to inject the values again so that the app callback can
|
||||
# deal with that sort of stuff.
|
||||
values['_external'] = external
|
||||
values['_anchor'] = anchor
|
||||
values['_method'] = method
|
||||
return appctx.app.handle_url_build_error(error, endpoint, values)
|
||||
|
||||
if anchor is not None:
|
||||
rv += '#' + url_quote(anchor)
|
||||
return rv
|
||||
|
||||
|
||||
def get_template_attribute(template_name, attribute):
|
||||
"""Loads a macro (or variable) a template exports. This can be used to
|
||||
invoke a macro from within Python code. If you for example have a
|
||||
template named `_cider.html` with the following contents:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% macro hello(name) %}Hello {{ name }}!{% endmacro %}
|
||||
|
||||
You can access this from Python code like this::
|
||||
|
||||
hello = get_template_attribute('_cider.html', 'hello')
|
||||
return hello('World')
|
||||
|
||||
.. versionadded:: 0.2
|
||||
|
||||
:param template_name: the name of the template
|
||||
:param attribute: the name of the variable of macro to access
|
||||
"""
|
||||
return getattr(current_app.jinja_env.get_template(template_name).module,
|
||||
attribute)
|
||||
|
||||
|
||||
def flash(message, category='message'):
|
||||
"""Flashes a message to the next request. In order to remove the
|
||||
flashed message from the session and to display it to the user,
|
||||
the template has to call :func:`get_flashed_messages`.
|
||||
|
||||
.. versionchanged:: 0.3
|
||||
`category` parameter added.
|
||||
|
||||
:param message: the message to be flashed.
|
||||
:param category: the category for the message. The following values
|
||||
are recommended: ``'message'`` for any kind of message,
|
||||
``'error'`` for errors, ``'info'`` for information
|
||||
messages and ``'warning'`` for warnings. However any
|
||||
kind of string can be used as category.
|
||||
"""
|
||||
# Original implementation:
|
||||
#
|
||||
# session.setdefault('_flashes', []).append((category, message))
|
||||
#
|
||||
# This assumed that changes made to mutable structures in the session are
|
||||
# are always in sync with the sess on object, which is not true for session
|
||||
# implementations that use external storage for keeping their keys/values.
|
||||
flashes = session.get('_flashes', [])
|
||||
flashes.append((category, message))
|
||||
session['_flashes'] = flashes
|
||||
message_flashed.send(current_app._get_current_object(),
|
||||
message=message, category=category)
|
||||
|
||||
|
||||
def get_flashed_messages(with_categories=False, category_filter=[]):
|
||||
"""Pulls all flashed messages from the session and returns them.
|
||||
Further calls in the same request to the function will return
|
||||
the same messages. By default just the messages are returned,
|
||||
but when `with_categories` is set to `True`, the return value will
|
||||
be a list of tuples in the form ``(category, message)`` instead.
|
||||
|
||||
Filter the flashed messages to one or more categories by providing those
|
||||
categories in `category_filter`. This allows rendering categories in
|
||||
separate html blocks. The `with_categories` and `category_filter`
|
||||
arguments are distinct:
|
||||
|
||||
* `with_categories` controls whether categories are returned with message
|
||||
text (`True` gives a tuple, where `False` gives just the message text).
|
||||
* `category_filter` filters the messages down to only those matching the
|
||||
provided categories.
|
||||
|
||||
See :ref:`message-flashing-pattern` for examples.
|
||||
|
||||
.. versionchanged:: 0.3
|
||||
`with_categories` parameter added.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
`category_filter` parameter added.
|
||||
|
||||
:param with_categories: set to `True` to also receive categories.
|
||||
:param category_filter: whitelist of categories to limit return values
|
||||
"""
|
||||
flashes = _request_ctx_stack.top.flashes
|
||||
if flashes is None:
|
||||
_request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \
|
||||
if '_flashes' in session else []
|
||||
if category_filter:
|
||||
flashes = list(filter(lambda f: f[0] in category_filter, flashes))
|
||||
if not with_categories:
|
||||
return [x[1] for x in flashes]
|
||||
return flashes
|
||||
|
||||
|
||||
def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
||||
attachment_filename=None, add_etags=True,
|
||||
cache_timeout=None, conditional=False):
|
||||
"""Sends the contents of a file to the client. This will use the
|
||||
most efficient method available and configured. By default it will
|
||||
try to use the WSGI server's file_wrapper support. Alternatively
|
||||
you can set the application's :attr:`~Flask.use_x_sendfile` attribute
|
||||
to ``True`` to directly emit an `X-Sendfile` header. This however
|
||||
requires support of the underlying webserver for `X-Sendfile`.
|
||||
|
||||
By default it will try to guess the mimetype for you, but you can
|
||||
also explicitly provide one. For extra security you probably want
|
||||
to send certain files as attachment (HTML for instance). The mimetype
|
||||
guessing requires a `filename` or an `attachment_filename` to be
|
||||
provided.
|
||||
|
||||
Please never pass filenames to this function from user sources without
|
||||
checking them first. Something like this is usually sufficient to
|
||||
avoid security problems::
|
||||
|
||||
if '..' in filename or filename.startswith('/'):
|
||||
abort(404)
|
||||
|
||||
.. versionadded:: 0.2
|
||||
|
||||
.. versionadded:: 0.5
|
||||
The `add_etags`, `cache_timeout` and `conditional` parameters were
|
||||
added. The default behavior is now to attach etags.
|
||||
|
||||
.. versionchanged:: 0.7
|
||||
mimetype guessing and etag support for file objects was
|
||||
deprecated because it was unreliable. Pass a filename if you are
|
||||
able to, otherwise attach an etag yourself. This functionality
|
||||
will be removed in Flask 1.0
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
cache_timeout pulls its default from application config, when None.
|
||||
|
||||
:param filename_or_fp: the filename of the file to send. This is
|
||||
relative to the :attr:`~Flask.root_path` if a
|
||||
relative path is specified.
|
||||
Alternatively a file object might be provided
|
||||
in which case `X-Sendfile` might not work and
|
||||
fall back to the traditional method. Make sure
|
||||
that the file pointer is positioned at the start
|
||||
of data to send before calling :func:`send_file`.
|
||||
:param mimetype: the mimetype of the file if provided, otherwise
|
||||
auto detection happens.
|
||||
:param as_attachment: set to `True` if you want to send this file with
|
||||
a ``Content-Disposition: attachment`` header.
|
||||
:param attachment_filename: the filename for the attachment if it
|
||||
differs from the file's filename.
|
||||
:param add_etags: set to `False` to disable attaching of etags.
|
||||
:param conditional: set to `True` to enable conditional responses.
|
||||
|
||||
:param cache_timeout: the timeout in seconds for the headers. When `None`
|
||||
(default), this value is set by
|
||||
:meth:`~Flask.get_send_file_max_age` of
|
||||
:data:`~flask.current_app`.
|
||||
"""
|
||||
mtime = None
|
||||
if isinstance(filename_or_fp, string_types):
|
||||
filename = filename_or_fp
|
||||
file = None
|
||||
else:
|
||||
from warnings import warn
|
||||
file = filename_or_fp
|
||||
filename = getattr(file, 'name', None)
|
||||
|
||||
# XXX: this behavior is now deprecated because it was unreliable.
|
||||
# removed in Flask 1.0
|
||||
if not attachment_filename and not mimetype \
|
||||
and isinstance(filename, string_types):
|
||||
warn(DeprecationWarning('The filename support for file objects '
|
||||
'passed to send_file is now deprecated. Pass an '
|
||||
'attach_filename if you want mimetypes to be guessed.'),
|
||||
stacklevel=2)
|
||||
if add_etags:
|
||||
warn(DeprecationWarning('In future flask releases etags will no '
|
||||
'longer be generated for file objects passed to the send_file '
|
||||
'function because this behavior was unreliable. Pass '
|
||||
'filenames instead if possible, otherwise attach an etag '
|
||||
'yourself based on another value'), stacklevel=2)
|
||||
|
||||
if filename is not None:
|
||||
if not os.path.isabs(filename):
|
||||
filename = os.path.join(current_app.root_path, filename)
|
||||
if mimetype is None and (filename or attachment_filename):
|
||||
mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
|
||||
if mimetype is None:
|
||||
mimetype = 'application/octet-stream'
|
||||
|
||||
headers = Headers()
|
||||
if as_attachment:
|
||||
if attachment_filename is None:
|
||||
if filename is None:
|
||||
raise TypeError('filename unavailable, required for '
|
||||
'sending as attachment')
|
||||
attachment_filename = os.path.basename(filename)
|
||||
headers.add('Content-Disposition', 'attachment',
|
||||
filename=attachment_filename)
|
||||
|
||||
if current_app.use_x_sendfile and filename:
|
||||
if file is not None:
|
||||
file.close()
|
||||
headers['X-Sendfile'] = filename
|
||||
headers['Content-Length'] = os.path.getsize(filename)
|
||||
data = None
|
||||
else:
|
||||
if file is None:
|
||||
file = open(filename, 'rb')
|
||||
mtime = os.path.getmtime(filename)
|
||||
headers['Content-Length'] = os.path.getsize(filename)
|
||||
data = wrap_file(request.environ, file)
|
||||
|
||||
rv = current_app.response_class(data, mimetype=mimetype, headers=headers,
|
||||
direct_passthrough=True)
|
||||
|
||||
# if we know the file modification date, we can store it as the
|
||||
# the time of the last modification.
|
||||
if mtime is not None:
|
||||
rv.last_modified = int(mtime)
|
||||
|
||||
rv.cache_control.public = True
|
||||
if cache_timeout is None:
|
||||
cache_timeout = current_app.get_send_file_max_age(filename)
|
||||
if cache_timeout is not None:
|
||||
rv.cache_control.max_age = cache_timeout
|
||||
rv.expires = int(time() + cache_timeout)
|
||||
|
||||
if add_etags and filename is not None:
|
||||
rv.set_etag('flask-%s-%s-%s' % (
|
||||
os.path.getmtime(filename),
|
||||
os.path.getsize(filename),
|
||||
adler32(
|
||||
filename.encode('utf-8') if isinstance(filename, text_type)
|
||||
else filename
|
||||
) & 0xffffffff
|
||||
))
|
||||
if conditional:
|
||||
rv = rv.make_conditional(request)
|
||||
# make sure we don't send x-sendfile for servers that
|
||||
# ignore the 304 status code for x-sendfile.
|
||||
if rv.status_code == 304:
|
||||
rv.headers.pop('x-sendfile', None)
|
||||
return rv
|
||||
|
||||
|
||||
def safe_join(directory, filename):
|
||||
"""Safely join `directory` and `filename`.
|
||||
|
||||
Example usage::
|
||||
|
||||
@app.route('/wiki/<path:filename>')
|
||||
def wiki_page(filename):
|
||||
filename = safe_join(app.config['WIKI_FOLDER'], filename)
|
||||
with open(filename, 'rb') as fd:
|
||||
content = fd.read() # Read and process the file content...
|
||||
|
||||
:param directory: the base directory.
|
||||
:param filename: the untrusted filename relative to that directory.
|
||||
:raises: :class:`~werkzeug.exceptions.NotFound` if the resulting path
|
||||
would fall out of `directory`.
|
||||
"""
|
||||
filename = posixpath.normpath(filename)
|
||||
for sep in _os_alt_seps:
|
||||
if sep in filename:
|
||||
raise NotFound()
|
||||
if os.path.isabs(filename) or \
|
||||
filename == '..' or \
|
||||
filename.startswith('../'):
|
||||
raise NotFound()
|
||||
return os.path.join(directory, filename)
|
||||
|
||||
|
||||
def send_from_directory(directory, filename, **options):
|
||||
"""Send a file from a given directory with :func:`send_file`. This
|
||||
is a secure way to quickly expose static files from an upload folder
|
||||
or something similar.
|
||||
|
||||
Example usage::
|
||||
|
||||
@app.route('/uploads/<path:filename>')
|
||||
def download_file(filename):
|
||||
return send_from_directory(app.config['UPLOAD_FOLDER'],
|
||||
filename, as_attachment=True)
|
||||
|
||||
.. admonition:: Sending files and Performance
|
||||
|
||||
It is strongly recommended to activate either `X-Sendfile` support in
|
||||
your webserver or (if no authentication happens) to tell the webserver
|
||||
to serve files for the given path on its own without calling into the
|
||||
web application for improved performance.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
|
||||
:param directory: the directory where all the files are stored.
|
||||
:param filename: the filename relative to that directory to
|
||||
download.
|
||||
:param options: optional keyword arguments that are directly
|
||||
forwarded to :func:`send_file`.
|
||||
"""
|
||||
filename = safe_join(directory, filename)
|
||||
if not os.path.isfile(filename):
|
||||
raise NotFound()
|
||||
options.setdefault('conditional', True)
|
||||
return send_file(filename, **options)
|
||||
|
||||
|
||||
def get_root_path(import_name):
|
||||
"""Returns the path to a package or cwd if that cannot be found. This
|
||||
returns the path of a package or the folder that contains a module.
|
||||
|
||||
Not to be confused with the package path returned by :func:`find_package`.
|
||||
"""
|
||||
# Module already imported and has a file attribute. Use that first.
|
||||
mod = sys.modules.get(import_name)
|
||||
if mod is not None and hasattr(mod, '__file__'):
|
||||
return os.path.dirname(os.path.abspath(mod.__file__))
|
||||
|
||||
# Next attempt: check the loader.
|
||||
loader = pkgutil.get_loader(import_name)
|
||||
|
||||
# Loader does not exist or we're referring to an unloaded main module
|
||||
# or a main module without path (interactive sessions), go with the
|
||||
# current working directory.
|
||||
if loader is None or import_name == '__main__':
|
||||
return os.getcwd()
|
||||
|
||||
# For .egg, zipimporter does not have get_filename until Python 2.7.
|
||||
# Some other loaders might exhibit the same behavior.
|
||||
if hasattr(loader, 'get_filename'):
|
||||
filepath = loader.get_filename(import_name)
|
||||
else:
|
||||
# Fall back to imports.
|
||||
__import__(import_name)
|
||||
filepath = sys.modules[import_name].__file__
|
||||
|
||||
# filepath is import_name.py for a module, or __init__.py for a package.
|
||||
return os.path.dirname(os.path.abspath(filepath))
|
||||
|
||||
|
||||
def find_package(import_name):
|
||||
"""Finds a package and returns the prefix (or None if the package is
|
||||
not installed) as well as the folder that contains the package or
|
||||
module as a tuple. The package path returned is the module that would
|
||||
have to be added to the pythonpath in order to make it possible to
|
||||
import the module. The prefix is the path below which a UNIX like
|
||||
folder structure exists (lib, share etc.).
|
||||
"""
|
||||
root_mod_name = import_name.split('.')[0]
|
||||
loader = pkgutil.get_loader(root_mod_name)
|
||||
if loader is None or import_name == '__main__':
|
||||
# import name is not found, or interactive/main module
|
||||
package_path = os.getcwd()
|
||||
else:
|
||||
# For .egg, zipimporter does not have get_filename until Python 2.7.
|
||||
if hasattr(loader, 'get_filename'):
|
||||
filename = loader.get_filename(root_mod_name)
|
||||
elif hasattr(loader, 'archive'):
|
||||
# zipimporter's loader.archive points to the .egg or .zip
|
||||
# archive filename is dropped in call to dirname below.
|
||||
filename = loader.archive
|
||||
else:
|
||||
# At least one loader is missing both get_filename and archive:
|
||||
# Google App Engine's HardenedModulesHook
|
||||
#
|
||||
# Fall back to imports.
|
||||
__import__(import_name)
|
||||
filename = sys.modules[import_name].__file__
|
||||
package_path = os.path.abspath(os.path.dirname(filename))
|
||||
# package_path ends with __init__.py for a package
|
||||
if loader.is_package(root_mod_name):
|
||||
package_path = os.path.dirname(package_path)
|
||||
|
||||
site_parent, site_folder = os.path.split(package_path)
|
||||
py_prefix = os.path.abspath(sys.prefix)
|
||||
if package_path.startswith(py_prefix):
|
||||
return py_prefix, package_path
|
||||
elif site_folder.lower() == 'site-packages':
|
||||
parent, folder = os.path.split(site_parent)
|
||||
# Windows like installations
|
||||
if folder.lower() == 'lib':
|
||||
base_dir = parent
|
||||
# UNIX like installations
|
||||
elif os.path.basename(parent).lower() == 'lib':
|
||||
base_dir = os.path.dirname(parent)
|
||||
else:
|
||||
base_dir = site_parent
|
||||
return base_dir, package_path
|
||||
return None, package_path
|
||||
|
||||
|
||||
class locked_cached_property(object):
|
||||
"""A decorator that converts a function into a lazy property. The
|
||||
function wrapped is called the first time to retrieve the result
|
||||
and then that calculated result is used the next time you access
|
||||
the value. Works like the one in Werkzeug but has a lock for
|
||||
thread safety.
|
||||
"""
|
||||
|
||||
def __init__(self, func, name=None, doc=None):
|
||||
self.__name__ = name or func.__name__
|
||||
self.__module__ = func.__module__
|
||||
self.__doc__ = doc or func.__doc__
|
||||
self.func = func
|
||||
self.lock = RLock()
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
if obj is None:
|
||||
return self
|
||||
with self.lock:
|
||||
value = obj.__dict__.get(self.__name__, _missing)
|
||||
if value is _missing:
|
||||
value = self.func(obj)
|
||||
obj.__dict__[self.__name__] = value
|
||||
return value
|
||||
|
||||
|
||||
class _PackageBoundObject(object):
|
||||
|
||||
def __init__(self, import_name, template_folder=None):
|
||||
#: The name of the package or module. Do not change this once
|
||||
#: it was set by the constructor.
|
||||
self.import_name = import_name
|
||||
|
||||
#: location of the templates. `None` if templates should not be
|
||||
#: exposed.
|
||||
self.template_folder = template_folder
|
||||
|
||||
#: Where is the app root located?
|
||||
self.root_path = get_root_path(self.import_name)
|
||||
|
||||
self._static_folder = None
|
||||
self._static_url_path = None
|
||||
|
||||
def _get_static_folder(self):
|
||||
if self._static_folder is not None:
|
||||
return os.path.join(self.root_path, self._static_folder)
|
||||
def _set_static_folder(self, value):
|
||||
self._static_folder = value
|
||||
static_folder = property(_get_static_folder, _set_static_folder)
|
||||
del _get_static_folder, _set_static_folder
|
||||
|
||||
def _get_static_url_path(self):
|
||||
if self._static_url_path is None:
|
||||
if self.static_folder is None:
|
||||
return None
|
||||
return '/' + os.path.basename(self.static_folder)
|
||||
return self._static_url_path
|
||||
def _set_static_url_path(self, value):
|
||||
self._static_url_path = value
|
||||
static_url_path = property(_get_static_url_path, _set_static_url_path)
|
||||
del _get_static_url_path, _set_static_url_path
|
||||
|
||||
@property
|
||||
def has_static_folder(self):
|
||||
"""This is `True` if the package bound object's container has a
|
||||
folder named ``'static'``.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
return self.static_folder is not None
|
||||
|
||||
@locked_cached_property
|
||||
def jinja_loader(self):
|
||||
"""The Jinja loader for this package bound object.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
if self.template_folder is not None:
|
||||
return FileSystemLoader(os.path.join(self.root_path,
|
||||
self.template_folder))
|
||||
|
||||
def get_send_file_max_age(self, filename):
|
||||
"""Provides default cache_timeout for the :func:`send_file` functions.
|
||||
|
||||
By default, this function returns ``SEND_FILE_MAX_AGE_DEFAULT`` from
|
||||
the configuration of :data:`~flask.current_app`.
|
||||
|
||||
Static file functions such as :func:`send_from_directory` use this
|
||||
function, and :func:`send_file` calls this function on
|
||||
:data:`~flask.current_app` when the given cache_timeout is `None`. If a
|
||||
cache_timeout is given in :func:`send_file`, that timeout is used;
|
||||
otherwise, this method is called.
|
||||
|
||||
This allows subclasses to change the behavior when sending files based
|
||||
on the filename. For example, to set the cache timeout for .js files
|
||||
to 60 seconds::
|
||||
|
||||
class MyFlask(flask.Flask):
|
||||
def get_send_file_max_age(self, name):
|
||||
if name.lower().endswith('.js'):
|
||||
return 60
|
||||
return flask.Flask.get_send_file_max_age(self, name)
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
return current_app.config['SEND_FILE_MAX_AGE_DEFAULT']
|
||||
|
||||
def send_static_file(self, filename):
|
||||
"""Function used internally to send static files from the static
|
||||
folder to the browser.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
if not self.has_static_folder:
|
||||
raise RuntimeError('No static folder for this object')
|
||||
# Ensure get_send_file_max_age is called in all cases.
|
||||
# Here, we ensure get_send_file_max_age is called for Blueprints.
|
||||
cache_timeout = self.get_send_file_max_age(filename)
|
||||
return send_from_directory(self.static_folder, filename,
|
||||
cache_timeout=cache_timeout)
|
||||
|
||||
def open_resource(self, resource, mode='rb'):
|
||||
"""Opens a resource from the application's resource folder. To see
|
||||
how this works, consider the following folder structure::
|
||||
|
||||
/myapplication.py
|
||||
/schema.sql
|
||||
/static
|
||||
/style.css
|
||||
/templates
|
||||
/layout.html
|
||||
/index.html
|
||||
|
||||
If you want to open the `schema.sql` file you would do the
|
||||
following::
|
||||
|
||||
with app.open_resource('schema.sql') as f:
|
||||
contents = f.read()
|
||||
do_something_with(contents)
|
||||
|
||||
:param resource: the name of the resource. To access resources within
|
||||
subfolders use forward slashes as separator.
|
||||
:param mode: resource file opening mode, default is 'rb'.
|
||||
"""
|
||||
if mode not in ('r', 'rb'):
|
||||
raise ValueError('Resources can only be opened for reading')
|
||||
return open(os.path.join(self.root_path, resource), mode)
|
Loading…
Add table
Add a link
Reference in a new issue