Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

fix to make the mechanism pulling docstrings from validators in work #44

Closed
wants to merge 5 commits into from

3 participants

@xrotwang

No description provided.

@ametaireau
Owner

Sorry, I'm not sure to get how this fix works. Any explanation on this is welcome :) (and would probably be nice to have as comments in the code).

Thanks!

@xrotwang

Well, I'm not sure how it works either :) I basically made things up starting from the following sphinx error until sphinx stopped complaining:

Sphinx version: 1.1.1
Python version: 2.6.5
Docutils version: 0.7 release
Jinja2 version: 2.6

Traceback (most recent call last):
File "lib/python2.6/site-packages/sphinx/cmdline.py", line 189, in main
app.build(force_all, filenames)
File "lib/python2.6/site-packages/sphinx/application.py", line 204, in build
self.builder.build_update()
File "lib/python2.6/site-packages/sphinx/builders/init.py", line 196, in build_update
'out of date' % len(to_build))
File "lib/python2.6/site-packages/sphinx/builders/init.py", line 216, in build
purple, length):
File "lib/python2.6/site-packages/sphinx/builders/init.py", line 120, in status_iterator
for item in iterable:
File "lib/python2.6/site-packages/sphinx/environment.py", line 613, in update_generator
self.read_doc(docname, app=app)
File "lib/python2.6/site-packages/sphinx/environment.py", line 761, in read_doc
pub.publish()
File "lib/python2.6/site-packages/docutils/core.py", line 203, in publish
self.settings)
File "lib/python2.6/site-packages/docutils/readers/init.py", line 69, in read
self.parse()
File "lib/python2.6/site-packages/docutils/readers/init.py", line 75, in parse
self.parser.parse(self.input, document)
File "lib/python2.6/site-packages/docutils/parsers/rst/init.py", line 157, in parse
self.statemachine.run(inputlines, document, inliner=self.inliner)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 170, in run
input_source=document['source'])
File "lib/python2.6/site-packages/docutils/statemachine.py", line 233, in run
context, state, transitions)
File "lib/python2.6/site-packages/docutils/statemachine.py", line 454, in check_line
return method(match, context, next_state)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 2706, in underline
self.section(title, source, style, lineno - 1, messages)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 329, in section
self.new_subsection(title, lineno, messages)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 398, in new_subsection
node=section_node, match_titles=1)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 284, in nested_parse
node=node, match_titles=match_titles)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 195, in run
results = StateMachineWS.run(self, input_lines, input_offset)
File "lib/python2.6/site-packages/docutils/statemachine.py", line 233, in run
context, state, transitions)
File "lib/python2.6/site-packages/docutils/statemachine.py", line 454, in check_line
return method(match, context, next_state)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 2281, in explicit_markup
nodelist, blank_finish = self.explicit_construct(match)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 2293, in explicit_construct
return method(self, expmatch)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 2035, in directive
directive_class, match, type_name, option_presets)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 2086, in run_directive
result = directive_instance.run()
File "src/cornice/cornice/sphinxext.py", line 210, in run
services_node += self.render_service(path, service, methods)
File "src/cornice/cornice/sphinxext.py", line 157, in render_service
node = rst2node(docstring)
File "src/cornice/cornice/util.py", line 56, in rst2node
parser.parse(data, document)
File "lib/python2.6/site-packages/docutils/parsers/rst/__init
.py", line 157, in parse
self.statemachine.run(inputlines, document, inliner=self.inliner)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 170, in run
input_source=document['source'])
File "lib/python2.6/site-packages/docutils/statemachine.py", line 233, in run
context, state, transitions)
File "lib/python2.6/site-packages/docutils/statemachine.py", line 454, in check_line
return method(match, context, next_state)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 1405, in field_marker
blank_finish=blank_finish)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 321, in nested_list_parse
node=node, match_titles=match_titles)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 195, in run
results = StateMachineWS.run(self, input_lines, input_offset)
File "lib/python2.6/site-packages/docutils/statemachine.py", line 233, in run
context, state, transitions)
File "lib/python2.6/site-packages/docutils/statemachine.py", line 454, in check_line
return method(match, context, next_state)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 2507, in field_marker
field, blank_finish = self.field(match)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 1425, in field
self.parse_field_body(indented, line_offset, field_body)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 1435, in parse_field_body
self.nested_parse(indented, input_offset=offset, node=node)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 284, in nested_parse
node=node, match_titles=match_titles)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 195, in run
results = StateMachineWS.run(self, input_lines, input_offset)
File "lib/python2.6/site-packages/docutils/statemachine.py", line 233, in run
context, state, transitions)
File "lib/python2.6/site-packages/docutils/statemachine.py", line 454, in check_line
return method(match, context, next_state)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 2643, in blank
context, self.state_machine.abs_line_number() - 1)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 421, in paragraph
textnodes, messages = self.inline_text(text, lineno)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 430, in inline_text
return self.inliner.parse(text, lineno, self.memo, self.parent)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 516, in parse
lineno)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 774, in interpreted_or_phrase_ref
lineno)
File "lib/python2.6/site-packages/docutils/parsers/rst/states.py", line 830, in interpreted
nodes, messages2 = role_fn(role, rawsource, text, lineno, self)
File "lib/python2.6/site-packages/sphinx/domains/init.py", line 173, in role_adapter
inliner, options, content)
File "lib/python2.6/site-packages/sphinx/roles.py", line 97, in call
env = inliner.document.settings.env
AttributeError: Values instance has no attribute 'env'

@xrotwang

sorry for misusing github. i forgot to create a branch for this latest commit which is unrelated to the first one in this pull request.

@tarekziade
Owner

@xrotwang do you mind remerging with master in your branch so we can merge ?
@ametaireau do you mind finishing reviewing this one so we can empty the pull requests pile ?

Thanks guys

@ametaireau
Owner

@xrotwang it seems that you did two different changes regarding different features in this pull request. do you mind closing this one and reopening two pull requests with the appropriate changes in each? (or just tell me which commit I should consider for inclusion here so I can cherry-pick them)

@xrotwang

The relevant commits are
xrotwang/cornice@8a44e67
and
xrotwang/cornice@9b4d8a9

I commited some other changes to my branch which i'm not sure are worth merging back. For example I switched to register services as named utilities (using ZCA features of the registry). This solved the problem of using cornice (by default) even for apps with no defined services (which resulted in an exception otherwise).

I also started to use the sphinx contrib package HTTP-Domain [1] to format the service docs.
[1] https://github.com/deceze/Sphinx-HTTP-domain

@ametaireau
Owner

I've cherry picked the two commits you pointed out. Feel free to open another pull request for further changes, thanks!

@ametaireau ametaireau closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 13, 2012
  1. fix to make doc strings in validators work

    rforkel authored
Commits on Mar 20, 2012
Commits on Mar 21, 2012
Commits on Mar 22, 2012
  1. removed unused static assets, tweaked sohinxext.

    rforkel authored
This page is out of date. Refresh to see the latest.
View
1  .gitignore
@@ -17,3 +17,4 @@ test
nosetests.xml
man
.channel
+*.komodoproject
View
89 cornice/__init__.py
@@ -4,22 +4,19 @@
import json
import logging
-from pyramid.events import BeforeRender, NewRequest
+from pyramid.events import NewRequest
from pyramid.httpexceptions import HTTPNotFound, HTTPMethodNotAllowed
from pyramid.exceptions import PredicateMismatch
from cornice import util
from cornice.errors import Errors
from cornice.service import Service # NOQA
+from cornice.interfaces import IService
logger = logging.getLogger('cornice')
-def add_renderer_globals(event):
- event['util'] = util
-
-
def wrap_request(event):
"""Adds a "validated" dict, a custom "errors" object and an "info" dict to
the request object if they don't already exists
@@ -42,56 +39,55 @@ def add_apidoc(config, pattern, func, service, **kwargs):
info['func'] = func
+def get_service(request):
+ if getattr(request, 'matched_route'):
+ return request.registry.queryUtility(IService, name=request.matched_route.pattern)
+
+
def tween_factory(handler, registry):
"""Wraps the default WSGI workflow to provide cornice utilities"""
def cornice_tween(request):
response = handler(request)
- if request.matched_route is not None:
- # do some sanity checking on the response using filters
- pattern = request.matched_route.pattern
- service = request.registry['cornice_services'].get(pattern)
- if service is not None:
- if request.method not in service.defined_methods:
- response = HTTPMethodNotAllowed()
- response.allow = service.defined_methods
- else:
- # get the filters for this call
- kwargs = service.definitions[request.method]
- for _filter in kwargs.get('filters', []):
- response = _filter(response)
+ service = get_service(request)
+ if service is not None:
+ if request.method not in service.defined_methods:
+ response = HTTPMethodNotAllowed()
+ response.allow = service.defined_methods
+ else:
+ # get the filters for this call
+ kwargs = service.definitions[request.method]
+ for _filter in kwargs.get('filters', []):
+ response = _filter(response)
return response
return cornice_tween
def _notfound(request):
- match = request.matchdict
- if match is not None:
- pattern = request.matched_route.pattern
- service = request.registry['cornice_services'].get(pattern)
- if (service is not None
- and isinstance(request.exception, PredicateMismatch)
- and request.method in service.defined_methods):
- # maybe was it the accept predicate that was not matched
- # in this case, returns a HTTP 406 NOT ACCEPTABLE with the
- # list of available choices
- api_kwargs = service.definitions[request.method]
- if 'accept' in api_kwargs:
- accept = api_kwargs.get('accept')
- acceptable = [a for a in util.to_list(accept) if
- not callable(a)]
-
- if 'acceptable' in request.info:
- for content_type in request.info['acceptable']:
- if content_type not in acceptable:
- acceptable.append(content_type)
-
- if not request.accept.best_match(acceptable):
- # if not, return the list of accepted headers
- resp = request.response
- resp.status = 406
- resp.content_type = "application/json"
- resp.body = json.dumps(acceptable)
- return resp
+ service = get_service(request)
+ if (service is not None
+ and isinstance(request.exception, PredicateMismatch)
+ and request.method in service.defined_methods):
+ # maybe was it the accept predicate that was not matched
+ # in this case, returns a HTTP 406 NOT ACCEPTABLE with the
+ # list of available choices
+ api_kwargs = service.definitions[request.method]
+ if 'accept' in api_kwargs:
+ accept = api_kwargs.get('accept')
+ acceptable = [a for a in util.to_list(accept) if
+ not callable(a)]
+
+ if 'acceptable' in request.info:
+ for content_type in request.info['acceptable']:
+ if content_type not in acceptable:
+ acceptable.append(content_type)
+
+ if not request.accept.best_match(acceptable):
+ # if not, return the list of accepted headers
+ resp = request.response
+ resp.status = 406
+ resp.content_type = "application/json"
+ resp.body = json.dumps(acceptable)
+ return resp
# 404
return request.exception
@@ -101,7 +97,6 @@ def includeme(config):
"""
config.add_directive('add_apidoc', add_apidoc)
config.add_view(_notfound, context=HTTPNotFound)
- config.add_subscriber(add_renderer_globals, BeforeRender)
config.add_subscriber(wrap_request, NewRequest)
config.add_tween('cornice.tween_factory')
config.add_renderer('simplejson', util.json_renderer)
View
6 cornice/interfaces.py
@@ -0,0 +1,6 @@
+from zope.interface import Interface
+
+
+class IService(Interface):
+ """Cornice service interface
+ """
View
23 cornice/service.py
@@ -5,12 +5,14 @@
import functools
import venusian
+from zope.interface import implements
+from cornice.interfaces import IService
from cornice.util import to_list, json_error, match_accept_header
from cornice.validators import (
- DEFAULT_VALIDATORS,
- DEFAULT_FILTERS,
- validate_colander_schema
+ DEFAULT_VALIDATORS,
+ DEFAULT_FILTERS,
+ validate_colander_schema
)
from cornice.schemas import CorniceSchema
@@ -24,10 +26,11 @@ def call_service(func, api_kwargs, context, request):
# apply validators
for validator in api_kwargs.get('validators', []):
validator(request)
- if len(request.errors) > 0:
- return json_error(request.errors)
+
+ if len(request.errors) > 0:
+ return json_error(request.errors)
- return func(request)
+ return dict(result=func(request), status='ok')
class Service(object):
@@ -68,6 +71,7 @@ class Service(object):
http://readthedocs.org/docs/pyramid/en/1.0-branch/glossary.html#term-acl
for more information about ACLs.
"""
+ implements(IService)
def __init__(self, **kw):
self.defined_methods = []
@@ -91,14 +95,13 @@ def __repr__(self):
self.route_name)
def _define(self, config, method):
- # setup the services hash if it isn't already
- services = config.registry.setdefault('cornice_services', {})
if self.index == -1:
+ services = list(config.registry.getUtilitiesFor(IService))
self.index = len(services)
# define the route if it isn't already
- if self.route_pattern not in services:
- services[self.route_pattern] = self
+ if not config.registry.queryUtility(IService, name=self.route_pattern):
+ config.registry.registerUtility(self, IService, name=self.route_pattern)
route_kw = {}
if self.factory is not None:
route_kw["factory"] = self.factory
View
7 cornice/sphinxext.py
@@ -42,7 +42,10 @@ def trim(docstring):
while trimmed and not trimmed[0]:
trimmed.pop(0)
# Return a single string:
- return '\n'.join(trimmed)
+ res = '\n'.join(trimmed)
+ if not isinstance(res, unicode):
+ res = res.decode('utf8')
+ return res
from sphinx.locale import l_
from sphinx.util.docfields import Field, GroupedField, TypedField
@@ -200,7 +203,7 @@ def run(self):
# we want to list all of them
services_id = "services-%d" % env.new_serialno('services')
services_node = nodes.section(ids=[services_id])
- services_node += nodes.title(text='Services')
+ services_node += nodes.title(text=pkg)
services_ = [(service.index, path, service, methods) \
for (path, service), methods in services.items()]
View
23 cornice/static/cornice.css
@@ -1,23 +0,0 @@
-div.resource {
- padding: 10px;
- margin: 5px;
- background-color: #D0D0D0;
- -moz-border-radius: 15px;
- border-radius: 15px;
- width: 90%
-}
-
-div.resource-title {
- margin-bottom: 5px;
- font-size: 120%;
-}
-
-div.resource-renderer {
- float: right;
- background-color: #FF9933;
- font-weight: bold;
- -moz-border-radius: 10px;
- border-radius: 10px;
- color: white;
- padding: 6px;
-}
View
BIN  cornice/static/favicon.ico
Binary file not shown
View
BIN  cornice/static/footerbg.png
Deleted file not rendered
View
BIN  cornice/static/headerbg.png
Deleted file not rendered
View
8 cornice/static/ie6.css
@@ -1,8 +0,0 @@
-* html img,
-* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
-this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
-this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
-this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
-this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
-);}
-#wrap{display:table;height:100%}
View
BIN  cornice/static/middlebg.png
Deleted file not rendered
View
65 cornice/static/pylons.css
@@ -1,65 +0,0 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
-a{color:#1b61d6;text-decoration:none;}
-a:hover{color:#e88f00;text-decoration:underline;}
-body h1,
-body h2,
-body h3,
-body h4,
-body h5,
-body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
-#wrap{min-height:100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
-#header{background:#000000;top:0;font-size:14px;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
-/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
View
BIN  cornice/static/pyramid-small.png
Deleted file not rendered
View
BIN  cornice/static/pyramid.png
Deleted file not rendered
View
BIN  cornice/static/transparent.gif
Deleted file not rendered
View
8 cornice/tests/test_resource.py
@@ -53,14 +53,14 @@ def test_basic_resource(self):
self.assertEquals(
self.app.get("/users").json,
- {'users': [1, 2]})
+ {'status': 'ok', 'result': {'users': [1, 2]}})
self.assertEquals(
self.app.get("/users/1").json,
- {'name': 'gawel'})
+ {'status': 'ok', 'result': {'name': 'gawel'}})
resp = self.app.get("/users/1?callback=test")
self.assertEquals(resp.body,
- 'test({"name": "gawel"})', resp.body)
+ 'test({"status": "ok", "result": {"name": "gawel"}})', resp.body)
def test_accept_headers(self):
# the accept headers should work even in case they're specified in a
@@ -69,4 +69,4 @@ def test_accept_headers(self):
self.app.post("/users",
headers={'Accept': 'text/json'},
params=json.dumps({'test': 'yeah'})).json,
- {'test': 'yeah'})
+ {'status': 'ok', 'result': {'test': 'yeah'}})
View
12 cornice/tests/test_service_definition.py
@@ -47,11 +47,11 @@ def test_basic_service_operation(self):
self.app.get("/unknown", status=404)
self.assertEquals(
self.app.get("/service1").json,
- {'test': "succeeded"})
+ {'status': 'ok', 'result': {'test': "succeeded"}})
self.assertEquals(
self.app.post("/service1", params="BODY").json,
- {'body': 'BODY'})
+ {'status': 'ok', 'result': {'body': 'BODY'}})
def test_loading_into_multiple_configurators(self):
# When initializing a second configurator, it shouldn't interfere
@@ -63,17 +63,17 @@ def test_loading_into_multiple_configurators(self):
# Calling the new configurator works as expected.
app = TestApp(CatchErrors(config2.make_wsgi_app()))
self.assertEqual(app.get("/service1").json,
- {'test': 'succeeded'})
+ {'status': 'ok', 'result': {'test': 'succeeded'}})
# Calling the old configurator works as expected.
self.assertEqual(self.app.get("/service1").json,
- {'test': 'succeeded'})
+ {'status': 'ok', 'result': {'test': 'succeeded'}})
def test_stacking_api_decorators(self):
# Stacking multiple @api calls on a single function should
# register it multiple times, just like @view_config does.
resp = self.app.get("/service2", headers={'Accept': 'text/html'})
- self.assertEquals(resp.json, {'test': 'succeeded'})
+ self.assertEquals(resp.json, {'status': 'ok', 'result': {'test': 'succeeded'}})
resp = self.app.post("/service2", headers={'Accept': 'audio/ogg'})
- self.assertEquals(resp.json, {'test': 'succeeded'})
+ self.assertEquals(resp.json, {'status': 'ok', 'result': {'test': 'succeeded'}})
View
4 cornice/tests/test_service_description.py
@@ -122,9 +122,9 @@ def test_schema_validation(self):
resp = self.app.post('/foobar?yeah=test', params=json.dumps(data),
status=200)
- self.assertEquals(resp.json, {"test": "succeeded"})
+ self.assertEquals(resp.json, {'status': 'ok', 'result': {"test": "succeeded"}})
def test_schema_validation2(self):
resp = self.app.get('/foobar?yeah=test', status=200)
- self.assertEquals(resp.json, {"test": "succeeded"})
+ self.assertEquals(resp.json, {'status': 'ok', 'result': {"test": "succeeded"}})
View
11 cornice/tests/test_validation.py
@@ -7,7 +7,7 @@
from webtest import TestApp
from pyramid.response import Response
-from cornice.tests.validationapp import main, _json
+from cornice.tests.validationapp import main, _json, main2
from cornice.tests.support import LoggingCatcher
from cornice.errors import Errors
from cornice.validators import filter_json_xsrf
@@ -15,6 +15,11 @@
class TestServiceDefinition(LoggingCatcher, unittest.TestCase):
+ def test_app_with_no_service(self):
+ app = TestApp(main2({}))
+ app.get('/view', status=200)
+
+
def test_validation(self):
app = TestApp(main({}))
app.get('/service', status=400)
@@ -24,13 +29,13 @@ def test_validation(self):
res = app.post('/service', params=json.dumps('buh'))
- self.assertEqual(res.body, json.dumps({'body': '"buh"'}))
+ self.assertEqual(res.body, json.dumps({'status': 'ok', 'result': {'body': '"buh"'}}))
app.get('/service?paid=yup')
# valid = foo is one
res = app.get('/service?foo=1&paid=yup')
- self.assertEqual(res.json['foo'], 1)
+ self.assertEqual(res.json['result']['foo'], 1)
# invalid value for foo
res = app.get('/service?foo=buh&paid=yup', status=400)
View
12 cornice/tests/validationapp.py
@@ -109,3 +109,15 @@ def main(global_config, **settings):
config = Configurator(settings={})
config.include(includeme)
return CatchErrors(config.make_wsgi_app())
+
+
+def view(request):
+ return {}
+
+def main2(global_config, **settings):
+ config = Configurator(settings={})
+ config.include('cornice')
+ config.add_route('view', '/view')
+ config.add_view(view, route_name='view', renderer='json')
+ return CatchErrors(config.make_wsgi_app())
+
View
5 cornice/util.py
@@ -41,6 +41,10 @@ def rst2html(data):
return core.publish_string(data, writer=_FragmentWriter())
+class Env(object):
+ temp_data = {}
+ docname = ''
+
def rst2node(data):
"""Converts a reStructuredText into its node
@@ -53,6 +57,7 @@ def rst2node(data):
document.settings.tab_width = 4
document.settings.pep_references = False
document.settings.rfc_references = False
+ document.settings.env = Env()
parser.parse(data, document)
if len(document.children) == 1:
return document.children[0]
Something went wrong with that request. Please try again.