abilian.web.util — Abilian 0.1 documentation

# Source code for abilian.web.util

    """
    A few utility functions.

    See https://docs.djangoproject.com/en/dev/topics/http/shortcuts/ for more ideas
    of stuff to implement.
    """
    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

    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')

    def get_object_or_404(cls, *args):
      """
      Shorthand similar to Django's `get_object_or_404`.
      """

      return cls.query.filter(*args).first_or_404()

    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)

    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