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)
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)
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)
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)
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)
""" 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):
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)
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)
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]
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)
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)