kinto 19.5.0__py3-none-any.whl → 19.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kinto might be problematic. Click here for more details.

Files changed (37) hide show
  1. kinto/core/__init__.py +3 -3
  2. kinto/core/cornice/__init__.py +93 -0
  3. kinto/core/cornice/cors.py +144 -0
  4. kinto/core/cornice/errors.py +40 -0
  5. kinto/core/cornice/pyramidhook.py +373 -0
  6. kinto/core/cornice/renderer.py +89 -0
  7. kinto/core/cornice/resource.py +205 -0
  8. kinto/core/cornice/service.py +641 -0
  9. kinto/core/cornice/util.py +138 -0
  10. kinto/core/cornice/validators/__init__.py +94 -0
  11. kinto/core/cornice/validators/_colander.py +142 -0
  12. kinto/core/cornice/validators/_marshmallow.py +182 -0
  13. kinto/core/cornice_swagger/__init__.py +92 -0
  14. kinto/core/cornice_swagger/converters/__init__.py +21 -0
  15. kinto/core/cornice_swagger/converters/exceptions.py +6 -0
  16. kinto/core/cornice_swagger/converters/parameters.py +90 -0
  17. kinto/core/cornice_swagger/converters/schema.py +249 -0
  18. kinto/core/cornice_swagger/swagger.py +725 -0
  19. kinto/core/cornice_swagger/templates/index.html +73 -0
  20. kinto/core/cornice_swagger/templates/index_script_template.html +21 -0
  21. kinto/core/cornice_swagger/util.py +42 -0
  22. kinto/core/cornice_swagger/views.py +78 -0
  23. kinto/core/openapi.py +2 -3
  24. kinto/core/resource/viewset.py +1 -1
  25. kinto/core/testing.py +1 -1
  26. kinto/core/utils.py +3 -2
  27. kinto/core/views/batch.py +1 -1
  28. kinto/core/views/openapi.py +1 -1
  29. kinto/plugins/flush.py +1 -1
  30. kinto/plugins/openid/views.py +1 -1
  31. kinto/views/contribute.py +2 -1
  32. {kinto-19.5.0.dist-info → kinto-19.6.0.dist-info}/METADATA +2 -4
  33. {kinto-19.5.0.dist-info → kinto-19.6.0.dist-info}/RECORD +37 -16
  34. {kinto-19.5.0.dist-info → kinto-19.6.0.dist-info}/LICENSE +0 -0
  35. {kinto-19.5.0.dist-info → kinto-19.6.0.dist-info}/WHEEL +0 -0
  36. {kinto-19.5.0.dist-info → kinto-19.6.0.dist-info}/entry_points.txt +0 -0
  37. {kinto-19.5.0.dist-info → kinto-19.6.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,89 @@
1
+ from pyramid import httpexceptions as exc
2
+ from pyramid.renderers import JSON
3
+ from pyramid.response import Response
4
+
5
+
6
+ def bytes_adapter(obj, request):
7
+ """Convert bytes objects to strings for json error renderer."""
8
+ if isinstance(obj, bytes):
9
+ return obj.decode("utf8")
10
+ return obj
11
+
12
+
13
+ class JSONError(exc.HTTPError):
14
+ def __init__(self, serializer, serializer_kw, errors, status=400):
15
+ body = {"status": "error", "errors": errors}
16
+ Response.__init__(self, serializer(body, **serializer_kw))
17
+ self.status = status
18
+ self.content_type = "application/json"
19
+
20
+
21
+ class CorniceRenderer(JSON):
22
+ """We implement JSON serialization by extending Pyramid's default
23
+ JSON rendering machinery using our own custom Content-Type logic `[1]`_.
24
+
25
+ This allows developers to config the JSON renderer using Pyramid's
26
+ configuration machinery `[2]`_.
27
+
28
+ .. _`[1]`: https://github.com/mozilla-services/cornice/pull/116 \
29
+ #issuecomment-14355865
30
+ .. _`[2]`: http://pyramid.readthedocs.io/en/latest/narr/renderers.html \
31
+ #serializing-custom-objects
32
+ """
33
+
34
+ acceptable = ("application/json", "text/plain")
35
+
36
+ def __init__(self, *args, **kwargs):
37
+ """Adds a `bytes` adapter by default."""
38
+ super(CorniceRenderer, self).__init__(*args, **kwargs)
39
+ self.add_adapter(bytes, bytes_adapter)
40
+
41
+ def render_errors(self, request):
42
+ """Returns an HTTPError with the given status and message.
43
+
44
+ The HTTP error content type is "application/json"
45
+ """
46
+ default = self._make_default(request)
47
+ serializer_kw = self.kw.copy()
48
+ serializer_kw["default"] = default
49
+ return JSONError(
50
+ serializer=self.serializer,
51
+ serializer_kw=serializer_kw,
52
+ errors=request.errors,
53
+ status=request.errors.status,
54
+ )
55
+
56
+ def render(self, value, system):
57
+ """Extends the default `_render` function of the pyramid JSON renderer.
58
+
59
+ Compared to the default `pyramid.renderers.JSON` renderer:
60
+ 1. Overrides the response with an empty string and
61
+ no Content-Type in case of HTTP 204.
62
+ 2. Overrides the default behavior of Content-Type handling,
63
+ forcing the use of `acceptable_offers`, instead of letting
64
+ the user specify the Content-Type manually.
65
+ TODO: maybe explain this a little better
66
+ """
67
+ request = system.get("request")
68
+ if request is not None:
69
+ response = request.response
70
+
71
+ # Do not return content with ``204 No Content``
72
+ if response.status_code == 204:
73
+ response.content_type = None
74
+ return ""
75
+
76
+ ctypes = request.accept.acceptable_offers(offers=self.acceptable)
77
+ if not ctypes:
78
+ ctypes = [(self.acceptable[0], 1.0)]
79
+ response.content_type = ctypes[0][0]
80
+ default = self._make_default(request)
81
+ return self.serializer(value, default=default, **self.kw)
82
+
83
+ def __call__(self, info):
84
+ """Overrides the default behavior of `pyramid.renderers.JSON`.
85
+
86
+ Uses a public `render()` method instead of defining render inside
87
+ `__call__`, to let the user extend it if necessary.
88
+ """
89
+ return self.render
@@ -0,0 +1,205 @@
1
+ # -*- coding: utf-8 -*-
2
+ # This Source Code Form is subject to the terms of the Mozilla Public
3
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ import functools
6
+ import warnings
7
+
8
+ import venusian
9
+
10
+ from kinto.core.cornice import Service
11
+
12
+
13
+ def resource(depth=2, **kw):
14
+ """Class decorator to declare resources.
15
+
16
+ All the methods of this class named by the name of HTTP resources
17
+ will be used as such. You can also prefix them by ``"collection_"`` and
18
+ they will be treated as HTTP methods for the given collection path
19
+ (collection_path), if any.
20
+
21
+ :param depth:
22
+ Which frame should be looked in default 2.
23
+
24
+ :param kw:
25
+ Keyword arguments configuring the resource.
26
+
27
+ Here is an example::
28
+
29
+ @resource(collection_path='/users', path='/users/{id}')
30
+ """
31
+
32
+ def wrapper(klass):
33
+ return add_resource(klass, depth, **kw)
34
+
35
+ return wrapper
36
+
37
+
38
+ def add_resource(klass, depth=2, **kw):
39
+ """Function to declare resources of a Class.
40
+
41
+ All the methods of this class named by the name of HTTP resources
42
+ will be used as such. You can also prefix them by ``"collection_"`` and
43
+ they will be treated as HTTP methods for the given collection path
44
+ (collection_path), if any.
45
+
46
+ :param klass:
47
+ The class (resource) on which to register the service.
48
+
49
+ :param depth:
50
+ Which frame should be looked in default 2.
51
+
52
+ :param kw:
53
+ Keyword arguments configuring the resource.
54
+
55
+
56
+ Here is an example:
57
+
58
+ .. code-block:: python
59
+
60
+ class User(object):
61
+ pass
62
+
63
+ add_resource(User, collection_path='/users', path='/users/{id}')
64
+
65
+ Alternatively if you want to reuse your existing pyramid routes:
66
+
67
+ .. code-block:: python
68
+
69
+ class User(object):
70
+ pass
71
+
72
+ add_resource(User, collection_pyramid_route='users',
73
+ pyramid_route='user')
74
+
75
+ """
76
+
77
+ services = {}
78
+
79
+ if ("collection_pyramid_route" in kw or "pyramid_route" in kw) and (
80
+ "collection_path" in kw or "path" in kw
81
+ ):
82
+ raise ValueError("You use either paths or route names, not both")
83
+
84
+ if "collection_path" in kw:
85
+ if kw["collection_path"] == kw["path"]:
86
+ msg = "Warning: collection_path and path are not distinct."
87
+ warnings.warn(msg)
88
+
89
+ prefixes = ("", "collection_")
90
+ else:
91
+ prefixes = ("",)
92
+
93
+ if "collection_pyramid_route" in kw:
94
+ if kw["collection_pyramid_route"] == kw["pyramid_route"]:
95
+ msg = "Warning: collection_pyramid_route and pyramid_route are not distinct."
96
+ warnings.warn(msg)
97
+
98
+ prefixes = ("", "collection_")
99
+
100
+ for prefix in prefixes:
101
+ # get clean view arguments
102
+ service_args = {}
103
+ for k in list(kw):
104
+ if k.startswith("collection_"):
105
+ if prefix == "collection_":
106
+ service_args[k[len(prefix) :]] = kw[k]
107
+ elif k not in service_args:
108
+ service_args[k] = kw[k]
109
+
110
+ # auto-wire klass as its own view factory, unless one
111
+ # is explicitly declared.
112
+ if "factory" not in kw:
113
+ service_args["factory"] = klass
114
+
115
+ # create service
116
+ service_name = service_args.pop("name", None) or klass.__name__.lower()
117
+ service_name = prefix + service_name
118
+ service = services[service_name] = Service(name=service_name, depth=depth, **service_args)
119
+ # ensure the service comes with the same properties as the wrapped
120
+ # resource
121
+ functools.update_wrapper(service, klass)
122
+
123
+ # initialize views
124
+ for verb in ("get", "post", "put", "delete", "options", "patch"):
125
+ view_attr = prefix + verb
126
+ meth = getattr(klass, view_attr, None)
127
+
128
+ if meth is not None:
129
+ # if the method has a __views__ arguments, then it had
130
+ # been decorated by a @view decorator. get back the name of
131
+ # the decorated method so we can register it properly
132
+ views = getattr(meth, "__views__", [])
133
+ if views:
134
+ for view_args in views:
135
+ service.add_view(verb, view_attr, klass=klass, **view_args)
136
+ else:
137
+ service.add_view(verb, view_attr, klass=klass)
138
+
139
+ setattr(klass, "_services", services)
140
+
141
+ def callback(context, name, ob):
142
+ # get the callbacks registered by the inner services
143
+ # and call them from here when the @resource classes are being
144
+ # scanned by venusian.
145
+ for service in services.values():
146
+ config = context.config.with_package(info.module)
147
+ config.add_cornice_service(service)
148
+
149
+ info = venusian.attach(klass, callback, category="pyramid", depth=depth)
150
+
151
+ return klass
152
+
153
+
154
+ def view(**kw):
155
+ """Method decorator to store view arguments when defining a resource with
156
+ the @resource class decorator
157
+
158
+ :param kw:
159
+ Keyword arguments configuring the view.
160
+ """
161
+
162
+ def wrapper(func):
163
+ return add_view(func, **kw)
164
+
165
+ return wrapper
166
+
167
+
168
+ def add_view(func, **kw):
169
+ """Method to store view arguments when defining a resource with
170
+ the add_resource class method
171
+
172
+ :param func:
173
+ The func to hook to
174
+
175
+ :param kw:
176
+ Keyword arguments configuring the view.
177
+
178
+ Example:
179
+
180
+ .. code-block:: python
181
+
182
+ class User(object):
183
+
184
+ def __init__(self, request):
185
+ self.request = request
186
+
187
+ def collection_get(self):
188
+ return {'users': _USERS.keys()}
189
+
190
+ def get(self):
191
+ return _USERS.get(int(self.request.matchdict['id']))
192
+
193
+ add_view(User.get, renderer='json')
194
+ add_resource(User, collection_path='/users', path='/users/{id}')
195
+ """
196
+ # XXX needed in py2 to set on instancemethod
197
+ if hasattr(func, "__func__"): # pragma: no cover
198
+ func = func.__func__
199
+ # store view argument to use them later in @resource
200
+ views = getattr(func, "__views__", None)
201
+ if views is None:
202
+ views = []
203
+ setattr(func, "__views__", views)
204
+ views.append(kw)
205
+ return func