cavisson-pythonagent 0.0.1__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 (180) hide show
  1. cavisson_pythonagent-0.0.1.dist-info/METADATA +32 -0
  2. cavisson_pythonagent-0.0.1.dist-info/RECORD +180 -0
  3. cavisson_pythonagent-0.0.1.dist-info/WHEEL +5 -0
  4. cavisson_pythonagent-0.0.1.dist-info/licenses/LICENSE +19 -0
  5. cavisson_pythonagent-0.0.1.dist-info/top_level.txt +1 -0
  6. pythonagent/__init__.py +22 -0
  7. pythonagent/agent/__init__.py +219 -0
  8. pythonagent/agent/internal/__init__.py +1 -0
  9. pythonagent/agent/internal/agent.py +1651 -0
  10. pythonagent/agent/internal/framesinfo.py +152 -0
  11. pythonagent/agent/internal/heap_dump.py +39 -0
  12. pythonagent/agent/internal/intercept_module.py +70 -0
  13. pythonagent/agent/internal/logs.py +122 -0
  14. pythonagent/agent/internal/metadata/__init__.py +0 -0
  15. pythonagent/agent/internal/metadata/agent_meta_data.py +276 -0
  16. pythonagent/agent/internal/proc_compat.py +75 -0
  17. pythonagent/agent/internal/profile.py +47 -0
  18. pythonagent/agent/internal/provider.py +83 -0
  19. pythonagent/agent/internal/thread_dump.py +85 -0
  20. pythonagent/agent/internal/udp.py +245 -0
  21. pythonagent/agent/internal/udp_message.py +800 -0
  22. pythonagent/agent/probes/Instrumentation/__init__.py +340 -0
  23. pythonagent/agent/probes/Instrumentation/find.py +243 -0
  24. pythonagent/agent/probes/Instrumentation/module_version_resolver.py +155 -0
  25. pythonagent/agent/probes/Instrumentation/new_parser.py +64 -0
  26. pythonagent/agent/probes/Instrumentation/parser.py +129 -0
  27. pythonagent/agent/probes/__init__.py +219 -0
  28. pythonagent/agent/probes/base.py +303 -0
  29. pythonagent/agent/probes/cache/__init__.py +51 -0
  30. pythonagent/agent/probes/cache/redis.py +119 -0
  31. pythonagent/agent/probes/cache/redis_asyncio.py +83 -0
  32. pythonagent/agent/probes/coroutines/__init__.py +1 -0
  33. pythonagent/agent/probes/coroutines/asyncio.py +63 -0
  34. pythonagent/agent/probes/elasticdb/__init__.py +7 -0
  35. pythonagent/agent/probes/elasticdb/aelastic.py +54 -0
  36. pythonagent/agent/probes/frameworks/__init__.py +27 -0
  37. pythonagent/agent/probes/frameworks/agentprofiler.py +105 -0
  38. pythonagent/agent/probes/frameworks/aiohttp_web.py +155 -0
  39. pythonagent/agent/probes/frameworks/aisess.py +151 -0
  40. pythonagent/agent/probes/frameworks/asgi.py +340 -0
  41. pythonagent/agent/probes/frameworks/bottle.py +27 -0
  42. pythonagent/agent/probes/frameworks/cherry.py +25 -0
  43. pythonagent/agent/probes/frameworks/django.py +128 -0
  44. pythonagent/agent/probes/frameworks/falcon.py +21 -0
  45. pythonagent/agent/probes/frameworks/fastapi.py +35 -0
  46. pythonagent/agent/probes/frameworks/flask.py +30 -0
  47. pythonagent/agent/probes/frameworks/pyramid.py +56 -0
  48. pythonagent/agent/probes/frameworks/test.py +108 -0
  49. pythonagent/agent/probes/frameworks/tornado_async_web.py +117 -0
  50. pythonagent/agent/probes/frameworks/tornado_web.py +133 -0
  51. pythonagent/agent/probes/frameworks/wsgi.py +353 -0
  52. pythonagent/agent/probes/grpc/__init__.py +76 -0
  53. pythonagent/agent/probes/grpc/client_interceptor.py +132 -0
  54. pythonagent/agent/probes/grpc/server_interceptor.py +129 -0
  55. pythonagent/agent/probes/havoc/__init__.py +0 -0
  56. pythonagent/agent/probes/havoc/custom_memory_stress.py +186 -0
  57. pythonagent/agent/probes/havoc/custom_thread_stress.py +187 -0
  58. pythonagent/agent/probes/havoc/havoc_constants.py +218 -0
  59. pythonagent/agent/probes/havoc/havoc_manager.py +981 -0
  60. pythonagent/agent/probes/http/__init__.py +49 -0
  61. pythonagent/agent/probes/http/aiohttp_client.py +59 -0
  62. pythonagent/agent/probes/http/boto.py +12 -0
  63. pythonagent/agent/probes/http/httplib.py +110 -0
  64. pythonagent/agent/probes/http/httpx_client.py +116 -0
  65. pythonagent/agent/probes/http/requests.py +15 -0
  66. pythonagent/agent/probes/http/tornado_httpclient.py +85 -0
  67. pythonagent/agent/probes/http/urllib3.py +16 -0
  68. pythonagent/agent/probes/langchain/__init__.py +21 -0
  69. pythonagent/agent/probes/langchain/base_tool.py +95 -0
  70. pythonagent/agent/probes/langchain/langchain_community.py +136 -0
  71. pythonagent/agent/probes/langchain/langchain_core.py +32 -0
  72. pythonagent/agent/probes/langchain/langchain_openai.py +110 -0
  73. pythonagent/agent/probes/logging/__init__.py +106 -0
  74. pythonagent/agent/probes/message_brokers/__init__.py +4 -0
  75. pythonagent/agent/probes/message_brokers/pika.py +126 -0
  76. pythonagent/agent/probes/mongodb/__init__.py +6 -0
  77. pythonagent/agent/probes/mongodb/pymongo.py +286 -0
  78. pythonagent/agent/probes/openai/__init__.py +3 -0
  79. pythonagent/agent/probes/openai/openai.py +797 -0
  80. pythonagent/agent/probes/span.py +101 -0
  81. pythonagent/agent/probes/sql/__init__.py +13 -0
  82. pythonagent/agent/probes/sql/botocores3.py +51 -0
  83. pythonagent/agent/probes/sql/dbapi.py +285 -0
  84. pythonagent/agent/probes/sql/dynamodb.py +90 -0
  85. pythonagent/agent/probes/sql/mysql_connector.py +24 -0
  86. pythonagent/agent/probes/sql/mysql_connector_cext.py +24 -0
  87. pythonagent/agent/probes/sql/mysqldb.py +43 -0
  88. pythonagent/agent/probes/sql/psycopg2.py +174 -0
  89. pythonagent/agent/probes/sql/pymysql.py +25 -0
  90. pythonagent/bootstrap/__init__.py +0 -0
  91. pythonagent/bootstrap/cav_gunicorn.py +26 -0
  92. pythonagent/bootstrap/cavagent_lambda_wrapper.py +291 -0
  93. pythonagent/bootstrap/run.py +47 -0
  94. pythonagent/bootstrap/sitecustomize.py +287 -0
  95. pythonagent/cavisson/netdiagnostics/CavAgent/instrumentationprofile.json +26 -0
  96. pythonagent/cavisson/netdiagnostics/CavAgent/interceptor_points.txt +29 -0
  97. pythonagent/cavisson/netdiagnostics/python/CavAgent/instrumentationprofile.json +42 -0
  98. pythonagent/cavisson/netdiagnostics/python/CavAgent/interceptor_points.txt +29 -0
  99. pythonagent/cavisson/netdiagnostics/python/config/ndsettings.conf +6 -0
  100. pythonagent/config.py +279 -0
  101. pythonagent/find.py +72 -0
  102. pythonagent/find_mod_cls_name.py +54 -0
  103. pythonagent/lang.py +131 -0
  104. pythonagent/lib.py +91 -0
  105. pythonagent/main/__init__.py +0 -0
  106. pythonagent/main/pytrace/__init__.py +79 -0
  107. pythonagent/main/pytrace/commands/__init__.py +0 -0
  108. pythonagent/main/pytrace/commands/auto_discovery.py +25 -0
  109. pythonagent/main/pytrace/commands/run.py +401 -0
  110. pythonagent/main/pytrace/pytrace.py +133 -0
  111. pythonagent/main/wsgi.py +6 -0
  112. pythonagent/main.py +27 -0
  113. pythonagent/run.py +46 -0
  114. pythonagent/sqins.py +8 -0
  115. pythonagent/test.py +73 -0
  116. pythonagent/utils.py +168 -0
  117. pythonagent/vendor/__init__.py +0 -0
  118. pythonagent/vendor/pympler/__init__.py +1 -0
  119. pythonagent/vendor/pympler/asizeof.py +2810 -0
  120. pythonagent/vendor/pympler/charts.py +62 -0
  121. pythonagent/vendor/pympler/classtracker.py +590 -0
  122. pythonagent/vendor/pympler/classtracker_stats.py +780 -0
  123. pythonagent/vendor/pympler/garbagegraph.py +80 -0
  124. pythonagent/vendor/pympler/mprofile.py +97 -0
  125. pythonagent/vendor/pympler/muppy.py +275 -0
  126. pythonagent/vendor/pympler/panels.py +115 -0
  127. pythonagent/vendor/pympler/process.py +238 -0
  128. pythonagent/vendor/pympler/py.typed +0 -0
  129. pythonagent/vendor/pympler/refbrowser.py +451 -0
  130. pythonagent/vendor/pympler/refgraph.py +350 -0
  131. pythonagent/vendor/pympler/summary.py +321 -0
  132. pythonagent/vendor/pympler/tracker.py +267 -0
  133. pythonagent/vendor/pympler/util/__init__.py +0 -0
  134. pythonagent/vendor/pympler/util/bottle.py +3809 -0
  135. pythonagent/vendor/pympler/util/compat.py +23 -0
  136. pythonagent/vendor/pympler/util/stringutils.py +77 -0
  137. pythonagent/vendor/pympler/web.py +346 -0
  138. pythonagent/vendor/werkzeug/__init__.py +20 -0
  139. pythonagent/vendor/werkzeug/_compat.py +228 -0
  140. pythonagent/vendor/werkzeug/_internal.py +473 -0
  141. pythonagent/vendor/werkzeug/_reloader.py +341 -0
  142. pythonagent/vendor/werkzeug/datastructures.py +3120 -0
  143. pythonagent/vendor/werkzeug/debug/__init__.py +498 -0
  144. pythonagent/vendor/werkzeug/debug/console.py +218 -0
  145. pythonagent/vendor/werkzeug/debug/repr.py +297 -0
  146. pythonagent/vendor/werkzeug/debug/tbtools.py +628 -0
  147. pythonagent/vendor/werkzeug/exceptions.py +829 -0
  148. pythonagent/vendor/werkzeug/filesystem.py +64 -0
  149. pythonagent/vendor/werkzeug/formparser.py +584 -0
  150. pythonagent/vendor/werkzeug/http.py +1307 -0
  151. pythonagent/vendor/werkzeug/local.py +420 -0
  152. pythonagent/vendor/werkzeug/middleware/__init__.py +25 -0
  153. pythonagent/vendor/werkzeug/middleware/dispatcher.py +66 -0
  154. pythonagent/vendor/werkzeug/middleware/http_proxy.py +219 -0
  155. pythonagent/vendor/werkzeug/middleware/lint.py +408 -0
  156. pythonagent/vendor/werkzeug/middleware/profiler.py +132 -0
  157. pythonagent/vendor/werkzeug/middleware/proxy_fix.py +169 -0
  158. pythonagent/vendor/werkzeug/middleware/shared_data.py +293 -0
  159. pythonagent/vendor/werkzeug/posixemulation.py +117 -0
  160. pythonagent/vendor/werkzeug/routing.py +2210 -0
  161. pythonagent/vendor/werkzeug/security.py +249 -0
  162. pythonagent/vendor/werkzeug/serving.py +1117 -0
  163. pythonagent/vendor/werkzeug/test.py +1123 -0
  164. pythonagent/vendor/werkzeug/testapp.py +241 -0
  165. pythonagent/vendor/werkzeug/urls.py +1138 -0
  166. pythonagent/vendor/werkzeug/useragents.py +202 -0
  167. pythonagent/vendor/werkzeug/utils.py +778 -0
  168. pythonagent/vendor/werkzeug/wrappers/__init__.py +36 -0
  169. pythonagent/vendor/werkzeug/wrappers/accept.py +50 -0
  170. pythonagent/vendor/werkzeug/wrappers/auth.py +33 -0
  171. pythonagent/vendor/werkzeug/wrappers/base_request.py +673 -0
  172. pythonagent/vendor/werkzeug/wrappers/base_response.py +700 -0
  173. pythonagent/vendor/werkzeug/wrappers/common_descriptors.py +341 -0
  174. pythonagent/vendor/werkzeug/wrappers/cors.py +100 -0
  175. pythonagent/vendor/werkzeug/wrappers/etag.py +304 -0
  176. pythonagent/vendor/werkzeug/wrappers/json.py +145 -0
  177. pythonagent/vendor/werkzeug/wrappers/request.py +49 -0
  178. pythonagent/vendor/werkzeug/wrappers/response.py +84 -0
  179. pythonagent/vendor/werkzeug/wrappers/user_agent.py +14 -0
  180. pythonagent/vendor/werkzeug/wsgi.py +1000 -0
@@ -0,0 +1,304 @@
1
+ from .._compat import string_types
2
+ from .._internal import _get_environ
3
+ from ..datastructures import ContentRange
4
+ from ..datastructures import RequestCacheControl
5
+ from ..datastructures import ResponseCacheControl
6
+ from ..http import generate_etag
7
+ from ..http import http_date
8
+ from ..http import is_resource_modified
9
+ from ..http import parse_cache_control_header
10
+ from ..http import parse_content_range_header
11
+ from ..http import parse_date
12
+ from ..http import parse_etags
13
+ from ..http import parse_if_range_header
14
+ from ..http import parse_range_header
15
+ from ..http import quote_etag
16
+ from ..http import unquote_etag
17
+ from ..utils import cached_property
18
+ from ..utils import header_property
19
+ from ..wrappers.base_response import _clean_accept_ranges
20
+ from ..wsgi import _RangeWrapper
21
+
22
+
23
+ class ETagRequestMixin(object):
24
+ """Add entity tag and cache descriptors to a request object or object with
25
+ a WSGI environment available as :attr:`~BaseRequest.environ`. This not
26
+ only provides access to etags but also to the cache control header.
27
+ """
28
+
29
+ @cached_property
30
+ def cache_control(self):
31
+ """A :class:`~werkzeug.datastructures.RequestCacheControl` object
32
+ for the incoming cache control headers.
33
+ """
34
+ cache_control = self.environ.get("HTTP_CACHE_CONTROL")
35
+ return parse_cache_control_header(cache_control, None, RequestCacheControl)
36
+
37
+ @cached_property
38
+ def if_match(self):
39
+ """An object containing all the etags in the `If-Match` header.
40
+
41
+ :rtype: :class:`~werkzeug.datastructures.ETags`
42
+ """
43
+ return parse_etags(self.environ.get("HTTP_IF_MATCH"))
44
+
45
+ @cached_property
46
+ def if_none_match(self):
47
+ """An object containing all the etags in the `If-None-Match` header.
48
+
49
+ :rtype: :class:`~werkzeug.datastructures.ETags`
50
+ """
51
+ return parse_etags(self.environ.get("HTTP_IF_NONE_MATCH"))
52
+
53
+ @cached_property
54
+ def if_modified_since(self):
55
+ """The parsed `If-Modified-Since` header as datetime object."""
56
+ return parse_date(self.environ.get("HTTP_IF_MODIFIED_SINCE"))
57
+
58
+ @cached_property
59
+ def if_unmodified_since(self):
60
+ """The parsed `If-Unmodified-Since` header as datetime object."""
61
+ return parse_date(self.environ.get("HTTP_IF_UNMODIFIED_SINCE"))
62
+
63
+ @cached_property
64
+ def if_range(self):
65
+ """The parsed `If-Range` header.
66
+
67
+ .. versionadded:: 0.7
68
+
69
+ :rtype: :class:`~werkzeug.datastructures.IfRange`
70
+ """
71
+ return parse_if_range_header(self.environ.get("HTTP_IF_RANGE"))
72
+
73
+ @cached_property
74
+ def range(self):
75
+ """The parsed `Range` header.
76
+
77
+ .. versionadded:: 0.7
78
+
79
+ :rtype: :class:`~werkzeug.datastructures.Range`
80
+ """
81
+ return parse_range_header(self.environ.get("HTTP_RANGE"))
82
+
83
+
84
+ class ETagResponseMixin(object):
85
+ """Adds extra functionality to a response object for etag and cache
86
+ handling. This mixin requires an object with at least a `headers`
87
+ object that implements a dict like interface similar to
88
+ :class:`~werkzeug.datastructures.Headers`.
89
+
90
+ If you want the :meth:`freeze` method to automatically add an etag, you
91
+ have to mixin this method before the response base class. The default
92
+ response class does not do that.
93
+ """
94
+
95
+ @property
96
+ def cache_control(self):
97
+ """The Cache-Control general-header field is used to specify
98
+ directives that MUST be obeyed by all caching mechanisms along the
99
+ request/response chain.
100
+ """
101
+
102
+ def on_update(cache_control):
103
+ if not cache_control and "cache-control" in self.headers:
104
+ del self.headers["cache-control"]
105
+ elif cache_control:
106
+ self.headers["Cache-Control"] = cache_control.to_header()
107
+
108
+ return parse_cache_control_header(
109
+ self.headers.get("cache-control"), on_update, ResponseCacheControl
110
+ )
111
+
112
+ def _wrap_response(self, start, length):
113
+ """Wrap existing Response in case of Range Request context."""
114
+ if self.status_code == 206:
115
+ self.response = _RangeWrapper(self.response, start, length)
116
+
117
+ def _is_range_request_processable(self, environ):
118
+ """Return ``True`` if `Range` header is present and if underlying
119
+ resource is considered unchanged when compared with `If-Range` header.
120
+ """
121
+ return (
122
+ "HTTP_IF_RANGE" not in environ
123
+ or not is_resource_modified(
124
+ environ,
125
+ self.headers.get("etag"),
126
+ None,
127
+ self.headers.get("last-modified"),
128
+ ignore_if_range=False,
129
+ )
130
+ ) and "HTTP_RANGE" in environ
131
+
132
+ def _process_range_request(self, environ, complete_length=None, accept_ranges=None):
133
+ """Handle Range Request related headers (RFC7233). If `Accept-Ranges`
134
+ header is valid, and Range Request is processable, we set the headers
135
+ as described by the RFC, and wrap the underlying response in a
136
+ RangeWrapper.
137
+
138
+ Returns ``True`` if Range Request can be fulfilled, ``False`` otherwise.
139
+
140
+ :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable`
141
+ if `Range` header could not be parsed or satisfied.
142
+ """
143
+ from ..exceptions import RequestedRangeNotSatisfiable
144
+
145
+ if (
146
+ accept_ranges is None
147
+ or complete_length is None
148
+ or not self._is_range_request_processable(environ)
149
+ ):
150
+ return False
151
+
152
+ parsed_range = parse_range_header(environ.get("HTTP_RANGE"))
153
+
154
+ if parsed_range is None:
155
+ raise RequestedRangeNotSatisfiable(complete_length)
156
+
157
+ range_tuple = parsed_range.range_for_length(complete_length)
158
+ content_range_header = parsed_range.to_content_range_header(complete_length)
159
+
160
+ if range_tuple is None or content_range_header is None:
161
+ raise RequestedRangeNotSatisfiable(complete_length)
162
+
163
+ content_length = range_tuple[1] - range_tuple[0]
164
+ self.headers["Content-Length"] = content_length
165
+ self.headers["Accept-Ranges"] = accept_ranges
166
+ self.content_range = content_range_header
167
+ self.status_code = 206
168
+ self._wrap_response(range_tuple[0], content_length)
169
+ return True
170
+
171
+ def make_conditional(
172
+ self, request_or_environ, accept_ranges=False, complete_length=None
173
+ ):
174
+ """Make the response conditional to the request. This method works
175
+ best if an etag was defined for the response already. The `add_etag`
176
+ method can be used to do that. If called without etag just the date
177
+ header is set.
178
+
179
+ This does nothing if the request method in the request or environ is
180
+ anything but GET or HEAD.
181
+
182
+ For optimal performance when handling range requests, it's recommended
183
+ that your response data object implements `seekable`, `seek` and `tell`
184
+ methods as described by :py:class:`io.IOBase`. Objects returned by
185
+ :meth:`~werkzeug.wsgi.wrap_file` automatically implement those methods.
186
+
187
+ It does not remove the body of the response because that's something
188
+ the :meth:`__call__` function does for us automatically.
189
+
190
+ Returns self so that you can do ``return resp.make_conditional(req)``
191
+ but modifies the object in-place.
192
+
193
+ :param request_or_environ: a request object or WSGI environment to be
194
+ used to make the response conditional
195
+ against.
196
+ :param accept_ranges: This parameter dictates the value of
197
+ `Accept-Ranges` header. If ``False`` (default),
198
+ the header is not set. If ``True``, it will be set
199
+ to ``"bytes"``. If ``None``, it will be set to
200
+ ``"none"``. If it's a string, it will use this
201
+ value.
202
+ :param complete_length: Will be used only in valid Range Requests.
203
+ It will set `Content-Range` complete length
204
+ value and compute `Content-Length` real value.
205
+ This parameter is mandatory for successful
206
+ Range Requests completion.
207
+ :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable`
208
+ if `Range` header could not be parsed or satisfied.
209
+ """
210
+ environ = _get_environ(request_or_environ)
211
+ if environ["REQUEST_METHOD"] in ("GET", "HEAD"):
212
+ # if the date is not in the headers, add it now. We however
213
+ # will not override an already existing header. Unfortunately
214
+ # this header will be overriden by many WSGI servers including
215
+ # wsgiref.
216
+ if "date" not in self.headers:
217
+ self.headers["Date"] = http_date()
218
+ accept_ranges = _clean_accept_ranges(accept_ranges)
219
+ is206 = self._process_range_request(environ, complete_length, accept_ranges)
220
+ if not is206 and not is_resource_modified(
221
+ environ,
222
+ self.headers.get("etag"),
223
+ None,
224
+ self.headers.get("last-modified"),
225
+ ):
226
+ if parse_etags(environ.get("HTTP_IF_MATCH")):
227
+ self.status_code = 412
228
+ else:
229
+ self.status_code = 304
230
+ if (
231
+ self.automatically_set_content_length
232
+ and "content-length" not in self.headers
233
+ ):
234
+ length = self.calculate_content_length()
235
+ if length is not None:
236
+ self.headers["Content-Length"] = length
237
+ return self
238
+
239
+ def add_etag(self, overwrite=False, weak=False):
240
+ """Add an etag for the current response if there is none yet."""
241
+ if overwrite or "etag" not in self.headers:
242
+ self.set_etag(generate_etag(self.get_data()), weak)
243
+
244
+ def set_etag(self, etag, weak=False):
245
+ """Set the etag, and override the old one if there was one."""
246
+ self.headers["ETag"] = quote_etag(etag, weak)
247
+
248
+ def get_etag(self):
249
+ """Return a tuple in the form ``(etag, is_weak)``. If there is no
250
+ ETag the return value is ``(None, None)``.
251
+ """
252
+ return unquote_etag(self.headers.get("ETag"))
253
+
254
+ def freeze(self, no_etag=False):
255
+ """Call this method if you want to make your response object ready for
256
+ pickeling. This buffers the generator if there is one. This also
257
+ sets the etag unless `no_etag` is set to `True`.
258
+ """
259
+ if not no_etag:
260
+ self.add_etag()
261
+ super(ETagResponseMixin, self).freeze()
262
+
263
+ accept_ranges = header_property(
264
+ "Accept-Ranges",
265
+ doc="""The `Accept-Ranges` header. Even though the name would
266
+ indicate that multiple values are supported, it must be one
267
+ string token only.
268
+
269
+ The values ``'bytes'`` and ``'none'`` are common.
270
+
271
+ .. versionadded:: 0.7""",
272
+ )
273
+
274
+ @property
275
+ def content_range(self):
276
+ """The ``Content-Range`` header as a
277
+ :class:`~werkzeug.datastructures.ContentRange` object. Available
278
+ even if the header is not set.
279
+
280
+ .. versionadded:: 0.7
281
+ """
282
+
283
+ def on_update(rng):
284
+ if not rng:
285
+ del self.headers["content-range"]
286
+ else:
287
+ self.headers["Content-Range"] = rng.to_header()
288
+
289
+ rv = parse_content_range_header(self.headers.get("content-range"), on_update)
290
+ # always provide a content range object to make the descriptor
291
+ # more user friendly. It provides an unset() method that can be
292
+ # used to remove the header quickly.
293
+ if rv is None:
294
+ rv = ContentRange(None, None, None, on_update=on_update)
295
+ return rv
296
+
297
+ @content_range.setter
298
+ def content_range(self, value):
299
+ if not value:
300
+ del self.headers["content-range"]
301
+ elif isinstance(value, string_types):
302
+ self.headers["Content-Range"] = value
303
+ else:
304
+ self.headers["Content-Range"] = value.to_header()
@@ -0,0 +1,145 @@
1
+ from __future__ import absolute_import
2
+
3
+ import datetime
4
+ import uuid
5
+
6
+ from .._compat import text_type
7
+ from ..exceptions import BadRequest
8
+ from ..utils import detect_utf_encoding
9
+
10
+ try:
11
+ import simplejson as _json
12
+ except ImportError:
13
+ import json as _json
14
+
15
+
16
+ class _JSONModule(object):
17
+ @staticmethod
18
+ def _default(o):
19
+ if isinstance(o, datetime.date):
20
+ return o.isoformat()
21
+
22
+ if isinstance(o, uuid.UUID):
23
+ return str(o)
24
+
25
+ if hasattr(o, "__html__"):
26
+ return text_type(o.__html__())
27
+
28
+ raise TypeError()
29
+
30
+ @classmethod
31
+ def dumps(cls, obj, **kw):
32
+ kw.setdefault("separators", (",", ":"))
33
+ kw.setdefault("default", cls._default)
34
+ kw.setdefault("sort_keys", True)
35
+ return _json.dumps(obj, **kw)
36
+
37
+ @staticmethod
38
+ def loads(s, **kw):
39
+ if isinstance(s, bytes):
40
+ # Needed for Python < 3.6
41
+ encoding = detect_utf_encoding(s)
42
+ s = s.decode(encoding)
43
+
44
+ return _json.loads(s, **kw)
45
+
46
+
47
+ class JSONMixin(object):
48
+ """Mixin to parse :attr:`data` as JSON. Can be mixed in for both
49
+ :class:`~werkzeug.wrappers.Request` and
50
+ :class:`~werkzeug.wrappers.Response` classes.
51
+
52
+ If `simplejson`_ is installed it is preferred over Python's built-in
53
+ :mod:`json` module.
54
+
55
+ .. _simplejson: https://simplejson.readthedocs.io/en/latest/
56
+ """
57
+
58
+ #: A module or other object that has ``dumps`` and ``loads``
59
+ #: functions that match the API of the built-in :mod:`json` module.
60
+ json_module = _JSONModule
61
+
62
+ @property
63
+ def json(self):
64
+ """The parsed JSON data if :attr:`mimetype` indicates JSON
65
+ (:mimetype:`application/json`, see :meth:`is_json`).
66
+
67
+ Calls :meth:`get_json` with default arguments.
68
+ """
69
+ return self.get_json()
70
+
71
+ @property
72
+ def is_json(self):
73
+ """Check if the mimetype indicates JSON data, either
74
+ :mimetype:`application/json` or :mimetype:`application/*+json`.
75
+ """
76
+ mt = self.mimetype
77
+ return (
78
+ mt == "application/json"
79
+ or mt.startswith("application/")
80
+ and mt.endswith("+json")
81
+ )
82
+
83
+ def _get_data_for_json(self, cache):
84
+ try:
85
+ return self.get_data(cache=cache)
86
+ except TypeError:
87
+ # Response doesn't have cache param.
88
+ return self.get_data()
89
+
90
+ # Cached values for ``(silent=False, silent=True)``. Initialized
91
+ # with sentinel values.
92
+ _cached_json = (Ellipsis, Ellipsis)
93
+
94
+ def get_json(self, force=False, silent=False, cache=True):
95
+ """Parse :attr:`data` as JSON.
96
+
97
+ If the mimetype does not indicate JSON
98
+ (:mimetype:`application/json`, see :meth:`is_json`), this
99
+ returns ``None``.
100
+
101
+ If parsing fails, :meth:`on_json_loading_failed` is called and
102
+ its return value is used as the return value.
103
+
104
+ :param force: Ignore the mimetype and always try to parse JSON.
105
+ :param silent: Silence parsing errors and return ``None``
106
+ instead.
107
+ :param cache: Store the parsed JSON to return for subsequent
108
+ calls.
109
+ """
110
+ if cache and self._cached_json[silent] is not Ellipsis:
111
+ return self._cached_json[silent]
112
+
113
+ if not (force or self.is_json):
114
+ return None
115
+
116
+ data = self._get_data_for_json(cache=cache)
117
+
118
+ try:
119
+ rv = self.json_module.loads(data)
120
+ except ValueError as e:
121
+ if silent:
122
+ rv = None
123
+
124
+ if cache:
125
+ normal_rv, _ = self._cached_json
126
+ self._cached_json = (normal_rv, rv)
127
+ else:
128
+ rv = self.on_json_loading_failed(e)
129
+
130
+ if cache:
131
+ _, silent_rv = self._cached_json
132
+ self._cached_json = (rv, silent_rv)
133
+ else:
134
+ if cache:
135
+ self._cached_json = (rv, rv)
136
+
137
+ return rv
138
+
139
+ def on_json_loading_failed(self, e):
140
+ """Called if :meth:`get_json` parsing fails and isn't silenced.
141
+ If this method returns a value, it is used as the return value
142
+ for :meth:`get_json`. The default implementation raises
143
+ :exc:`~werkzeug.exceptions.BadRequest`.
144
+ """
145
+ raise BadRequest("Failed to decode JSON object: {0}".format(e))
@@ -0,0 +1,49 @@
1
+ from .accept import AcceptMixin
2
+ from .auth import AuthorizationMixin
3
+ from .base_request import BaseRequest
4
+ from .common_descriptors import CommonRequestDescriptorsMixin
5
+ from .cors import CORSRequestMixin
6
+ from .etag import ETagRequestMixin
7
+ from .user_agent import UserAgentMixin
8
+
9
+
10
+ class Request(
11
+ BaseRequest,
12
+ AcceptMixin,
13
+ ETagRequestMixin,
14
+ UserAgentMixin,
15
+ AuthorizationMixin,
16
+ CORSRequestMixin,
17
+ CommonRequestDescriptorsMixin,
18
+ ):
19
+ """Full featured request object implementing the following mixins:
20
+
21
+ - :class:`AcceptMixin` for accept header parsing
22
+ - :class:`ETagRequestMixin` for etag and cache control handling
23
+ - :class:`UserAgentMixin` for user agent introspection
24
+ - :class:`AuthorizationMixin` for http auth handling
25
+ - :class:`~werkzeug.wrappers.cors.CORSRequestMixin` for Cross
26
+ Origin Resource Sharing headers
27
+ - :class:`CommonRequestDescriptorsMixin` for common headers
28
+
29
+ """
30
+
31
+
32
+ class StreamOnlyMixin(object):
33
+ """If mixed in before the request object this will change the behavior
34
+ of it to disable handling of form parsing. This disables the
35
+ :attr:`files`, :attr:`form` attributes and will just provide a
36
+ :attr:`stream` attribute that however is always available.
37
+
38
+ .. versionadded:: 0.9
39
+ """
40
+
41
+ disable_data_descriptor = True
42
+ want_form_data_parsed = False
43
+
44
+
45
+ class PlainRequest(StreamOnlyMixin, Request):
46
+ """A request object without special form parsing capabilities.
47
+
48
+ .. versionadded:: 0.9
49
+ """
@@ -0,0 +1,84 @@
1
+ from ..utils import cached_property
2
+ from .auth import WWWAuthenticateMixin
3
+ from .base_response import BaseResponse
4
+ from .common_descriptors import CommonResponseDescriptorsMixin
5
+ from .cors import CORSResponseMixin
6
+ from .etag import ETagResponseMixin
7
+
8
+
9
+ class ResponseStream(object):
10
+ """A file descriptor like object used by the :class:`ResponseStreamMixin` to
11
+ represent the body of the stream. It directly pushes into the response
12
+ iterable of the response object.
13
+ """
14
+
15
+ mode = "wb+"
16
+
17
+ def __init__(self, response):
18
+ self.response = response
19
+ self.closed = False
20
+
21
+ def write(self, value):
22
+ if self.closed:
23
+ raise ValueError("I/O operation on closed file")
24
+ self.response._ensure_sequence(mutable=True)
25
+ self.response.response.append(value)
26
+ self.response.headers.pop("Content-Length", None)
27
+ return len(value)
28
+
29
+ def writelines(self, seq):
30
+ for item in seq:
31
+ self.write(item)
32
+
33
+ def close(self):
34
+ self.closed = True
35
+
36
+ def flush(self):
37
+ if self.closed:
38
+ raise ValueError("I/O operation on closed file")
39
+
40
+ def isatty(self):
41
+ if self.closed:
42
+ raise ValueError("I/O operation on closed file")
43
+ return False
44
+
45
+ def tell(self):
46
+ self.response._ensure_sequence()
47
+ return sum(map(len, self.response.response))
48
+
49
+ @property
50
+ def encoding(self):
51
+ return self.response.charset
52
+
53
+
54
+ class ResponseStreamMixin(object):
55
+ """Mixin for :class:`BaseResponse` subclasses. Classes that inherit from
56
+ this mixin will automatically get a :attr:`stream` property that provides
57
+ a write-only interface to the response iterable.
58
+ """
59
+
60
+ @cached_property
61
+ def stream(self):
62
+ """The response iterable as write-only stream."""
63
+ return ResponseStream(self)
64
+
65
+
66
+ class Response(
67
+ BaseResponse,
68
+ ETagResponseMixin,
69
+ WWWAuthenticateMixin,
70
+ CORSResponseMixin,
71
+ ResponseStreamMixin,
72
+ CommonResponseDescriptorsMixin,
73
+ ):
74
+ """Full featured response object implementing the following mixins:
75
+
76
+ - :class:`ETagResponseMixin` for etag and cache control handling
77
+ - :class:`WWWAuthenticateMixin` for HTTP authentication support
78
+ - :class:`~werkzeug.wrappers.cors.CORSResponseMixin` for Cross
79
+ Origin Resource Sharing headers
80
+ - :class:`ResponseStreamMixin` to add support for the ``stream``
81
+ property
82
+ - :class:`CommonResponseDescriptorsMixin` for various HTTP
83
+ descriptors
84
+ """
@@ -0,0 +1,14 @@
1
+ from ..useragents import UserAgent
2
+ from ..utils import cached_property
3
+
4
+
5
+ class UserAgentMixin(object):
6
+ """Adds a `user_agent` attribute to the request object which
7
+ contains the parsed user agent of the browser that triggered the
8
+ request as a :class:`~werkzeug.useragents.UserAgent` object.
9
+ """
10
+
11
+ @cached_property
12
+ def user_agent(self):
13
+ """The current user agent."""
14
+ return UserAgent(self.environ)