Coder Social home page Coder Social logo

cornice's Introduction

cornice's People

Contributors

almet avatar amarandon avatar amotl avatar billrest avatar bowlofeggs avatar circlingthesun avatar czekan avatar dairiki avatar delijati avatar ergo avatar flupke avatar gabisurita avatar georgevreilly avatar j-carl avatar joesteeve avatar leplatrem avatar mamalos avatar natim avatar okin avatar rafrombrc avatar rfk avatar squidsoup avatar tarekziade avatar thruflo avatar tisdall avatar tomster avatar tseaver avatar vincentfretin avatar vmaksymiv avatar witsch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cornice's Issues

Change the way documentation is generated

Currently, we're creating rst nodes to create the documentaiton. This means that we have code that's not easy to read and not easy to write. The benefit of this is that we're able to produce valid rst nods and thus that we can generate the documentation in different formats (pdf, html, latex, etc.).

However, we had been thinking with tarek about changing this in favor of a simple templating system, ala jinja.

handle 406 / Accept

# case 1 : static
@service.post(accept=('text/plain', 'application/json'))
def _service(request):
    ... if accept is in headers and not listed => 406...

# case 2: dynamic
def _get_accept(request):
     """ docstring USED by the doc"""
    ...
    return False, accepted

def _get_accept2(request):
     """ docstring USED by the doc"""
    request.validated['res'] = result
    return True, None

@service.post(accept=_get_accept)
def _service(request):
    ... the decorator calls _get_accept, and returns a 406  if False
    ... get accept can prepare something to be used by the main function to return a response (response.validated['xxx'] = xxx)
    request.response = request.validated['res']

Unable to define ACL on Resource view

You're unable to specify an acl on a @view decorator for a resource. Here is an example:

@resource(path='/api/users/{username}')
class UserResource(object):
    def __init__(self, request):
        self.request = request

    def auth_user(self, request):
        # not called
        return True

    @view(acl=auth_user)
    def get(self):
        # expect to have acl already called; not true

This 'acl' actually gets attached when the resource is instantiated (wrapped) in cornice/resource.py:65. However, it never gets called.

Errors in documentation

the Full Tutorial

  1. request.validated section
from webob import HTTPUnauthorized

doesn't work with the pip installed webob package and does not comply with the example which explicitly uses :

raise exc.HTTPUnauthorized()

Should be replaced with:

from webob import exc
  1. In users management
    The_USERS variable is defined as a list, and later used as a dictionnary :
_USERS[user['name']] = user['token']

replace _USERS = [] with _USERS = {} seems to make it, but I haven't dig enough to know if it actually should be a dict or a list :-)

Cannot add cornice to Pyramid configurator without installing at least 1 service

I have a platform for services that hosts Pyramid & Cornice. A dynamic configuration may optionally register REST services written using Cornice. However, if no Cornice services are registered, I get a KeyError in a Pyramid tween registered by Cornice.

Here's a sample program that reproduces the problem.

from pyramid.config import Configurator
from pyramid.response import Response
from wsgiref.simple_server import make_server

def hello(request):
    return Response('Hello, world!')

if __name__ == '__main__':
    # Create a Pyramid application that installs
    # cornice but creates no cornice services.
    application = Configurator()
    application.include('cornice')

    # Register some normal Pyramid application.
    application.add_view(hello, route_name='hello')
    application.add_route(name='hello', pattern='/hello')

    # Do not install Cornice services.

    # Launch the application.
    server = make_server('', 8000, application.make_wsgi_app())
    server.serve_forever()

You can launch it using:

virtualenv python-env
python-env/Scripts/activate
pip install pyramid
pip install cornice
python cornice-bug.py

Then, open your browser and access:

http://localhost:8000/hello

and you get an HTTP 500 error. The console shows:

KeyError: 'cornice_services'

triggered in cornice/pyramidhook.py, line 81:

 service = request.registry['cornice_services'].get(pattern)

The problem seems to be that the cornice_tween that processes the handler's response expects the request.registry to contain a 'cornice_services' key. However, if register_service_views() is never called (via the config.add_cornice_service() directive), this key is never set.

Can you make the tween use registry.get('cornice_services',{}) and skip the tween if the key is missing?

Thanks!

Can't build doc with sphinxcontrib.httpdomain

I want to use with sphinxcontrib.httpdomain

hello = Service(name='hello', path='/hello',description=desc)

@hello.get()
def get_info(request):
    """
    .. http:get:: /api/v1/version/{id:int}
       Retrieve a single Version

       :query id: A Version id.

       ** Example response **

But, I can't build.

sphinx-build -b html -d _build/doctrees   . _build/html
Running Sphinx v1.1.3
loading pickled environment... not yet created
building [html]: targets for 1 source files that are out of date
updating environment: 1 added, 0 changed, 0 removed
self.env <cornice.ext.sphinxext.Env object at 0x9cc26ec>                                                                       

Exception occurred:
  File "/usr/local/lib/python2.7/dist-packages/sphinxcontrib/httpdomain.py", line 189, in add_target_and_index
    self.env.domaindata['http'][self.method][sig] = (self.env.docname, '')
KeyError: 'http'
The full traceback has been saved in /tmp/sphinx-err-fV9Q8W.log, if you want to report the issue to the developers.
Please also report this if it was a user error, so that a better error message can be provided next time.
Either send bugs to the mailing list at <http://groups.google.com/group/sphinx-dev/>,
or report them in the tracker at <http://bitbucket.org/birkenfeld/sphinx/issues/>. Thanks!

In sphinxcontrib/httpdomain.py

  def add_target_and_index(self, name_cls, sig, signode):
        signode['ids'].append(http_resource_anchor(*name_cls[1:]))
        self.env.domaindata['http'][self.method][sig] = (self.env.docname, '')

In cornice/ext/sphinxext.py

class Env(object):
    temp_data = {}
    docname = ''
    domaindata = dict()


def rst2node(data):
    """Converts a reStructuredText into its node
    """
    if not data:
        return
    parser = docutils.parsers.rst.Parser()
    document = docutils.utils.new_document('<>')
    document.settings = docutils.frontend.OptionParser().get_default_values()
    document.settings.tab_width = 4
    document.settings.pep_references = False
    document.settings.rfc_references = False
    document.settings.env = Env()

I don't know both of sphinx,cornice yet. What is problem? :-)

add class-level filters and validators

It would be great to be able to write this:

@resource(path='/something')
class MyService(Service):
    def __init__(self, request):
        self.request = request

    @view(validators=['one', 'two'] 
    def get(self):
         ...

    def one(self):
         ...

    def two(self):
         ...

Allow validators to set the HTTP status code

Currently, validators have to work like (untested, fictive example code):

def user_validator(request):
    try:
        request.validated['user'] = get_user(request.matchdict['user'])
    except KeyError:
        request.errors.add('querystring', 'user', 'User field required.')
        # -> 400, bad request
    except MyNotFound:
        raise HTTPNotFound('User {} not found.'.format(user))
        # -> 404, not found

To keep it consistent, I’d like to be able to attach a status code to errors so I could write:

request.errors.add('querystring',
                   'user',
                   'User {} not found.'.format(user),
                   status_code=404)

Body could be default location, using Colander schemas

Omitting location kwarg in Colander SchemaNode fails in Cornice.

Like this :

class ModelDefinition(MappingSchema):
    title = SchemaNode(String())

@model_definition.put(schema=ModelDefinition)
def create_model_definition(request):
   ...

raises :

  File "/home/mle/Code/daybed/env/local/lib/python2.7/site-packages/cornice-0.8-py2.7.egg/cornice/schemas.py", line 24, in get_attributes
    return filter(_filter, self._attributes)
  File "/home/mle/Code/daybed/env/local/lib/python2.7/site-packages/cornice-0.8-py2.7.egg/cornice/schemas.py", line 21, in _filter
    return (attr.location in to_list(location) and
AttributeError: 'SchemaNode' object has no attribute 'location'

body location (or querystring) could either be used by default, or raise a more explicit error at instanciation if not any is specified.

provide a structured output for the validation

Right now the validators must return the status and a message, we want something more structurized:

status, message, details

where details is a list of errors the validator found. each error is a dict containing those keys:

  • domain: the domain of the error "local" or "global" (local to the request, global to the service)
  • reason: a string describing the reason of the failure ("required", etc)
  • message: a strng detailing the problem
  • locationType : where the faulty parameter is to be found in the request ("header", "body", "path", "method")
  • location: The actual parameter name

The response will be a json object containing all this information.

Document how to test cornice applications

We're using webtest to test the behavior of cornice applications, but we're not explaning how we do that in the documentation. That would be useful to do so for newcomers.

sphinxext returns empty apidocs

I've tried various ways the get the sphinx-extension working, but nothing seems working.
In the following I can reproduce all the steps I've taken:

  • install cornice in a 2.7-virtualenv,
  • generate sphinx-docs with the sphinx-quickstarter,
  • add the cornice-directive in conf.py and
  • use this directive in a document.

the result is an empty block where I expect the autogenerated API

wouter@midgard: /opt/dev/sandbox-cornice > bin/sphinx-quickstart
Welcome to the Sphinx 1.1.3 quickstart utility.

Please enter values for the following settings (just press Enter to
accept a default value, if one is given in brackets).

Enter the root path for documentation.

Root path for the documentation [.]: docs

You have two options for placing the build directory for Sphinx output.
Either, you use a directory "_build" within the root path, or you separate
"source" and "build" directories within the root path.

Separate source and build directories (y/N) [n]: n

Inside the root directory, two more directories will be created; "_templates"
for custom HTML templates and "_static" for custom stylesheets and other static
files. You can enter another prefix (such as ".") to replace the underscore.

Name prefix for templates and static dir [_]:

The project name will occur in several places in the built documentation.

Project name: Demo
Author name(s): Wouter

Sphinx has the notion of a "version" and a "release" for the
software. Each version can have multiple releases. For example, for
Python the version is something like 2.5 or 3.0, while the release is
something like 2.5.1 or 3.0a1. If you don't need this dual structure,
just set both to the same value.

Project version: 0.1
Project release [0.1]: 0.1

The file name suffix for source files. Commonly, this is either ".txt"
or ".rst". Only files with this suffix are considered documents.

Source file suffix [.rst]:

One document is special in that it is considered the top node of the
"contents tree", that is, it is the root of the hierarchical structure
of the documents. Normally, this is "index", but if your "index"
document is a custom template, you can also set this to another filename.

Name of your master document (without suffix) [index]:

Sphinx can also add configuration for epub output:

Do you want to use the epub builder (y/N) [n]:

Please indicate if you want to use one of the following Sphinx extensions:

autodoc: automatically insert docstrings from modules (y/N) [n]:
doctest: automatically test code snippets in doctest blocks (y/N) [n]:
intersphinx: link between Sphinx documentation of different projects (y/N) [n]:
todo: write "todo" entries that can be shown or hidden on build (y/N) [n]:
coverage: checks for documentation coverage (y/N) [n]:
pngmath: include math, rendered as PNG images (y/N) [n]:
mathjax: include math, rendered in the browser by MathJax (y/N) [n]:
ifconfig: conditional inclusion of content based on config values (y/N) [n]:
viewcode: include links to the source code of documented Python objects (y/N) [n]:

A Makefile and a Windows command file can be generated for you so that you
only have to run e.g. `make html' instead of invoking sphinx-build
directly.

Create Makefile? (Y/n) [y]:
Create Windows command file? (Y/n) [y]: n

Creating file docs/conf.py.
Creating file docs/index.rst.
Creating file docs/Makefile.

Finished: An initial directory structure has been created.

You should now populate your master file docs/index.rst and create other documentation
source files. Use the Makefile to build the docs, like so:
make builder
where "builder" is one of the supported builders, e.g. html, latex or linkcheck.

wouter@midgard: /opt/dev/sandbox-cornice >

vi Makefile # so we don"t have to activate thevirtual environment
SPHINXBUILD = /opt/dev/sandbox-cornice/bin/sphinx-build

vi conf.py

>>> import cornice
>>> sys.path.insert(0, os.path.abspath(cornice.__file__))
>>> extensions = ['cornice.sphinxext']

vi index.rst

.. services::
📦 cornice.tests.validationapp

make html

the results:

cornice is found, includeme is run, add_apidoc is indeed called,
but apidocs in _get_services is returning an empty dict

def _get_services(self, package, ignore):
    from pyramid.config import Configurator
    conf = Configurator()
    conf.include('cornice')
    conf.scan(package, ignore=ignore)
    by_service = defaultdict(dict)
    apidocs = conf.registry.settings.get('apidocs', {})   <-- this is always an empty dict

I don't see what I'm missing here.

However creating an application and getting the apidoc from the request does not have this problem:

cd /opt/dev/sandbox-cornice
bin/paster create --t cornice demo
Enter appname (Application name) ['']: Demo
Enter description (One-line description of the project) ['']: Demo
Enter author (Author name) ['']: Wouter

cd demo
/opt/dev/sandbox-cornice/bin/python setup.py develop
/opt/dev/sandbox-cornice/bin/paster serve demo.ini
--> no problems running the application

vi demo/views.py
9 @hello.get()
10 def get_info(request):
11 """Returns Hello in JSON."""
12 print 'APIDOC: ', request.registry.settings.get('apidocs')
13 return {'Hello': 'World'}
~

/opt/dev/sandbox-cornice/bin/paster serve demo.ini

APIDOC:
{('/', 'GET'): {'filters': [<function filter_json_xsrf at 0x1b05758>], 'service': , 'request_method': 'GET', 'renderer': 'simplejson', 'func': <function get_info at 0x1b68cf8>, 'validators': []}}

add error code in error outputs

23:48 < tarek> we can add a list of them in cornice for sure,
23:48 < tarek> stuff like 'invalid json' is something that would be better of an error code
23:48 @mconnor tarek: mostly it's "if I get HTTP 400, with error 6, it should always be 'JSON parse failure' and not some other state"

simplify services code

the code that registers view is insanely complicated. In order to implenet Issue #39, we should refactor it:

  • have a central cornice registry that index all services
  • hook a single entry point for Pyramid
  • dispatch the request to the right location, and with the right functions chaining, given the index of services

colander schema data type

The way that the schema's attributes are being handled in sphinxext.py is with:

if 'schema' in args:
        schema = args['schema']

        attrs_node = nodes.inline()
        for location in ('headers', 'querystring', 'body'):
            attributes = schema.get_attributes(location=location)
            if attributes:
                attrs_node += nodes.inline(
                        text='values in the %s' % location)
                location_attrs = nodes.bullet_list()

                for attr in attributes:
                    temp = nodes.list_item()
                    desc = "%s : " % attr.name

                    if hasattr(attr, 'type'):
                        desc += " %s, " % attr.type

The problem is that Colander's node objects do not have a 'type' property, and so sphinx is never adding the type to docs.

These objects do, however, have an attribute called 'typ', which is a colander data-type object. We can then use isinstance() to check which data-type the schema node is representing.

I would like to re-factor to something like the following:

if 'schema' in args:
            schema = args['schema']

            attrs_node = nodes.inline()
            for location in ('headers', 'querystring', 'body'):
                attributes = schema.get_attributes(location=location)
                if attributes:
                    attrs_node += nodes.inline(
                            text='values in the %s' % location)
                    location_attrs = nodes.bullet_list()

                    for attr in attributes:
                        temp = nodes.list_item()
                        desc = "%s : " % attr.name

                        if hasattr(attr, 'type'):
                            # I'm not sure where the type property is at all in colander but leave it in as to not risk breaking anything
                            attr_type = attr.type
                        elif hasattr(attr, 'typ'):
                            attr_type = self._colander_data_type(attr.typ)

                        desc += " %s, " % attr_type

And a new protected method to determine the data-type:

def _colander_data_type(self, typ):
        """
        Get the data-type for an object in a colander schema node.
        """
        if isinstance(typ, colander.Mapping):
            data_type = 'mapping'
        elif isinstance(typ, colander.Tuple):
            data_type = 'tuple'
        elif isinstance(typ, colander.Sequence):
            data_type = 'sequence'
        elif isinstance(typ, colander.Seq):
            data_type = 'sequence'
        elif isinstance(typ, colander.String):
            data_type = 'string'
        elif isinstance(typ, colander.Integer):
            data_type = 'integer'
        elif isinstance(typ, colander.Int):
            data_type = 'integer'
        elif isinstance(typ, colander.Float):
            data_type = 'float'
        elif isinstance(typ, colander.Decimal):
            data_type = 'decimal'
        elif isinstance(typ, colander.Boolean):
            data_type = 'boolean'
        elif isinstance(typ, colander.Bool):
            data_type = 'boolean'
        elif isinstance(typ, colander.GlobalObject):
            data_type = 'global'
        elif isinstance(typ, colander.DateTime):
            data_type = 'datetime'
        elif isinstance(typ, colander.Date):
            data_type = 'date'
        elif isinstance(typ, colander.Time):
            data_type = 'time'

        return data_type

This could be helpful with creating a sample curl request and help with generating the full hierarchy of a schema for requests containing JSON, which is another issue ;)

Add request.current_service attribute

In #98, @rfk said:

As a larger architectural thought, I wonder if we should pass the service object into each validator by default. IOW, make the signature of a validator function "validate(service, request)" or similar. How common is it to make a validator that closes over the service object like this?

Update error response to be a dict

Right now the errors look like this:

[{"location": "url", "name": "delete", "description": "Delete must be 'false' if specified."}]

Ideally it should look like:

{'status': 'error',
 'errors': [{"location": "url", "name": "delete", "description": "Delete must be 'false' if specified."}]
}

So that one can always look to see what the status is, and read the errors if applicable.

Documentation should include working examples for each feature

It would be really helpful if we had examples that could be copy-paste-run to see how each feature is meant to be used, from both a service and resource approach.

Documentation that confuses me:


Doing validation and filtering at class level

If you want to use class methods to do validation, you can do so by passing the klass parameter to the hook_view or @method decorators, plus a string representing the name of the method you want to invoke on validation. This means something like this:

class MyClass:
    def validate_it(request):
        # put the validation logic here

@service.get(klass=MyClass, validators=('validate_it',))
def view(request):
    # do something with the request

How does the validation work in the method validate_it? Do I just add errors to the request? What gets returned (if anything)? If I try to fill in the blanks and run it I get:

File "/local/lib/python2.7/site-packages/cornice/service.py", line 320, in wrapper
    ob = args['klass'](request)
TypeError: this constructor takes no arguments

If I add an init method the request never makes it back to my view. Is this a bug in Cornice or am I not using klass correctly?

Another Example:


Managing ACLs

You can also specify a way to deal with ACLs: pass in a function that takes a request and returns an ACL, and that ACL will be applied to all views in the service:

foo = Service(name='foo', path='/foo', acl=_check_acls)

What goes in _check_acls? What would be an acceptable ACE? How do I tie an ACE to an http method? What if I'm using resources instead of services?

A working example for each feature documented, no matter how simple, would help.

scan(package, ignore=ignore) ignores everything

With Pyramid 1.3, and venusian==1.0a5, the ignore (which seems to be [''] by default, totally ignores every service.

This method is called in _get_services() somewhere in sphinxext.py.

Probably we could just ignore the empty string when populating the 'ignore' list in run(), near the top.

Views aren't registered in the right order with using the resource feature.

Following André Caron's mail on services-dev, here is an issue about the registration order of the views when using the resource feature.

Here is the complete mail

We're experimenting with Cornice to expose some REST APIs and I
found the following limitation: if we register multiple services
with the "@resource" decorator, we get this weird situation where
some URLs are not directed to the right views.

After some investigation into Pyramid internals, we extracted
the (ordered) list of URLs like so:

routes = configurator.get_routes_mapper().get_routes()
for route in routes:
logger.debug('WebServices: URL "%s" -> method "%s".',
route.pattern, route.name)

This shows that URLs appear ordered within a single service, but
services don't appear in the order they are defined within the
module (all the services are in the same file for the moment).
This causes some more general URL patterns to appear first in the
list and they capture URLs destined to other views.

This seems to be caused by Venusian, which does not call the
deferred decorators in the original sequence.

I know this issue is rather subtle and conflicting URL patterns
is not the ideal situation, but I think that the Cornice
documentation should warn against using "@resource" decorators on
a large scale and should recommend using the
"config.add_cornice_service()" explicitly instead.

Note that even if Venusian respected the class declaration order
within a single module, the problem could still occur on a larger
scale (module import order would still influence the order of URL
patterns).

You might be interested in looking at the routing system used by
Werkzeug since it provides some automatic URL pattern ordering to
avoid this confusion:

Flask uses the Werkzeug routing system which has was designed
to automatically order routes by complexity. This means that
you can declare routes in arbitrary order and they will still
work as expected. This is a requirement if you want to
properly implement decorator based routing since decorators
could be fired in undefined order when the application is
split into multiple modules.

(http://flask.pocoo.org/docs/design/#the-routing-system)

Consider using "X-Content-Type-Options: nosniff" to avoid IE XSS vulnerabilities

IE has some rather unfortunately content-type-sniffing behaviour that can be used to trigger XSS attacks via a JSON API, as described here:

http://blog.watchfire.com/wfblog/2011/10/json-based-xss-exploitation.html
https://superevr.com/blog/2012/exploiting-xss-in-ajax-web-applications/

It would be interesting to try to make cornice safe-by-default against this attacked, e.g. by automatically including X-Content-Type-Options header that disables this sniffing.

Remove the dependency to pyramid and sphinx

currently, the setup.py needs pyramid and sphinx to be installed. As the last modifications allow to not use pyramid, and because the sphinx plugin is optional, it could make sense to not have these dependencies as hard deps.

I propose to remove these and to add some try/import/except importerror statements in the code.

What do you think?

KeyError exception if error occurs in nested Colander Schema

Consider the following definition :


class ModelField(MappingSchema):
    name = SchemaNode(String())
    description = SchemaNode(String())

class ModelFields(SequenceSchema):
    field = ModelField()

class ModelDefinition(MappingSchema):
    title = SchemaNode(String(), location="body")
    fields = ModelFields(validator=Length(min=1), location="body")

Posting this valid data poses no problem :

{"title": "Mushroom", "fields": [{"name": "genre", "description": "Genre"}]}

Posting data with error in the nested field schema, like this :

{"title": "Mushroom", "fields": [{"schmil": "Blick"}]}

Fails with a keyerror in Cornice :

      File "/home/mle/Code/daybed/env/local/lib/python2.7/site-packages/pyramid-1.3-py2.7.egg/pyramid/config/views.py", line 333, in rendered_view
        result = view(context, request)
      File "/home/mle/Code/daybed/env/local/lib/python2.7/site-packages/cornice-0.8-py2.7.egg/cornice/service.py", line 30, in call_service
        validator(request)
      File "/home/mle/Code/daybed/env/local/lib/python2.7/site-packages/cornice-0.8-py2.7.egg/cornice/validators.py", line 52, in validator
        _validate_fields('body', body)
      File "/home/mle/Code/daybed/env/local/lib/python2.7/site-packages/cornice-0.8-py2.7.egg/cornice/validators.py", line 44, in _validate_fields
        request.errors.add(location, attr.name, e.asdict()[attr.name])
    KeyError: 'fields'


Full example : https://github.com/spiral-project/daybed/blob/master/HACK.rst

resources functionality broken?

Hey guys,

I tried to get resources working as described here: http://cornice.readthedocs.org/en/latest/resources.html
Didn't get it to run though so I tried to run the tests, but the tests that test resources fail as well (all other tests pass)

Python: 2.7.2
Cornice: Latest from github

Test output:

(pylons_pyramid)macbook-7:tmp mfeller$ python -m unittest discover -s cornice/cornice/tests/

EE............

ERROR: test_accept_headers (test_resource.TestResource)

Traceback (most recent call last):
File "/private/tmp/cornice/cornice/tests/test_resource.py", line 71, in test_accept_headers
params=json.dumps({'test': 'yeah'})).json,
File "/Users/mfeller/programming/python/virtualenv/pylons_pyramid/lib/python2.7/site-packages/webtest/app.py", line 807, in post
content_type=content_type)
File "/Users/mfeller/programming/python/virtualenv/pylons_pyramid/lib/python2.7/site-packages/webtest/app.py", line 787, in _gen_request
expect_errors=expect_errors)
File "/Users/mfeller/programming/python/virtualenv/pylons_pyramid/lib/python2.7/site-packages/webtest/app.py", line 1075, in do_request
self._check_status(status, res)
File "/Users/mfeller/programming/python/virtualenv/pylons_pyramid/lib/python2.7/site-packages/webtest/app.py", line 1111, in _check_status
res.body))
AppError: Bad response: 404 Not Found (not 200 OK or 3xx redirect for http://localhost/users)
404 Not Found

The resource could not be found.

/users

ERROR: test_basic_resource (test_resource.TestResource)

Traceback (most recent call last):
File "/private/tmp/cornice/cornice/tests/test_resource.py", line 55, in test_basic_resource
self.app.get("/users").json,
File "/Users/mfeller/programming/python/virtualenv/pylons_pyramid/lib/python2.7/site-packages/webtest/app.py", line 753, in get
expect_errors=expect_errors)
File "/Users/mfeller/programming/python/virtualenv/pylons_pyramid/lib/python2.7/site-packages/webtest/app.py", line 1075, in do_request
self._check_status(status, res)
File "/Users/mfeller/programming/python/virtualenv/pylons_pyramid/lib/python2.7/site-packages/webtest/app.py", line 1111, in _check_status
res.body))
AppError: Bad response: 404 Not Found (not 200 OK or 3xx redirect for http://localhost/users)
404 Not Found

The resource could not be found.

/users


Ran 14 tests in 0.194s

FAILED (errors=2)

Is there anything I'm doing wrong?

Cheers,

Martin

Allow view config decorators on classes

In my effort to get cornice to work smoothly with Pyramid's hybrid routing (URL dispatch + Traversal), I've come across the need to put the view decorators on a class instead of a function, like so:

foo = Service(
  name='foo',
  path='/foo/*traverse',
  description='Foo',
  factory='foo.RootFactory'
)

@foo.get(context=FooResource)
class ViewFoo(object):
  def __init__(self, request):
    self.request = request
    self.foo = request.context

  def __call__(self):
    foo_data = self.foo.some_method()
    if foo_data:
      return {'status': 'OK', 'data': foo_data }
    else:
      return {'status': 'NOT_FOUND_OR_NOT_ALLOWED' }

The problem is that cornice does not appear to support putting view decorators on classes, only on callables. What I'm doing above is a standard Pyramid idiom for decorating a class with a view configuration, and it makes sense that cornice should support this.

The patch below does seem to fix the issue, but I'm pretty sure that there is a better way to do this. ;) 30 minutes of digging around the Pyramid and Cornice source code did not reveal it, though -- so I'm posting this here as a first effort to get the issue moving:

--- service.py.orig 2012-11-28 10:35:31.000000000 -0800
+++ service.py  2012-11-28 10:36:37.000000000 -0800
@@ -2,6 +2,7 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 import functools
+import inspect
 import warnings

 from cornice.validators import (
@@ -346,6 +347,10 @@
         if len(request.errors) > 0:
             return args['error_handler'](request.errors)

+        # if the view decorator was on a class, it still needs to be called.
+        if inspect.isclass(view_):
+          response = response()
+
         # We can't apply filters at this level, since "response" may not have
         # been rendered into a proper Response object yet.  Instead, give the
         # request a reference to its api_kwargs so that a tween can apply them.

document all options

We need to document all options of the Services class, and the various decorators, including resource

tutorial / example broken (Unknown predicate values: {'validator': (, )})

when following the tutorial i get the following error upon starting up the app with bin/pserve:

# bin/pserve messaging/messaging.ini 
Traceback (most recent call last):
  File "bin/pserve", line 9, in <module>
    load_entry_point('pyramid==1.4a2', 'console_scripts', 'pserve')()
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/scripts/pserve.py", line 47, in main
    return command.run()
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/scripts/pserve.py", line 290, in run
    relative_to=base, global_conf=vars)
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/scripts/pserve.py", line 318, in loadapp
    return loadapp(app_spec, name=name, relative_to=relative_to, **kw)
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/paste/deploy/loadwsgi.py", line 247, in loadapp
    return loadobj(APP, uri, name=name, **kw)
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/paste/deploy/loadwsgi.py", line 272, in loadobj
    return context.create()
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/paste/deploy/loadwsgi.py", line 710, in create
    return self.object_type.invoke(self)
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/paste/deploy/loadwsgi.py", line 146, in invoke
    return fix_call(context.object, context.global_conf, **context.local_conf)
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/paste/deploy/util.py", line 56, in fix_call
    val = callable(*args, **kw)
  File "/Users/tomster/Development/cornice/examples/messaging/messaging/__init__.py", line 10, in main
    return config.make_wsgi_app()
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/config/__init__.py", line 955, in make_wsgi_app
    self.commit()
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/config/__init__.py", line 629, in commit
    self.action_state.execute_actions(introspector=self.introspector)
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/config/__init__.py", line 1064, in execute_actions
    for action in resolveConflicts(self.actions):
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/config/__init__.py", line 1148, in resolveConflicts
    discriminator = undefer(action['discriminator'])
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/registry.py", line 250, in undefer
    v = v.resolve()
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/registry.py", line 242, in resolve
    return self.func()
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/config/views.py", line 1099, in discrim_func
    order, preds, phash = predlist.make(self, **pvals)
  File "/Users/tomster/Development/cornice/examples/lib/python2.7/site-packages/pyramid/config/util.py", line 273, in make
    raise ConfigurationError('Unknown predicate values: %r' % (kw,))
pyramid.exceptions.ConfigurationError: Unknown predicate values: {'validator': (<function valid_token at 0x107a4cf50>, <function valid_message at 0x107a4e140>)}

Changing DEFAULT_VALIDATORS has no effect.

I'm applying the suggestion for site-wide validators, but the following code has no effect:

import cornice.validators

def validate_auth_token(request):
    # Validate token found in custom HTTP header.

cornice.validators.DEFAULT_VALIDATORS.append(validate_auth_token)

The validator is not invoked.

Looking at cornice/service.py shows that the Service.default_validators is initialized with DEFAULT_VALIDATORS, but the Service.default_validators attribute is never used.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.