Example #1
0
def pdf(filename=None, content_type="application/pdf"):
    def entangle(func):
        def decorated(func, *args, **kw):
            def kwpop(default, *names):
                for name in names:
                    if name in kw:
                        return kw.pop(name)
                return default

            # get the output from the decorated function
            output = func(*args, **kw)

            dst = StringIO.StringIO()
            result = pisa.CreatePDF(
                StringIO.StringIO(output),
                dst
                )

            # print cherrypy.url("index.html")
            if not result.err:
                cherrypy.response.headers["Content-Type"] = content_type
                if filename:
                    cherrypy.response.headers["Content-Disposition"] = "attachment; filename=" + filename
                output = dst.getvalue()

            return output
        return decorated
    return weak_signature_decorator(entangle)
Example #2
0
def pdf(filename=None, content_type="application/pdf"):
    def entangle(func):
        def decorated(func, *args, **kw):
            def kwpop(default, *names):
                for name in names:
                    if name in kw:
                        return kw.pop(name)
                return default

            # get the output from the decorated function
            output = func(*args, **kw)

            dst = StringIO.StringIO()
            result = pisa.CreatePDF(StringIO.StringIO(output), dst)

            # print cherrypy.url("index.html")
            if not result.err:
                cherrypy.response.headers["Content-Type"] = content_type
                if filename:
                    cherrypy.response.headers[
                        "Content-Disposition"] = "attachment; filename=" + filename
                output = dst.getvalue()

            return output

        return decorated

    return weak_signature_decorator(entangle)
Example #3
0
def to_pdf(filename=None, content_type="application/pdf"):
    def entangle(func):
        def decorated(func, *args, **kw):
            output = func(*args, **kw)
            dst = StringIO.StringIO()
            result = pisa.CreatePDF(
                StringIO.StringIO(output),
                dst
                )
            if not result.err:
                cherrypy.response.headers["Content-Type"] = content_type
                if filename:
                    cherrypy.response.headers["Content-Disposition"] = "attachment; filename=" + filename
                output = dst.getvalue()
            return output
        return decorated
    return weak_signature_decorator(entangle)
Example #4
0
def to_pdf(filename=None, content_type="application/pdf"):
    def entangle(func):
        def decorated(func, *args, **kw):
            output = func(*args, **kw)
            dst = StringIO.StringIO()
            result = pisa.CreatePDF(StringIO.StringIO(output), dst)
            if not result.err:
                cherrypy.response.headers["Content-Type"] = content_type
                if filename:
                    cherrypy.response.headers[
                        "Content-Disposition"] = "attachment; filename=" + filename
                output = dst.getvalue()
            return output

        return decorated

    return weak_signature_decorator(entangle)
Example #5
0
def paginate(var_name,
             default_order='',
             default_reversed=None,
             limit=10,
             max_limit=0,
             allow_limit_override=None,
             max_pages=5,
             max_sort=1000,
             dynamic_limit=None):
    """The famous TurboGears paginate decorator.

    @param var_name: The variable name that the paginate decorator will try
    to control. This key must be present in the dictionnary returned from your
    controller in order for the paginate decorator to be able to handle it.
    @type var_name: string

    @param default_order: The column name(s) that will be used to orde
    pagination results. Due to the way pagination is implemented specifying a
    default_order will override any result ordering performed in the controller.
    @type default_order: string or a list of strings. Any string starting with
    "-" (minus sign) indicates a reverse order for that field/column.

    @param default_reversed: Deprecated, use default_order with minus sign.
    @type default_reversed: Boolean

    @param limit: The hard-coded limit that the paginate decorator will impose
    on the number of "var_name" to display at the same time. This value can be
    overridden by the use of the dynamic_limit keyword argument.
    @type limit: integer

    @param max_limit: The maximum number to which the imposed limit can be
    increased using the "var_name"_tgp_limit keyword argument in the URL.
    If this is set to 0, no dynamic change at all will be allowed;
    if it is set to None, any change will be allowed.
    @type max_limit: int

    @param allow_limit_override: Deprecated, use max_limit.
    @type allow_limit_override: Boolean

    @param max_pages: Used to generate the tg.paginate.pages variable. If the
    page count is larger than max_pages, tg.paginate.pages will only contain
    the page numbers surrounding the current page at a distance of max_pages/2.
    A zero value means that all pages will be shown, no matter how much.
    @type max_pages: integer

    @param max_sort: The maximum number of records that will be sorted in
    memory if the data cannot be sorted using SQL. If set to 0, sorting in
    memory will never be performed; if set to None, no limit will be imposed.
    @type max_sort: integer

    @param dynamic_limit: If specified, this parameter must be the name
    of a key present in the dictionary returned by your decorated
    controller. The value found for this key will be used as the limit
    for our pagination and will override the other settings, the hard-coded
    one declared in the decorator itself AND the URL parameter one.
    This enables the programmer to store a limit settings inside the
    application preferences and then let the user manage it.
    @type dynamic_limit: string

    """

    if default_reversed is not None:
        warnings.warn(
            "default_reversed is deprecated."
            " Use default_order='-field' to indicate"
            " default reversed order, or"
            " default_order=['field1', '-field2, 'field3']"
            " for multiple fields.", DeprecationWarning, 2)
    if allow_limit_override is not None:
        warnings.warn(
            "allow_limit_override is deprecated."
            " Use max_limit to specify an upper bound for limit.",
            DeprecationWarning, 2)

    def entangle(func):

        get = turbogears.config.get

        def decorated(func, *args, **kw):
            def kwpop(name, default=None):
                return kw.pop(var_name + '_tgp_' + name,
                              kw.pop('tg_paginate_' + name, default))

            page = kwpop('no')
            if page is None:
                page = 1
            elif page == 'last':
                page = None
            else:
                try:
                    page = int(page)
                    if page < 1:
                        raise ValueError
                except (TypeError, ValueError):
                    page = 1
                    if get('paginate.redirect_on_out_of_range'):
                        cherrypy.request.params[var_name + '_tgp_no'] = page
                        redirect(cherrypy.request.path_info,
                                 cherrypy.request.params)

            try:
                limit_ = int(kwpop('limit'))
                if max_limit is not None and not (allow_limit_override
                                                  and max_limit == 0):
                    if max_limit <= 0:
                        raise ValueError
                    limit_ = min(limit_, max_limit)
            except (TypeError, ValueError):
                limit_ = limit
            order = kwpop('order')
            ordering = kwpop('ordering')

            log.debug("paginate params: page=%s, limit=%s, order=%s", page,
                      limit_, order)

            # get the output from the decorated function
            output = func(*args, **kw)
            if not isinstance(output, dict):
                return output

            try:
                var_data = output[var_name]
            except KeyError:
                raise KeyError("paginate: var_name"
                               " (%s) not found in output dict" % var_name)
            if not hasattr(var_data, '__getitem__') and callable(var_data):
                # e.g. SQLAlchemy query class
                var_data = var_data()
                if not hasattr(var_data, '__getitem__'):
                    raise TypeError('Paginate variable is not a sequence')

            if dynamic_limit:
                try:
                    dyn_limit = output[dynamic_limit]
                except KeyError:
                    raise KeyError("paginate: dynamic_limit"
                                   " (%s) not found in output dict" %
                                   dynamic_limit)
                limit_ = dyn_limit

            if ordering:
                ordering = str(ordering).split(',')
            else:
                ordering = default_order or []
                if isinstance(ordering, basestring):
                    # adapt old style default_order to new style
                    if default_reversed:
                        ordering = "-" + ordering
                    ordering = [ordering]
                elif default_reversed:
                    raise ValueError(
                        "paginate: default_reversed (deprecated)"
                        " only allowed when default_order is a basestring")

            if order:
                order = str(order)
                log.debug('paginate: ordering was %s, sort is %s', ordering,
                          order)
                sort_ordering(ordering, order)
            log.debug('paginate: ordering is %s', ordering)

            try:
                row_count = len(var_data)
            except TypeError:
                try:  # SQL query
                    row_count = var_data.count() or 0
                except AttributeError:  # other iterator
                    var_data = list(var_data)
                    row_count = len(var_data)

            if ordering:
                var_data = sort_data(
                    var_data, ordering, max_sort is None
                    or 0 < row_count <= max_sort)

            # If limit is zero then return all our rows
            if not limit_:
                limit_ = row_count or 1

            page_count = int(ceil(float(row_count) / limit_))

            if page is None:
                page = max(page_count, 1)
                if get('paginate.redirect_on_last_page'):
                    cherrypy.request.params[var_name + '_tgp_no'] = page
                    redirect(cherrypy.request.path_info,
                             cherrypy.request.params)
            elif page > page_count:
                page = max(page_count, 1)
                if get('paginate.redirect_on_out_of_range'):
                    cherrypy.request.params[var_name + '_tgp_no'] = page
                    redirect(cherrypy.request.path_info,
                             cherrypy.request.params)

            offset = (page - 1) * limit_

            pages_to_show = _select_pages_to_show(page, page_count, max_pages)

            # remove pagination parameters from request
            input_values = variable_encode(cherrypy.request.params.copy())
            input_values.pop('self', None)
            for input_key in input_values.keys():
                if (input_key.startswith(var_name + '_tgp_')
                        or input_key.startswith('tg_paginate_')):
                    del input_values[input_key]

            paginate_instance = Paginate(current_page=page,
                                         limit=limit_,
                                         pages=pages_to_show,
                                         page_count=page_count,
                                         input_values=input_values,
                                         order=order,
                                         ordering=ordering,
                                         row_count=row_count,
                                         var_name=var_name)

            cherrypy.request.paginate = paginate_instance
            if not hasattr(cherrypy.request, 'paginates'):
                cherrypy.request.paginates = dict()
            cherrypy.request.paginates[var_name] = paginate_instance

            # we replace the var with the sliced one
            endpoint = offset + limit_
            log.debug("paginate: slicing data between %d and %d", offset,
                      endpoint)

            global _simulate_offset
            if _simulate_offset is None:
                _simulate_offset = get('paginate.simulate_offset', None)
                if _simulate_offset is None:
                    _simulate_offset = False
                    so_db = get('sqlobject.dburi', 'NOMATCH:').split(':', 1)[0]
                    sa_db = get('sqlalchemy.dburi', 'NOMATCH:').split(':',
                                                                      1)[0]
                    if so_db in _so_no_offset or sa_db in _sa_no_offset:
                        _simulate_offset = True
                        log.warning(
                            "paginate: simulating OFFSET,"
                            " paginate may be slow"
                            " (disable with paginate.simulate_offset=False)")

            if _simulate_offset:
                var_data = iter(var_data[:endpoint])
                # skip over the number of records specified by offset
                for i in xrange(offset):
                    var_data.next()
                # return the records that remain
                output[var_name] = list(var_data)
            else:
                try:
                    output[var_name] = var_data[offset:endpoint]
                except TypeError:
                    for i in xrange(offset):
                        var_data.next()
                    output[var_name] = [
                        var_data.next() for i in xrange(offset, endpoint)
                    ]

            return output

        if not get('tg.strict_parameters', False):
            # add hint that paginate parameters shall be left intact
            args = set()
            for arg in 'no', 'limit', 'order', 'ordering':
                args.add(var_name + '_tgp_' + arg)
                args.add('tg_paginate_' + arg)
            add_tg_args(func, args)
        return decorated

    return weak_signature_decorator(entangle)
Example #6
0
    """
    def entangle(fn):
        def require(func, self, *args, **kwargs):
            try:
                errors = []
                if predicate is None or \
                   predicate.eval_with_object(current, errors):
                    return fn(self, *args, **kwargs)
            except IdentityException, e:
                errors = [str(e)]
            raise IdentityFailure(errors)

        fn._require = predicate
        return require

    return weak_signature_decorator(entangle)


def _check_method(obj, fn, predicate):
    def _wrapper(*args, **kw):
        errors = []
        if predicate.eval_with_object(current, errors):
            return fn(*args, **kw)
        else:
            raise IdentityFailure(errors)

    _wrapper.exposed = True
    return _wrapper


class SecureResource(object):
Example #7
0
def paginate(var_name, default_order='', default_reversed=None, limit=10,
            max_limit=0, allow_limit_override=None, max_pages=5,
            max_sort=1000, dynamic_limit=None):
    """The famous TurboGears paginate decorator.

    @param var_name: The variable name that the paginate decorator will try
    to control. This key must be present in the dictionnary returned from your
    controller in order for the paginate decorator to be able to handle it.
    @type var_name: string

    @param default_order: The column name(s) that will be used to orde
    pagination results. Due to the way pagination is implemented specifying a
    default_order will override any result ordering performed in the controller.
    @type default_order: string or a list of strings. Any string starting with
    "-" (minus sign) indicates a reverse order for that field/column.

    @param default_reversed: Deprecated, use default_order with minus sign.
    @type default_reversed: Boolean

    @param limit: The hard-coded limit that the paginate decorator will impose
    on the number of "var_name" to display at the same time. This value can be
    overridden by the use of the dynamic_limit keyword argument.
    @type limit: integer

    @param max_limit: The maximum number to which the imposed limit can be
    increased using the "var_name"_tgp_limit keyword argument in the URL.
    If this is set to 0, no dynamic change at all will be allowed;
    if it is set to None, any change will be allowed.
    @type max_limit: int

    @param allow_limit_override: Deprecated, use max_limit.
    @type allow_limit_override: Boolean

    @param max_pages: Used to generate the tg.paginate.pages variable. If the
    page count is larger than max_pages, tg.paginate.pages will only contain
    the page numbers surrounding the current page at a distance of max_pages/2.
    A zero value means that all pages will be shown, no matter how much.
    @type max_pages: integer

    @param max_sort: The maximum number of records that will be sorted in
    memory if the data cannot be sorted using SQL. If set to 0, sorting in
    memory will never be performed; if set to None, no limit will be imposed.
    @type max_sort: integer

    @param dynamic_limit: If specified, this parameter must be the name
    of a key present in the dictionary returned by your decorated
    controller. The value found for this key will be used as the limit
    for our pagination and will override the other settings, the hard-coded
    one declared in the decorator itself AND the URL parameter one.
    This enables the programmer to store a limit settings inside the
    application preferences and then let the user manage it.
    @type dynamic_limit: string

    """

    if default_reversed is not None:
        warnings.warn("default_reversed is deprecated."
            " Use default_order='-field' to indicate"
            " default reversed order, or"
            " default_order=['field1', '-field2, 'field3']"
            " for multiple fields.", DeprecationWarning, 2)
    if allow_limit_override is not None:
        warnings.warn("allow_limit_override is deprecated."
            " Use max_limit to specify an upper bound for limit.",
            DeprecationWarning, 2)

    def entangle(func):

        get = turbogears.config.get

        def decorated(func, *args, **kw):

            def kwpop(name, default=None):
                return kw.pop(var_name + '_tgp_' + name,
                    kw.pop('tg_paginate_' + name, default))

            page = kwpop('no')
            if page is None:
                page = 1
            elif page == 'last':
                page = None
            else:
                try:
                    page = int(page)
                    if page < 1:
                        raise ValueError
                except (TypeError, ValueError):
                    page = 1
                    if get('paginate.redirect_on_out_of_range'):
                        cherrypy.request.params[var_name + '_tgp_no'] = page
                        redirect(cherrypy.request.path_info, cherrypy.request.params)

            try:
                limit_ = int(kwpop('limit'))
                if max_limit is not None and not (
                        allow_limit_override and max_limit == 0):
                    if max_limit <= 0:
                        raise ValueError
                    limit_ = min(limit_, max_limit)
            except (TypeError, ValueError):
                limit_ = limit
            order = kwpop('order')
            ordering = kwpop('ordering')

            log.debug("paginate params: page=%s, limit=%s, order=%s",
                page, limit_, order)

            # get the output from the decorated function
            output = func(*args, **kw)
            if not isinstance(output, dict):
                return output

            try:
                var_data = output[var_name]
            except KeyError:
                raise KeyError("paginate: var_name"
                    " (%s) not found in output dict" % var_name)
            if not hasattr(var_data, '__getitem__') and callable(var_data):
                # e.g. SQLAlchemy query class
                var_data = var_data()
                if not hasattr(var_data, '__getitem__'):
                    raise TypeError('Paginate variable is not a sequence')

            if dynamic_limit:
                try:
                    dyn_limit = output[dynamic_limit]
                except KeyError:
                    raise KeyError("paginate: dynamic_limit"
                        " (%s) not found in output dict" % dynamic_limit)
                limit_ = dyn_limit

            if ordering:
                ordering = str(ordering).split(',')
            else:
                ordering = default_order or []
                if isinstance(ordering, basestring):
                    # adapt old style default_order to new style
                    if default_reversed:
                        ordering = "-" + ordering
                    ordering = [ordering]
                elif default_reversed:
                    raise ValueError("paginate: default_reversed (deprecated)"
                        " only allowed when default_order is a basestring")

            if order:
                order = str(order)
                log.debug('paginate: ordering was %s, sort is %s',
                    ordering, order)
                sort_ordering(ordering, order)
            log.debug('paginate: ordering is %s', ordering)

            try:
                row_count = len(var_data)
            except TypeError:
                try: # SQL query
                    row_count = var_data.count() or 0
                except AttributeError: # other iterator
                    var_data = list(var_data)
                    row_count = len(var_data)

            if ordering:
                var_data = sort_data(var_data, ordering,
                    max_sort is None or 0 < row_count <= max_sort)

            # If limit is zero then return all our rows
            if not limit_:
                limit_ = row_count or 1

            page_count = int(ceil(float(row_count)/limit_))

            if page is None:
                page = max(page_count, 1)
                if get('paginate.redirect_on_last_page'):
                    cherrypy.request.params[var_name + '_tgp_no'] = page
                    redirect(cherrypy.request.path_info, cherrypy.request.params)
            elif page > page_count:
                page = max(page_count, 1)
                if get('paginate.redirect_on_out_of_range'):
                    cherrypy.request.params[var_name + '_tgp_no'] = page
                    redirect(cherrypy.request.path_info, cherrypy.request.params)

            offset = (page-1) * limit_

            pages_to_show = _select_pages_to_show(page, page_count, max_pages)

            # remove pagination parameters from request
            input_values =  variable_encode(cherrypy.request.params.copy())
            input_values.pop('self', None)
            for input_key in input_values.keys():
                if (input_key.startswith(var_name + '_tgp_') or
                        input_key.startswith('tg_paginate_')):
                    del input_values[input_key]

            paginate_instance = Paginate(
                current_page=page,
                limit=limit_,
                pages=pages_to_show,
                page_count=page_count,
                input_values=input_values,
                order=order,
                ordering=ordering,
                row_count=row_count,
                var_name=var_name)

            cherrypy.request.paginate = paginate_instance
            if not hasattr(cherrypy.request, 'paginates'):
                cherrypy.request.paginates = dict()
            cherrypy.request.paginates[var_name] = paginate_instance

            # we replace the var with the sliced one
            endpoint = offset + limit_
            log.debug("paginate: slicing data between %d and %d",
                offset, endpoint)

            global _simulate_offset
            if _simulate_offset is None:
                _simulate_offset = get('paginate.simulate_offset', None)
                if _simulate_offset is None:
                    _simulate_offset = False
                    so_db = get('sqlobject.dburi', 'NOMATCH:').split(':', 1)[0]
                    sa_db = get('sqlalchemy.dburi', 'NOMATCH:').split(':', 1)[0]
                    if so_db in _so_no_offset or sa_db in _sa_no_offset:
                        _simulate_offset = True
                        log.warning("paginate: simulating OFFSET,"
                            " paginate may be slow"
                            " (disable with paginate.simulate_offset=False)")

            if _simulate_offset:
                var_data = iter(var_data[:endpoint])
                # skip over the number of records specified by offset
                for i in xrange(offset):
                    var_data.next()
                # return the records that remain
                output[var_name] = list(var_data)
            else:
                try:
                    output[var_name] = var_data[offset:endpoint]
                except TypeError:
                    for i in xrange(offset):
                        var_data.next()
                    output[var_name] = [var_data.next()
                        for i in xrange(offset, endpoint)]

            return output

        if not get('tg.strict_parameters', False):
            # add hint that paginate parameters shall be left intact
            args = set()
            for arg in 'no', 'limit', 'order', 'ordering':
                args.add(var_name + '_tgp_' + arg)
                args.add('tg_paginate_' + arg)
            add_tg_args(func, args)
        return decorated

    return weak_signature_decorator(entangle)
Example #8
0
def expose(template=None, validators=None, allow_json=None, html=None,
           format=None, content_type=None, inputform=None, fragment=False,
           as_format="default", mapping=None, accept_format=None):
    """Exposes a method to the web.

    By putting the expose decorator on a method, you tell TurboGears that
    the method should be accessible via URL traversal. Additionally, expose
    handles the output processing (turning a dictionary into finished
    output) and is also responsible for ensuring that the request is
    wrapped in a database transaction.

    You can apply multiple expose decorators to a method, if
    you'd like to support multiple output formats. The decorator that's
    listed first in your code without as_format or accept_format is
    the default that is chosen when no format is specifically asked for.
    Any other expose calls that are missing as_format and accept_format
    will have as_format implicitly set to the whatever comes before
    the ":" in the template name (or the whole template name if there
    is no ":". For example, <code>expose("json")</code>, if it's not
    the default expose, will have as_format set to "json".

    When as_format is set, passing the same value in the tg_format
    parameter in a request will choose the options for that expose
    decorator. Similarly, accept_format will watch for matching
    Accept headers. You can also use both. expose("json", as_format="json",
    accept_format="application/json") will choose JSON output for either
    case: tg_format=json as a parameter or Accept: application/json as a
    request header.

    Passing allow_json=True to an expose decorator
    is equivalent to adding the decorator just mentioned.

    Each expose decorator has its own set of options, and each one
    can choose a different template or even template engine (you can
    use Kid for HTML output and Cheetah for plain text, for example).
    See the other expose parameters below to learn about the options
    you can pass to the template engine.

    Take a look at the
    <a href="tests/test_expose-source.html">test_expose.py</a> suite
    for more examples.

    @param template "templateengine:dotted.reference" reference along the
            Python path for the template and the template engine. For
            example, "kid:foo.bar" will have Kid render the bar template in
            the foo package.
    @keyparam format format for the template engine to output (if the
            template engine can render different formats. Kid, for example,
            can render "html", "xml" or "xhtml")
    @keyparam content_type sets the content-type http header
    @keyparam allow_json allow the function to be exposed as json
    @keyparam fragment for template engines (like Kid) that generate
            DOCTYPE declarations and the like, this is a signal to
            just generate the immediate template fragment. Use this
            if you're building up a page from multiple templates or
            going to put something onto a page with .innerHTML.
    @keyparam mapping mapping with options that are sent to the template
            engine
    @keyparam as_format designates which value of tg_format will choose
            this expose.
    @keyparam accept_format which value of an Accept: header will
            choose this expose.
    @keyparam html deprecated in favor of template
    @keyparam validators deprecated. Maps argument names to validator
            applied to that arg
    @keyparam inputform deprecated. A form object that generates the
            input to this method

    """
    if html:
        template = html
    if not template:
        template = format
    if format == "json" or (format is None and template is None):
        template = "json"
        allow_json = True
    if content_type is None:
        content_type = config.get("tg.content_type", None)

    if config.get("tg.session.automatic_lock", None):
        cherrypy.session.acquire_lock()

    def entangle(func):
        log.debug("Exposing %s", func)
        log.debug("template: %s, format: %s, allow_json: %s, "
            "content-type: %s", template, format, allow_json, content_type)
        if not getattr(func, "exposed", False):
            def expose(func, *args, **kw):
                accept = request.headers.get('Accept', "").lower()
                accept = tg_util.simplify_http_accept_header(accept)
                if not hasattr(func, "_expose"):
                    _build_rules(func)
                if hasattr(request, "in_transaction"):
                    output = func._expose(func, accept, func._allow_json,
                                *args, **kw)
                else:
                    request.in_transaction = True
                    output = database.run_with_transaction(
                            func._expose, func, accept, func._allow_json,
                            *args, **kw)
                return output
            func.exposed = True
            func._ruleinfo = []
            allow_json_from_config = config.get("tg.allow_json", False)
            func._allow_json = allow_json_from_config or template == 'json'
        else:
            expose = lambda func, *args, **kw: func(*args, **kw)

        func._ruleinfo.insert(0, dict(as_format=as_format,
            accept_format=accept_format, template=template,
            rulefunc = lambda _func, accept, allow_json, *args, **kw:
                _execute_func(_func, template, format, content_type,
                    mapping, fragment, args, kw)))

        if allow_json:
            func._allow_json = True

        if inputform or validators:
            import warnings
            warnings.warn(
                "Use a separate decorator validate() rather than passing "
                "arguments validators and/or inputform to decorator "
                "expose().",
                DeprecationWarning, 2)
            func = validate(form=inputform, validators=validators)(func)

        return expose
    return weak_signature_decorator(entangle)
Example #9
0
                        kw.update(validators.to_python(value, state))
                    except Invalid, e:
                        errors = e.unpack_errors()
                        request.validation_exception = e
            request.validation_errors = errors
            request.input_values = kw.copy()
            request.validation_state = state

            if errors:
                kw = errorhandling.dispatch_failsafe(failsafe_schema,
                                            failsafe_values, errors, func, kw)
            args, kw = tg_util.from_kw(func, args, kw)
            return errorhandling.run_with_errors(errors, func, *args, **kw)

        return validate
    return weak_signature_decorator(entangle)


class CustomDispatch(functions.GenericFunction):

    def combine(self, cases):
        strict = [strategy.ordered_signatures, strategy.safe_methods]
        cases = strategy.separate_qualifiers(
            cases,
            primary = strict,
        )
        primary = strategy.method_chain(cases.get('primary', []))
        if type(primary) != types.FunctionType:
            for i in primary:
                for y in i:
                    return y[1]
Example #10
0
def expose(template=None,
           validators=None,
           allow_json=None,
           html=None,
           format=None,
           content_type=None,
           inputform=None,
           fragment=False,
           as_format="default",
           mapping=None,
           accept_format=None,
           exclude_from_memory_profiling=False):
    """Exposes a method to the web.

    By putting the expose decorator on a method, you tell TurboGears that
    the method should be accessible via URL traversal. Additionally, expose
    handles the output processing (turning a dictionary into finished
    output) and is also responsible for ensuring that the request is
    wrapped in a database transaction.

    You can apply multiple expose decorators to a method, if
    you'd like to support multiple output formats. The decorator that's
    listed first in your code without as_format or accept_format is
    the default that is chosen when no format is specifically asked for.
    Any other expose calls that are missing as_format and accept_format
    will have as_format implicitly set to the whatever comes before
    the ":" in the template name (or the whole template name if there
    is no ":". For example, <code>expose("json")</code>, if it's not
    the default expose, will have as_format set to "json".

    When as_format is set, passing the same value in the tg_format
    parameter in a request will choose the options for that expose
    decorator. Similarly, accept_format will watch for matching
    Accept headers. You can also use both. expose("json", as_format="json",
    accept_format="application/json") will choose JSON output for either
    case: tg_format=json as a parameter or Accept: application/json as a
    request header.

    Passing allow_json=True to an expose decorator
    is equivalent to adding the decorator just mentioned.

    Each expose decorator has its own set of options, and each one
    can choose a different template or even template engine (you can
    use Kid for HTML output and Cheetah for plain text, for example).
    See the other expose parameters below to learn about the options
    you can pass to the template engine.

    Take a look at the
    <a href="tests/test_expose-source.html">test_expose.py</a> suite
    for more examples.

    @param template "templateengine:dotted.reference" reference along the
            Python path for the template and the template engine. For
            example, "kid:foo.bar" will have Kid render the bar template in
            the foo package.
    @keyparam format format for the template engine to output (if the
            template engine can render different formats. Kid, for example,
            can render "html", "xml" or "xhtml")
    @keyparam content_type sets the content-type http header
    @keyparam allow_json allow the function to be exposed as json
    @keyparam fragment for template engines (like Kid) that generate
            DOCTYPE declarations and the like, this is a signal to
            just generate the immediate template fragment. Use this
            if you're building up a page from multiple templates or
            going to put something onto a page with .innerHTML.
    @keyparam mapping mapping with options that are sent to the template
            engine
    @keyparam as_format designates which value of tg_format will choose
            this expose.
    @keyparam accept_format which value of an Accept: header will
            choose this expose.
    @keyparam html deprecated in favor of template
    @keyparam validators deprecated. Maps argument names to validator
            applied to that arg
    @keyparam inputform deprecated. A form object that generates the
            input to this method
    @keyparam exclude_from_memory_profiling allows to exclude individual end points from memory profiling. Can be
            used for performance or in case profiling generates errors
    """
    if html:
        template = html
    if not template:
        template = format
    if format == "json" or (format is None and template is None):
        template = "json"
        allow_json = True
    if content_type is None:
        content_type = config.get("tg.content_type", None)

    if config.get("tg.session.automatic_lock", None):
        cherrypy.session.acquire_lock()

    def entangle(func):
        log.debug("Exposing %s", func)
        log.debug(
            "template: %s, format: %s, allow_json: %s, "
            "content-type: %s", template, format, allow_json, content_type)
        if not getattr(func, "exposed", False):

            def expose(func, *args, **kw):
                accept = request.headers.get('Accept', "").lower()
                accept = tg_util.simplify_http_accept_header(accept)
                if not hasattr(func, "_expose"):
                    _build_rules(func)
                if hasattr(request, "in_transaction"):
                    output = func._expose(func, accept, func._allow_json,
                                          *args, **kw)
                else:
                    request.in_transaction = True
                    output = profile_expose_method(
                        _run_with_transaction, accept, args, func, kw,
                        exclude_from_memory_profiling)
                return output

            func.exposed = True
            func._ruleinfo = []
            allow_json_from_config = config.get("tg.allow_json", False)
            func._allow_json = allow_json_from_config or template == 'json'
        else:
            expose = lambda func, *args, **kw: func(*args, **kw)

        func._ruleinfo.insert(
            0,
            dict(as_format=as_format,
                 accept_format=accept_format,
                 template=template,
                 rulefunc=lambda _func, accept, allow_json, *args, **kw:
                 _execute_func(_func, template, format, content_type, mapping,
                               fragment, args, kw)))

        if allow_json:
            func._allow_json = True

        if inputform or validators:
            import warnings
            warnings.warn(
                "Use a separate decorator validate() rather than passing "
                "arguments validators and/or inputform to decorator "
                "expose().", DeprecationWarning, 2)
            func = validate(form=inputform, validators=validators)(func)

        return expose

    return weak_signature_decorator(entangle)
Example #11
0
def paginate(var_name, default_order='', default_reversed=False, limit=10,
            allow_limit_override=False, max_pages=5):
    def entangle(func):
        def decorated(func, *args, **kw):
            page = int(kw.pop('tg_paginate_no', 1))
            limit_ = int(kw.pop('tg_paginate_limit', limit))
            order = kw.pop('tg_paginate_order', None)
            ordering = kw.pop('tg_paginate_ordering', {})

            # Convert ordering str to a dict.
            if ordering:
                ordering = convert_ordering(ordering)

            if not allow_limit_override:
                limit_ = limit

            log.debug("Pagination params: page=%s, limit=%s, order=%s "
                      "", page, limit_, order)
            
            # get the output from the decorated function    
            output = func(*args, **kw)
            if not isinstance(output, dict):
                return output
            try:
                var_data = output[var_name]
            except KeyError:
                raise "Didn't get expected variable"
            
            if order and not default_order:
                raise "If you want to enable ordering you need " \
                      "to provide a default_order" 
            elif default_order and not ordering:
                ordering = {default_order:[0, not default_reversed]}
            elif ordering and order:
                sort_ordering(ordering, order)
            log.debug('ordering %s' % ordering)

            row_count = 0
            if (SelectResults and isinstance(var_data, SelectResults)) or \
               (SASelectResults and isinstance(var_data, SASelectResults)):
                row_count = var_data.count()
                if ordering:
                    # Build order_by list.
                    order_cols = range(len(ordering))
                    for (colname, order_opts) in ordering.items():
                        col = sql_get_column(colname, var_data)
                        if not col:
                            raise StandardError, "The order column (%s) doesn't exist" % colname
                        order_by_expr = sql_order_col(col, order_opts[1])
                        order_cols[order_opts[0]] = order_by_expr
                    # May need to address potential of ordering already
                    # existing in var_data.
                    # SO and SA differ on this method name.
                    if hasattr(var_data, 'orderBy'):
                        var_data = var_data.orderBy(order_cols)
                    else:
                        var_data = var_data.order_by(order_cols)
            elif isinstance(var_data, list):
                row_count = len(var_data)
            else:
                raise 'Variable is not a list or SelectResults'

            offset = (page-1) * limit_
            page_count = int(ceil(float(row_count)/limit_))

            # if it's possible display every page
            if page_count <= max_pages:
                pages_to_show = range(1,page_count+1)
            else:
                pages_to_show = _select_pages_to_show(page_count=page_count,
                                              current_page=page,
                                              max_pages=max_pages)
                
            # which one should we use? cherrypy.request.input_values or kw?
            #input_values = cherrypy.request.input_values.copy()
            ##input_values = kw.copy()
            input_values =  variable_encode(cherrypy.request.params.copy())
            input_values.pop('self', None)
            for input_key in input_values.keys():
                if input_key.startswith('tg_paginate'):
                    del input_values[input_key]

            cherrypy.request.paginate = Paginate(current_page=page,
                                             limit=limit_, 
                                             pages=pages_to_show, 
                                             page_count=page_count, 
                                             input_values=input_values, 
                                             order=order,
                                             ordering=ordering,
                                             row_count=row_count)
                                             
            # we replace the var with the sliced one
            endpoint = offset + limit_
            log.debug("slicing data between %d and %d", offset, endpoint)
            output[var_name] = var_data[offset:endpoint]

            return output
        return decorated
    return weak_signature_decorator(entangle)