py2http 0.1.65__tar.gz → 0.1.67__tar.gz
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.
- {py2http-0.1.65 → py2http-0.1.67}/PKG-INFO +1 -1
- {py2http-0.1.65 → py2http-0.1.67}/py2http/default_configs.py +41 -23
- {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/object_dispatch.py +5 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/service.py +498 -33
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_attr_dispatch.py +5 -0
- py2http-0.1.67/py2http/tests/test_instance_endpoints.py +240 -0
- py2http-0.1.67/py2http/tests/test_obj_ownership.py +253 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/PKG-INFO +1 -1
- {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/SOURCES.txt +2 -0
- {py2http-0.1.65 → py2http-0.1.67}/setup.cfg +1 -1
- {py2http-0.1.65 → py2http-0.1.67}/LICENSE +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/README.md +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/__init__.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/bottle_plugins.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/config.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/constants.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/decorators.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/diagnosis.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/__init__.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/custom_input_mapper.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/custom_input_mappers.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/example_service.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/example_service_bottle.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/example_service_flask.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/middleware.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/openapi_utils.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/route_utils.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/schema_tools.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/__init__.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/example_service_test.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/objects_for_testing.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/simple_run_process_test.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_binary_req.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_decorators.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_flask.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_jwt_middleware.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_not_set_defaults.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_p2h2p.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/utils_for_testing.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/types.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http/util.py +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/dependency_links.txt +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/not-zip-safe +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/requires.txt +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/top_level.txt +0 -0
- {py2http-0.1.65 → py2http-0.1.67}/setup.py +0 -0
|
@@ -23,6 +23,7 @@ from i2.errors import (
|
|
|
23
23
|
InputError,
|
|
24
24
|
NotFoundError,
|
|
25
25
|
DuplicateRecordError,
|
|
26
|
+
OverwritesNotAllowed,
|
|
26
27
|
)
|
|
27
28
|
|
|
28
29
|
from py2http.decorators import handle_json_req, send_json_resp, JsonRespEncoder
|
|
@@ -59,34 +60,51 @@ def _raise_http_client_error(error, message, reason=None):
|
|
|
59
60
|
)
|
|
60
61
|
|
|
61
62
|
|
|
63
|
+
# HTTP status of each client error, most specific class first. ``ForbiddenError`` and
|
|
64
|
+
# ``OverwritesNotAllowed`` mean the client may not do this (403); any other
|
|
65
|
+
# ``AuthorizationError`` means the request did not say who it is (401).
|
|
66
|
+
_CLIENT_ERROR_STATUSES = (
|
|
67
|
+
((ForbiddenError, OverwritesNotAllowed), 403),
|
|
68
|
+
(AuthorizationError, 401),
|
|
69
|
+
((InputError, DuplicateRecordError), 400),
|
|
70
|
+
(NotFoundError, 404),
|
|
71
|
+
)
|
|
72
|
+
_AIOHTTP_ERRORS = {
|
|
73
|
+
400: web.HTTPBadRequest,
|
|
74
|
+
401: web.HTTPUnauthorized,
|
|
75
|
+
403: web.HTTPForbidden,
|
|
76
|
+
404: web.HTTPNotFound,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def error_status(error: Exception) -> int:
|
|
81
|
+
"""The HTTP status for an error a handler raised (500 when it is not a client's).
|
|
82
|
+
|
|
83
|
+
>>> error_status(AuthorizationError()), error_status(ForbiddenError())
|
|
84
|
+
(401, 403)
|
|
85
|
+
>>> error_status(InputError()), error_status(KeyError())
|
|
86
|
+
(400, 500)
|
|
87
|
+
"""
|
|
88
|
+
for error_types, status in _CLIENT_ERROR_STATUSES:
|
|
89
|
+
if isinstance(error, error_types):
|
|
90
|
+
return status
|
|
91
|
+
return 500
|
|
92
|
+
|
|
93
|
+
|
|
62
94
|
def aiohttp_error_handler(error: Exception):
|
|
63
|
-
|
|
64
|
-
if
|
|
65
|
-
_raise_http_client_error(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
elif isinstance(error, ForbiddenError):
|
|
69
|
-
_raise_http_client_error(web.HTTPForbidden, message)
|
|
70
|
-
elif isinstance(error, NotFoundError):
|
|
71
|
-
_raise_http_client_error(web.HTTPNotFound, message)
|
|
72
|
-
else:
|
|
73
|
-
message = "Internal server error"
|
|
74
|
-
_raise_http_client_error(web.HTTPInternalServerError, message)
|
|
95
|
+
status = error_status(error)
|
|
96
|
+
if status == 500:
|
|
97
|
+
_raise_http_client_error(web.HTTPInternalServerError, "Internal server error")
|
|
98
|
+
reason = type(error).__name__ if status == 400 else None
|
|
99
|
+
_raise_http_client_error(_AIOHTTP_ERRORS[status], str(error), reason=reason)
|
|
75
100
|
|
|
76
101
|
|
|
77
102
|
def bottle_error_handler(error: Exception):
|
|
78
103
|
message = str(error)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
response.status = 403
|
|
84
|
-
elif isinstance(error, NotFoundError):
|
|
85
|
-
response.status = 404
|
|
86
|
-
else:
|
|
87
|
-
response.status = 500
|
|
88
|
-
if os.getenv("OPAQUE_ERRORS", None):
|
|
89
|
-
message = "Internal server error"
|
|
104
|
+
status = error_status(error)
|
|
105
|
+
response.status = f"400 {type(error).__name__}" if status == 400 else status
|
|
106
|
+
if status == 500 and os.getenv("OPAQUE_ERRORS", None):
|
|
107
|
+
message = "Internal server error"
|
|
90
108
|
return {"error": message}
|
|
91
109
|
|
|
92
110
|
|
|
@@ -11,6 +11,8 @@ and its ecosystem (mongodol, s3dol, etc.).
|
|
|
11
11
|
|
|
12
12
|
from typing import MutableMapping
|
|
13
13
|
|
|
14
|
+
from py2http.service import shared_objects
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
class BackendStore(MutableMapping):
|
|
16
18
|
"""MutableMapping wrapper that assumes that values are given as strings,
|
|
@@ -47,6 +49,9 @@ handlers = [
|
|
|
47
49
|
endpoint=backend_store,
|
|
48
50
|
name="backend_store",
|
|
49
51
|
attr_names=["__iter__", "__getitem__", "__setitem__"],
|
|
52
|
+
# One store every client shares and may write to: fine for a demo. A real
|
|
53
|
+
# service passes obj_owner=<(request) -> authenticated user id> instead.
|
|
54
|
+
obj_owner=shared_objects,
|
|
50
55
|
),
|
|
51
56
|
]
|
|
52
57
|
|