DefEssential 0.1.8__25-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.
Files changed (104) hide show
  1. DefEssential/Adapter/Implements/Gunicorn/Server.py +152 -0
  2. DefEssential/Adapter/Implements/Gunicorn/Transport.py +171 -0
  3. DefEssential/Adapter/Implements/Gunicorn/__init__.py +5 -0
  4. DefEssential/Adapter/Implements/Python/Server.py +273 -0
  5. DefEssential/Adapter/Implements/Python/__init__.py +5 -0
  6. DefEssential/Adapter/Implements/Uvicorn/HTTP.py +256 -0
  7. DefEssential/Adapter/Implements/Uvicorn/Portal.py +65 -0
  8. DefEssential/Adapter/Implements/Uvicorn/Server.py +137 -0
  9. DefEssential/Adapter/Implements/Uvicorn/WebSocket.py +222 -0
  10. DefEssential/Adapter/Implements/Uvicorn/__init__.py +5 -0
  11. DefEssential/Adapter/Implements/__init__.py +3 -0
  12. DefEssential/Adapter/RequestHandler.py +109 -0
  13. DefEssential/Adapter/Server.py +54 -0
  14. DefEssential/Adapter/WSGI.py +219 -0
  15. DefEssential/Adapter/__init__.py +5 -0
  16. DefEssential/Application.py +657 -0
  17. DefEssential/Body.py +50 -0
  18. DefEssential/Content.py +18 -0
  19. DefEssential/Credentials.py +245 -0
  20. DefEssential/CrossOriginResourceSharing.py +107 -0
  21. DefEssential/Descriptor/DataObject.py +409 -0
  22. DefEssential/Descriptor/Types.py +248 -0
  23. DefEssential/Descriptor/Value.py +188 -0
  24. DefEssential/Descriptor/__init__.py +172 -0
  25. DefEssential/Error.py +66 -0
  26. DefEssential/Extension.py +191 -0
  27. DefEssential/Extensions/File.py +95 -0
  28. DefEssential/Extensions/FileSystem.py +195 -0
  29. DefEssential/Extensions/Metrics.py +511 -0
  30. DefEssential/Extensions/Ping.py +93 -0
  31. DefEssential/Extensions/Specifications.py +342 -0
  32. DefEssential/Extensions/Status.html +207 -0
  33. DefEssential/Extensions/Status.py +110 -0
  34. DefEssential/Extensions/Swagger.html +77 -0
  35. DefEssential/Extensions/Swagger.py +117 -0
  36. DefEssential/Extensions/Utils/Routes.py +42 -0
  37. DefEssential/Extensions/Utils/__init__.py +43 -0
  38. DefEssential/Extensions/__init__.py +21 -0
  39. DefEssential/IO/Readers/ChunkedStream.py +106 -0
  40. DefEssential/IO/Readers/ContentReader.py +36 -0
  41. DefEssential/IO/Readers/ContentReaders.py +241 -0
  42. DefEssential/IO/Readers/__init__.py +25 -0
  43. DefEssential/IO/RequestReader.py +125 -0
  44. DefEssential/IO/ResponseWriter.py +242 -0
  45. DefEssential/IO/WebSocket/WebSocket.py +288 -0
  46. DefEssential/IO/WebSocket/WebSocketError.py +17 -0
  47. DefEssential/IO/WebSocket/WebSocketFrame.py +68 -0
  48. DefEssential/IO/WebSocket/WebSocketFrameReader.py +98 -0
  49. DefEssential/IO/WebSocket/WebSocketHandshake.py +94 -0
  50. DefEssential/IO/WebSocket/__init__.py +13 -0
  51. DefEssential/IO/Writers/ChunkedStreamWriter.py +19 -0
  52. DefEssential/IO/Writers/ServerSentEvents.py +41 -0
  53. DefEssential/IO/Writers/ServerSentEventsWriter.py +67 -0
  54. DefEssential/IO/Writers/StreamWriter.py +57 -0
  55. DefEssential/IO/Writers/__init__.py +11 -0
  56. DefEssential/IO/__init__.py +7 -0
  57. DefEssential/Request.py +110 -0
  58. DefEssential/RequestRunner.py +261 -0
  59. DefEssential/Response.py +89 -0
  60. DefEssential/ResponseSpecification.py +23 -0
  61. DefEssential/Responses/ResponseFile.py +108 -0
  62. DefEssential/Responses/__init__.py +63 -0
  63. DefEssential/Route.py +55 -0
  64. DefEssential/Router.py +48 -0
  65. DefEssential/Runners/RunExtension.py +116 -0
  66. DefEssential/Runners/RunFactory.py +26 -0
  67. DefEssential/Runners/RunOptions.py +77 -0
  68. DefEssential/Runners/RunRequest.py +198 -0
  69. DefEssential/Runners/__init__.py +5 -0
  70. DefEssential/Startup.py +104 -0
  71. DefEssential/Utils/FileSystemObject/Configuration.py +15 -0
  72. DefEssential/Utils/FileSystemObject/Connection.py +57 -0
  73. DefEssential/Utils/FileSystemObject/File.py +41 -0
  74. DefEssential/Utils/FileSystemObject/Implements/FileSystem/Configuration.py +15 -0
  75. DefEssential/Utils/FileSystemObject/Implements/FileSystem/Connection.py +115 -0
  76. DefEssential/Utils/FileSystemObject/Implements/FileSystem/File.py +77 -0
  77. DefEssential/Utils/FileSystemObject/Implements/FileSystem/__init__.py +11 -0
  78. DefEssential/Utils/FileSystemObject/Implements/__init__.py +3 -0
  79. DefEssential/Utils/FileSystemObject/__init__.py +11 -0
  80. DefEssential/Utils/Loader.py +81 -0
  81. DefEssential/Utils/__init__.py +3 -0
  82. DefEssential/Validator/Pattern.py +36 -0
  83. DefEssential/Validator/Patterns/Array.py +30 -0
  84. DefEssential/Validator/Patterns/Common.py +92 -0
  85. DefEssential/Validator/Patterns/Compare.py +180 -0
  86. DefEssential/Validator/Patterns/Condition.py +60 -0
  87. DefEssential/Validator/Patterns/DataObject.py +85 -0
  88. DefEssential/Validator/Patterns/DateTime.py +111 -0
  89. DefEssential/Validator/Patterns/Number.py +43 -0
  90. DefEssential/Validator/Patterns/Object.py +150 -0
  91. DefEssential/Validator/Patterns/Size.py +110 -0
  92. DefEssential/Validator/Patterns/String.py +85 -0
  93. DefEssential/Validator/Patterns/Type.py +350 -0
  94. DefEssential/Validator/Patterns/__init__.py +137 -0
  95. DefEssential/Validator/Validate.py +29 -0
  96. DefEssential/Validator/Validator.py +30 -0
  97. DefEssential/Validator/__init__.py +11 -0
  98. DefEssential/Validators.py +255 -0
  99. DefEssential/__init__.py +83 -0
  100. defessential-0.1.8.dist-info/METADATA +86 -0
  101. defessential-0.1.8.dist-info/RECORD +104 -0
  102. defessential-0.1.8.dist-info/WHEEL +6 -0
  103. defessential-0.1.8.dist-info/licenses/LICENSE +674 -0
  104. defessential-0.1.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,18 @@
1
+ from dataclasses import dataclass
2
+ from typing import (
3
+ Any,
4
+ Optional,
5
+ )
6
+
7
+ __all__ = (
8
+ 'Content',
9
+ )
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class Content:
14
+ """Describe input or output content formats, schemas, and examples."""
15
+
16
+ format: str
17
+ schema: Optional[object] = None
18
+ example: Any = None
@@ -0,0 +1,245 @@
1
+ from dataclasses import dataclass
2
+ from http.cookies import (
3
+ CookieError,
4
+ SimpleCookie,
5
+ )
6
+ from typing import (
7
+ Any,
8
+ Optional,
9
+ Sequence,
10
+ )
11
+
12
+ from .Error import ClientError
13
+ from .Request import Request
14
+ from .Validators import Credentials
15
+
16
+ __all__ = (
17
+ 'HTTPAuthorization',
18
+ 'Query',
19
+ 'Header',
20
+ 'Cookie',
21
+ 'HTTP',
22
+ 'OAuth2Implicit',
23
+ 'OAuth2Password',
24
+ 'OAuth2ClientCredentials',
25
+ 'OAuth2AuthorizationCode',
26
+ )
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class HTTPAuthorization:
31
+ """Represent the scheme and credentials of an HTTP authentication header."""
32
+
33
+ scheme: str
34
+ credentials: str
35
+
36
+
37
+ class Query(Credentials):
38
+ """Extract a single credential value from the query string."""
39
+
40
+ def __init__(
41
+ self,
42
+ name: str,
43
+ ):
44
+ self.key = name
45
+ return
46
+
47
+ def __call__(
48
+ self,
49
+ request: Request,
50
+ ) -> Optional[str]:
51
+ value = request.qs.get(self.key)
52
+ if value is not None and not isinstance(value, str):
53
+ raise ClientError(
54
+ 'Credential query parameter must have a single value',
55
+ status=401,
56
+ )
57
+ return value
58
+
59
+ @property
60
+ def name(self) -> str:
61
+ return 'Query'
62
+
63
+ @property
64
+ def format(self) -> dict:
65
+ return {
66
+ 'type': 'apiKey',
67
+ 'in': 'query',
68
+ 'name': self.key,
69
+ }
70
+
71
+
72
+ class Header(Query):
73
+ """Extract credentials from the specified header."""
74
+
75
+ def __call__(
76
+ self,
77
+ request: Request,
78
+ ) -> Optional[str]:
79
+ return request.header(self.key)
80
+
81
+ @property
82
+ def name(self) -> str:
83
+ return 'Header'
84
+
85
+ @property
86
+ def format(self) -> dict:
87
+ return {
88
+ 'type': 'apiKey',
89
+ 'in': 'header',
90
+ 'name': self.key,
91
+ }
92
+
93
+ @property
94
+ def headers(self) -> Sequence[str]:
95
+ return (self.key,)
96
+
97
+
98
+ class Cookie(Query):
99
+ """Extract the value of the specified cookie."""
100
+
101
+ def __call__(
102
+ self,
103
+ request: Request,
104
+ ) -> Optional[str]:
105
+ value = request.header('Cookie')
106
+ if value is None:
107
+ return None
108
+ cookies: SimpleCookie = SimpleCookie()
109
+ try:
110
+ cookies.load(value)
111
+ except CookieError as error:
112
+ raise ClientError(
113
+ 'Invalid authentication cookie',
114
+ status=401,
115
+ ) from error
116
+ cookie = cookies.get(self.key)
117
+ return cookie.value if cookie is not None else None
118
+
119
+ @property
120
+ def name(self) -> str:
121
+ return 'Cookie'
122
+
123
+ @property
124
+ def format(self) -> dict:
125
+ return {
126
+ 'type': 'apiKey',
127
+ 'in': 'cookie',
128
+ 'name': self.key,
129
+ }
130
+
131
+
132
+ class HTTP(Credentials):
133
+ """Parse the Authorization header and verify the specified scheme."""
134
+
135
+ def __init__(
136
+ self,
137
+ scheme: str,
138
+ format: Optional[str] = None,
139
+ ):
140
+ self.scheme = scheme
141
+ self.fmt = format
142
+ return
143
+
144
+ def __call__(
145
+ self,
146
+ request: Request,
147
+ ) -> Optional[HTTPAuthorization]:
148
+ value = request.header('Authorization')
149
+ if value is None:
150
+ return None
151
+ scheme, separator, credentials = value.partition(' ')
152
+ if not separator or not credentials.strip() or scheme.lower() != self.scheme.lower():
153
+ raise ClientError(
154
+ 'Invalid authentication scheme or credentials',
155
+ status=401,
156
+ )
157
+ return HTTPAuthorization(
158
+ scheme=scheme,
159
+ credentials=credentials.strip(),
160
+ )
161
+
162
+ @property
163
+ def name(self) -> str:
164
+ return 'HTTP'
165
+
166
+ @property
167
+ def format(self) -> dict:
168
+ value = {
169
+ 'type': 'http',
170
+ 'scheme': self.scheme.lower(),
171
+ }
172
+ if self.fmt is not None:
173
+ value['bearerFormat'] = self.fmt
174
+ return value
175
+
176
+ @property
177
+ def headers(self) -> Sequence[str]:
178
+ return ('Authorization',)
179
+
180
+
181
+ class OAuth2Implicit(HTTP):
182
+ """Configure bearer authentication metadata for the OAuth2 implicit flow."""
183
+
184
+ flow = 'implicit'
185
+
186
+ def __init__(
187
+ self,
188
+ authorizationUrl: Optional[str] = None,
189
+ ):
190
+ super().__init__('Bearer')
191
+ self.options: dict[str, Any] = {'scopes': {}}
192
+ if authorizationUrl is not None:
193
+ self.options['authorizationUrl'] = authorizationUrl
194
+ return
195
+
196
+ @property
197
+ def name(self) -> str:
198
+ return type(self).__name__
199
+
200
+ @property
201
+ def format(self) -> dict:
202
+ return {
203
+ 'type': 'oauth2',
204
+ 'flows': {self.flow: dict(self.options)},
205
+ }
206
+
207
+
208
+ class OAuth2Password(OAuth2Implicit):
209
+ """Configure token and refresh URLs for the OAuth2 password flow."""
210
+
211
+ flow = 'password'
212
+
213
+ def __init__(
214
+ self,
215
+ tokenUrl: Optional[str] = None,
216
+ refreshUrl: Optional[str] = None,
217
+ ):
218
+ super().__init__()
219
+ if tokenUrl is not None:
220
+ self.options['tokenUrl'] = tokenUrl
221
+ if refreshUrl is not None:
222
+ self.options['refreshUrl'] = refreshUrl
223
+ return
224
+
225
+
226
+ class OAuth2ClientCredentials(OAuth2Password):
227
+ """Represent the OAuth2 clientCredentials flow."""
228
+
229
+ flow = 'clientCredentials'
230
+
231
+
232
+ class OAuth2AuthorizationCode(OAuth2Password):
233
+ """Configure authorization and token URLs for the OAuth2 authorizationCode flow."""
234
+
235
+ flow = 'authorizationCode'
236
+
237
+ def __init__(
238
+ self,
239
+ authorizationUrl: Optional[str] = None,
240
+ tokenUrl: Optional[str] = None,
241
+ ):
242
+ super().__init__(tokenUrl=tokenUrl)
243
+ if authorizationUrl is not None:
244
+ self.options['authorizationUrl'] = authorizationUrl
245
+ return
@@ -0,0 +1,107 @@
1
+ from typing import Sequence
2
+
3
+ from .Error import ClientError
4
+ from .Request import Request
5
+ from .RequestRunner import RequestProperties
6
+ from .Response import Response
7
+ from .Validators import (
8
+ Auth,
9
+ Headers,
10
+ Origin,
11
+ )
12
+
13
+ __all__ = (
14
+ 'CrossOriginResourceSharing',
15
+ )
16
+
17
+
18
+ class CrossOriginResourceSharing:
19
+ """Apply the Cross-Origin Resource Sharing policy configured for a request."""
20
+
21
+ def __init__(
22
+ self,
23
+ properties: RequestProperties,
24
+ ):
25
+ self.request_properties = properties
26
+ return
27
+
28
+ def origin_policies(self) -> Sequence[Origin]:
29
+ value = self.request_properties.origin
30
+ if isinstance(value, Origin):
31
+ return (value,)
32
+ if isinstance(value, Sequence):
33
+ return tuple(item for item in value if isinstance(item, Origin))
34
+ return ()
35
+
36
+ def has_origin_policy(self) -> bool:
37
+ return bool(self.origin_policies())
38
+
39
+ def validate_origin(
40
+ self,
41
+ request: Request,
42
+ ) -> None:
43
+ policies = self.origin_policies()
44
+ if policies:
45
+ if any(policy.allows(request.header('Origin')) for policy in policies):
46
+ return
47
+ error = next((policy.error for policy in policies if policy.error is not None), None)
48
+ raise error if error is not None else ClientError(
49
+ 'Origin not allowed',
50
+ status=403,
51
+ )
52
+ return
53
+
54
+ def allowed_request_headers(self) -> list[str]:
55
+ headers = []
56
+ if isinstance(self.request_properties.headers, Headers):
57
+ headers.extend(self.request_properties.headers.headers)
58
+ if isinstance(self.request_properties.auth, Auth):
59
+ headers.extend(self.request_properties.auth.credentials.headers)
60
+ if self.request_properties.body is not None:
61
+ headers.append('Content-Type')
62
+ return list(dict.fromkeys(headers))
63
+
64
+ def apply_to_response(
65
+ self,
66
+ request: Request,
67
+ response: Response,
68
+ *,
69
+ preflight: bool = False,
70
+ ) -> Response:
71
+ if not self.has_origin_policy():
72
+ return response
73
+ origin = request.header('Origin')
74
+ if origin is None:
75
+ return response
76
+ self.validate_origin(request)
77
+ response.header(
78
+ 'Access-Control-Allow-Origin',
79
+ origin,
80
+ )
81
+ vary = response.header('Vary')
82
+ fields = [item.strip() for item in vary.split(',')] if vary else []
83
+ if 'origin' not in (item.lower() for item in fields) and '*' not in fields:
84
+ fields.append('Origin')
85
+ response.header(
86
+ 'Vary',
87
+ ', '.join(fields),
88
+ )
89
+ if self.request_properties.auth is not None:
90
+ response.header(
91
+ 'Access-Control-Allow-Credentials',
92
+ 'true',
93
+ )
94
+ if not preflight:
95
+ expose = [
96
+ key for key, _ in response.headers()
97
+ if key.lower() not in (
98
+ 'set-cookie',
99
+ 'content-length',
100
+ ) and not key.lower().startswith('access-control-')
101
+ ]
102
+ if expose:
103
+ response.header(
104
+ 'Access-Control-Expose-Headers',
105
+ ', '.join(expose),
106
+ )
107
+ return response