"""
A few utility functions.
See https://docs.djangoproject.com/en/dev/topics/http/shortcuts/ for more ideas
of stuff to implement.
"""
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import logging
import sys
try:
import ipdb as pdb
except ImportError:
import pdb
from werkzeug.routing import BuildError
from flask import (
current_app,
session,
url_for as flask_url_for,
stream_with_context,
)
from flask.helpers import send_from_directory
[docs]def url_for(obj, **kw):
"""
Polymorphic variant of Flask's `url_for` function.
Behaves like the original function when the first argument is a string.
When it's an object, it
"""
if isinstance(obj, basestring):
return flask_url_for(obj, **kw)
try:
return current_app.default_view.url_for(obj, **kw)
except KeyError:
if hasattr(obj, "_url"):
return obj._url
elif hasattr(obj, "url"):
return obj.url
raise BuildError(repr(obj), kw, 'GET')
[docs]def get_object_or_404(cls, *args):
"""
Shorthand similar to Django's `get_object_or_404`.
"""
return cls.query.filter(*args).first_or_404()
[docs]def send_file_from_directory(filename, directory, app=None):
"""
Helper to add static rules, like in `abilian.app`.app
Example use::
app.add_url_rule(
app.static_url_path + '/abilian/<path:filename>',
endpoint='abilian_static',
view_func=partial(send_file_from_directory,
directory='/path/to/static/files/dir'))
"""
if app is None:
app = current_app
cache_timeout = app.get_send_file_max_age(filename)
return send_from_directory(directory, filename,
cache_timeout=cache_timeout)
[docs]def capture_stream_errors(logger, msg):
"""
Decorator that capture and log errors during streamed response.
Decorated function is automatically decorated with
:func:<`Flask.stream_with_context`>.
@param logger: a logger name or logger instance
@param msg: message to log
"""
if isinstance(logger, basestring):
logger = logging.getLogger(logger)
def decorator(fun):
@stream_with_context
def wrapper(*args, **kwargs):
# this is for developpers convenience. The debugger middleware doesn't
# work when using streamed responses.
should_pdb = current_app.debug and session.get('pdb_streamed_responses')
try:
generator = fun(*args, **kwargs)
for chunk in generator:
yield chunk
except Exception:
# Anonymous "except" would also capture GeneratorExit that is not a
# subclass of Exception. GeneratorExit happens when user stop download
# during generation: it's not an error that should be logged
type_, value, tb = sys.exc_info()
if tb.tb_next is not None:
# error has happened inside decorated function, remove us from top
# stack: better readability in logs, accurate label in sentry
tb = tb.tb_next
logger.error(msg, exc_info=(type_, value, tb))
if should_pdb:
pdb.post_mortem(tb)
raise
return wrapper
return decorator