def describe_handler(handler): """Return a serialisable description of a handler. :type handler: :class:`OperationsHandler` or :class:`AnonymousOperationsHandler` instance or subclass. """ # Want the class, not an instance. if isinstance(handler, BaseHandler): handler = type(handler) path = generate_doc(handler).resource_uri_template path = "" if path is None else path resource_uri = getattr(handler, "resource_uri", lambda: ()) view_name, uri_params, uri_kw = merge(resource_uri(), (None, (), {})) assert uri_kw == {}, ( "Resource URI specifications with keyword parameters are not yet " "supported: handler=%r; view_name=%r" % (handler, view_name)) return { "actions": list(describe_actions(handler)), "doc": getdoc(handler), "name": handler.__name__, "params": tuple(uri_params), "path": path, }
def test_resource_uri_in_docs_matches_handlers_idea_of_resource_uri(self): # Sigh. Piston asks handlers for resource_uri information, but also # makes use of Django's URL patterns to figure out resource_uri # templates for the documentation. Here we check that they match up. formatter = string.Formatter() def gen_handlers(resource): if resource.anonymous is not None: yield resource.anonymous if resource.handler is not None: yield resource.handler handlers = chain.from_iterable( map(gen_handlers, find_api_resources(urlconf)) ) mismatches = [] for handler in map(type, handlers): if hasattr(handler, "resource_uri"): resource_uri_params = handler.resource_uri()[1] resource_uri_template = generate_doc( handler ).resource_uri_template fields_expected = tuple(resource_uri_params) fields_observed = tuple( fname for _, fname, _, _ in formatter.parse( resource_uri_template ) if fname is not None ) if fields_observed != fields_expected: mismatches.append( (handler, fields_expected, fields_observed) ) if len(mismatches) != 0: messages = ( "{handler.__module__}.{handler.__name__} has mismatched " "fields:\n expected: {expected}\n observed: {observed}" "".format( handler=handler, expected=" ".join(expected), observed=" ".join(observed), ) for handler, expected, observed in mismatches ) messages = chain( messages, [ "Amend the URL patterns for these handlers/resources so that " "the observed fields match what is expected." ], ) self.fail("\n--\n".join(messages))
def generate_api_docs(resources): """Generate ReST documentation objects for the ReST API. Yields Piston Documentation objects describing the given resources. This also ensures that handlers define 'resource_uri' methods. This is easily forgotten and essential in order to generate proper documentation. :return: Generates :class:`piston.doc.HandlerDocumentation` instances. """ sentinel = object() resource_key = (lambda resource: resource.handler.__class__.__name__) for resource in sorted(resources, key=resource_key): handler = type(resource.handler) if getattr(handler, "resource_uri", sentinel) is sentinel: raise AssertionError("Missing resource_uri in %s" % handler.__name__) yield generate_doc(handler)