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.
Files changed (46) hide show
  1. {py2http-0.1.65 → py2http-0.1.67}/PKG-INFO +1 -1
  2. {py2http-0.1.65 → py2http-0.1.67}/py2http/default_configs.py +41 -23
  3. {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/object_dispatch.py +5 -0
  4. {py2http-0.1.65 → py2http-0.1.67}/py2http/service.py +498 -33
  5. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_attr_dispatch.py +5 -0
  6. py2http-0.1.67/py2http/tests/test_instance_endpoints.py +240 -0
  7. py2http-0.1.67/py2http/tests/test_obj_ownership.py +253 -0
  8. {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/PKG-INFO +1 -1
  9. {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/SOURCES.txt +2 -0
  10. {py2http-0.1.65 → py2http-0.1.67}/setup.cfg +1 -1
  11. {py2http-0.1.65 → py2http-0.1.67}/LICENSE +0 -0
  12. {py2http-0.1.65 → py2http-0.1.67}/README.md +0 -0
  13. {py2http-0.1.65 → py2http-0.1.67}/py2http/__init__.py +0 -0
  14. {py2http-0.1.65 → py2http-0.1.67}/py2http/bottle_plugins.py +0 -0
  15. {py2http-0.1.65 → py2http-0.1.67}/py2http/config.py +0 -0
  16. {py2http-0.1.65 → py2http-0.1.67}/py2http/constants.py +0 -0
  17. {py2http-0.1.65 → py2http-0.1.67}/py2http/decorators.py +0 -0
  18. {py2http-0.1.65 → py2http-0.1.67}/py2http/diagnosis.py +0 -0
  19. {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/__init__.py +0 -0
  20. {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/custom_input_mapper.py +0 -0
  21. {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/custom_input_mappers.py +0 -0
  22. {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/example_service.py +0 -0
  23. {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/example_service_bottle.py +0 -0
  24. {py2http-0.1.65 → py2http-0.1.67}/py2http/examples/example_service_flask.py +0 -0
  25. {py2http-0.1.65 → py2http-0.1.67}/py2http/middleware.py +0 -0
  26. {py2http-0.1.65 → py2http-0.1.67}/py2http/openapi_utils.py +0 -0
  27. {py2http-0.1.65 → py2http-0.1.67}/py2http/route_utils.py +0 -0
  28. {py2http-0.1.65 → py2http-0.1.67}/py2http/schema_tools.py +0 -0
  29. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/__init__.py +0 -0
  30. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/example_service_test.py +0 -0
  31. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/objects_for_testing.py +0 -0
  32. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/simple_run_process_test.py +0 -0
  33. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_binary_req.py +0 -0
  34. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_decorators.py +0 -0
  35. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_flask.py +0 -0
  36. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_jwt_middleware.py +0 -0
  37. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_not_set_defaults.py +0 -0
  38. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/test_p2h2p.py +0 -0
  39. {py2http-0.1.65 → py2http-0.1.67}/py2http/tests/utils_for_testing.py +0 -0
  40. {py2http-0.1.65 → py2http-0.1.67}/py2http/types.py +0 -0
  41. {py2http-0.1.65 → py2http-0.1.67}/py2http/util.py +0 -0
  42. {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/dependency_links.txt +0 -0
  43. {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/not-zip-safe +0 -0
  44. {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/requires.txt +0 -0
  45. {py2http-0.1.65 → py2http-0.1.67}/py2http.egg-info/top_level.txt +0 -0
  46. {py2http-0.1.65 → py2http-0.1.67}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py2http
3
- Version: 0.1.65
3
+ Version: 0.1.67
4
4
  Summary: Declarative HTTP service entry point.
5
5
  Home-page: https://github.com/i2mint/py2http
6
6
  License: MIT
@@ -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
- message = str(error)
64
- if isinstance(error, (AuthorizationError, InputError, DuplicateRecordError)):
65
- _raise_http_client_error(
66
- web.HTTPBadRequest, message, reason=type(error).__name__
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
- if isinstance(error, (AuthorizationError, InputError, DuplicateRecordError)):
80
- response.status = f"400 {type(error).__name__}"
81
- # response.reason = type(error).__name__
82
- elif isinstance(error, ForbiddenError):
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