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,132 @@
1
+ """
2
+ Application Profiler
3
+ ====================
4
+
5
+ This module provides a middleware that profiles each request with the
6
+ :mod:`cProfile` module. This can help identify bottlenecks in your code
7
+ that may be slowing down your application.
8
+
9
+ .. autoclass:: ProfilerMiddleware
10
+
11
+ :copyright: 2007 Pallets
12
+ :license: BSD-3-Clause
13
+ """
14
+ from __future__ import print_function
15
+
16
+ import os.path
17
+ import sys
18
+ import time
19
+ from pstats import Stats
20
+
21
+ try:
22
+ from cProfile import Profile
23
+ except ImportError:
24
+ from profile import Profile
25
+
26
+
27
+ class ProfilerMiddleware(object):
28
+ """Wrap a WSGI application and profile the execution of each
29
+ request. Responses are buffered so that timings are more exact.
30
+
31
+ If ``stream`` is given, :class:`pstats.Stats` are written to it
32
+ after each request. If ``profile_dir`` is given, :mod:`cProfile`
33
+ data files are saved to that directory, one file per request.
34
+
35
+ The filename can be customized by passing ``filename_format``. If
36
+ it is a string, it will be formatted using :meth:`str.format` with
37
+ the following fields available:
38
+
39
+ - ``{method}`` - The request method; GET, POST, etc.
40
+ - ``{path}`` - The request path or 'root' should one not exist.
41
+ - ``{elapsed}`` - The elapsed time of the request.
42
+ - ``{time}`` - The time of the request.
43
+
44
+ If it is a callable, it will be called with the WSGI ``environ``
45
+ dict and should return a filename.
46
+
47
+ :param app: The WSGI application to wrap.
48
+ :param stream: Write stats to this stream. Disable with ``None``.
49
+ :param sort_by: A tuple of columns to sort stats by. See
50
+ :meth:`pstats.Stats.sort_stats`.
51
+ :param restrictions: A tuple of restrictions to filter stats by. See
52
+ :meth:`pstats.Stats.print_stats`.
53
+ :param profile_dir: Save profile data files to this directory.
54
+ :param filename_format: Format string for profile data file names,
55
+ or a callable returning a name. See explanation above.
56
+
57
+ .. code-block:: python
58
+
59
+ from pythonagent.vendor.werkzeug.middleware.profiler import ProfilerMiddleware
60
+ app = ProfilerMiddleware(app)
61
+
62
+ .. versionchanged:: 0.15
63
+ Stats are written even if ``profile_dir`` is given, and can be
64
+ disable by passing ``stream=None``.
65
+
66
+ .. versionadded:: 0.15
67
+ Added ``filename_format``.
68
+
69
+ .. versionadded:: 0.9
70
+ Added ``restrictions`` and ``profile_dir``.
71
+ """
72
+
73
+ def __init__(
74
+ self,
75
+ app,
76
+ stream=sys.stdout,
77
+ sort_by=("time", "calls"),
78
+ restrictions=(),
79
+ profile_dir=None,
80
+ filename_format="{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof",
81
+ ):
82
+ self._app = app
83
+ self._stream = stream
84
+ self._sort_by = sort_by
85
+ self._restrictions = restrictions
86
+ self._profile_dir = profile_dir
87
+ self._filename_format = filename_format
88
+
89
+ def __call__(self, environ, start_response):
90
+ response_body = []
91
+
92
+ def catching_start_response(status, headers, exc_info=None):
93
+ start_response(status, headers, exc_info)
94
+ return response_body.append
95
+
96
+ def runapp():
97
+ app_iter = self._app(environ, catching_start_response)
98
+ response_body.extend(app_iter)
99
+
100
+ if hasattr(app_iter, "close"):
101
+ app_iter.close()
102
+
103
+ profile = Profile()
104
+ start = time.time()
105
+ profile.runcall(runapp)
106
+ body = b"".join(response_body)
107
+ elapsed = time.time() - start
108
+
109
+ if self._profile_dir is not None:
110
+ if callable(self._filename_format):
111
+ filename = self._filename_format(environ)
112
+ else:
113
+ filename = self._filename_format.format(
114
+ method=environ["REQUEST_METHOD"],
115
+ path=(
116
+ environ.get("PATH_INFO").strip("/").replace("/", ".") or "root"
117
+ ),
118
+ elapsed=elapsed * 1000.0,
119
+ time=time.time(),
120
+ )
121
+ filename = os.path.join(self._profile_dir, filename)
122
+ profile.dump_stats(filename)
123
+
124
+ if self._stream is not None:
125
+ stats = Stats(profile, stream=self._stream)
126
+ stats.sort_stats(*self._sort_by)
127
+ print("-" * 80, file=self._stream)
128
+ print("PATH: {!r}".format(environ.get("PATH_INFO", "")), file=self._stream)
129
+ stats.print_stats(*self._restrictions)
130
+ print("-" * 80 + "\n", file=self._stream)
131
+
132
+ return [body]
@@ -0,0 +1,169 @@
1
+ """
2
+ X-Forwarded-For Proxy Fix
3
+ =========================
4
+
5
+ This module provides a middleware that adjusts the WSGI environ based on
6
+ ``X-Forwarded-`` headers that proxies in front of an application may
7
+ set.
8
+
9
+ When an application is running behind a proxy server, WSGI may see the
10
+ request as coming from that server rather than the real client. Proxies
11
+ set various headers to track where the request actually came from.
12
+
13
+ This middleware should only be applied if the application is actually
14
+ behind such a proxy, and should be configured with the number of proxies
15
+ that are chained in front of it. Not all proxies set all the headers.
16
+ Since incoming headers can be faked, you must set how many proxies are
17
+ setting each header so the middleware knows what to trust.
18
+
19
+ .. autoclass:: ProxyFix
20
+
21
+ :copyright: 2007 Pallets
22
+ :license: BSD-3-Clause
23
+ """
24
+ from pythonagent.vendor.werkzeug.http import parse_list_header
25
+
26
+
27
+ class ProxyFix(object):
28
+ """Adjust the WSGI environ based on ``X-Forwarded-`` that proxies in
29
+ front of the application may set.
30
+
31
+ - ``X-Forwarded-For`` sets ``REMOTE_ADDR``.
32
+ - ``X-Forwarded-Proto`` sets ``wsgi.url_scheme``.
33
+ - ``X-Forwarded-Host`` sets ``HTTP_HOST``, ``SERVER_NAME``, and
34
+ ``SERVER_PORT``.
35
+ - ``X-Forwarded-Port`` sets ``HTTP_HOST`` and ``SERVER_PORT``.
36
+ - ``X-Forwarded-Prefix`` sets ``SCRIPT_NAME``.
37
+
38
+ You must tell the middleware how many proxies set each header so it
39
+ knows what values to trust. It is a security issue to trust values
40
+ that came from the client rather than a proxy.
41
+
42
+ The original values of the headers are stored in the WSGI
43
+ environ as ``werkzeug.proxy_fix.orig``, a dict.
44
+
45
+ :param app: The WSGI application to wrap.
46
+ :param x_for: Number of values to trust for ``X-Forwarded-For``.
47
+ :param x_proto: Number of values to trust for ``X-Forwarded-Proto``.
48
+ :param x_host: Number of values to trust for ``X-Forwarded-Host``.
49
+ :param x_port: Number of values to trust for ``X-Forwarded-Port``.
50
+ :param x_prefix: Number of values to trust for
51
+ ``X-Forwarded-Prefix``.
52
+
53
+ .. code-block:: python
54
+
55
+ from pythonagent.vendor.werkzeug.middleware.proxy_fix import ProxyFix
56
+ # App is behind one proxy that sets the -For and -Host headers.
57
+ app = ProxyFix(app, x_for=1, x_host=1)
58
+
59
+ .. versionchanged:: 1.0
60
+ Deprecated code has been removed:
61
+
62
+ * The ``num_proxies`` argument and attribute.
63
+ * The ``get_remote_addr`` method.
64
+ * The environ keys ``orig_remote_addr``,
65
+ ``orig_wsgi_url_scheme``, and ``orig_http_host``.
66
+
67
+ .. versionchanged:: 0.15
68
+ All headers support multiple values. The ``num_proxies``
69
+ argument is deprecated. Each header is configured with a
70
+ separate number of trusted proxies.
71
+
72
+ .. versionchanged:: 0.15
73
+ Original WSGI environ values are stored in the
74
+ ``werkzeug.proxy_fix.orig`` dict. ``orig_remote_addr``,
75
+ ``orig_wsgi_url_scheme``, and ``orig_http_host`` are deprecated
76
+ and will be removed in 1.0.
77
+
78
+ .. versionchanged:: 0.15
79
+ Support ``X-Forwarded-Port`` and ``X-Forwarded-Prefix``.
80
+
81
+ .. versionchanged:: 0.15
82
+ ``X-Forwarded-Host`` and ``X-Forwarded-Port`` modify
83
+ ``SERVER_NAME`` and ``SERVER_PORT``.
84
+ """
85
+
86
+ def __init__(self, app, x_for=1, x_proto=1, x_host=0, x_port=0, x_prefix=0):
87
+ self.app = app
88
+ self.x_for = x_for
89
+ self.x_proto = x_proto
90
+ self.x_host = x_host
91
+ self.x_port = x_port
92
+ self.x_prefix = x_prefix
93
+
94
+ def _get_real_value(self, trusted, value):
95
+ """Get the real value from a list header based on the configured
96
+ number of trusted proxies.
97
+
98
+ :param trusted: Number of values to trust in the header.
99
+ :param value: Comma separated list header value to parse.
100
+ :return: The real value, or ``None`` if there are fewer values
101
+ than the number of trusted proxies.
102
+
103
+ .. versionchanged:: 1.0
104
+ Renamed from ``_get_trusted_comma``.
105
+
106
+ .. versionadded:: 0.15
107
+ """
108
+ if not (trusted and value):
109
+ return
110
+ values = parse_list_header(value)
111
+ if len(values) >= trusted:
112
+ return values[-trusted]
113
+
114
+ def __call__(self, environ, start_response):
115
+ """Modify the WSGI environ based on the various ``Forwarded``
116
+ headers before calling the wrapped application. Store the
117
+ original environ values in ``werkzeug.proxy_fix.orig_{key}``.
118
+ """
119
+ environ_get = environ.get
120
+ orig_remote_addr = environ_get("REMOTE_ADDR")
121
+ orig_wsgi_url_scheme = environ_get("wsgi.url_scheme")
122
+ orig_http_host = environ_get("HTTP_HOST")
123
+ environ.update(
124
+ {
125
+ "werkzeug.proxy_fix.orig": {
126
+ "REMOTE_ADDR": orig_remote_addr,
127
+ "wsgi.url_scheme": orig_wsgi_url_scheme,
128
+ "HTTP_HOST": orig_http_host,
129
+ "SERVER_NAME": environ_get("SERVER_NAME"),
130
+ "SERVER_PORT": environ_get("SERVER_PORT"),
131
+ "SCRIPT_NAME": environ_get("SCRIPT_NAME"),
132
+ }
133
+ }
134
+ )
135
+
136
+ x_for = self._get_real_value(self.x_for, environ_get("HTTP_X_FORWARDED_FOR"))
137
+ if x_for:
138
+ environ["REMOTE_ADDR"] = x_for
139
+
140
+ x_proto = self._get_real_value(
141
+ self.x_proto, environ_get("HTTP_X_FORWARDED_PROTO")
142
+ )
143
+ if x_proto:
144
+ environ["wsgi.url_scheme"] = x_proto
145
+
146
+ x_host = self._get_real_value(self.x_host, environ_get("HTTP_X_FORWARDED_HOST"))
147
+ if x_host:
148
+ environ["HTTP_HOST"] = x_host
149
+ parts = x_host.split(":", 1)
150
+ environ["SERVER_NAME"] = parts[0]
151
+ if len(parts) == 2:
152
+ environ["SERVER_PORT"] = parts[1]
153
+
154
+ x_port = self._get_real_value(self.x_port, environ_get("HTTP_X_FORWARDED_PORT"))
155
+ if x_port:
156
+ host = environ.get("HTTP_HOST")
157
+ if host:
158
+ parts = host.split(":", 1)
159
+ host = parts[0] if len(parts) == 2 else host
160
+ environ["HTTP_HOST"] = "%s:%s" % (host, x_port)
161
+ environ["SERVER_PORT"] = x_port
162
+
163
+ x_prefix = self._get_real_value(
164
+ self.x_prefix, environ_get("HTTP_X_FORWARDED_PREFIX")
165
+ )
166
+ if x_prefix:
167
+ environ["SCRIPT_NAME"] = x_prefix
168
+
169
+ return self.app(environ, start_response)
@@ -0,0 +1,293 @@
1
+ """
2
+ Serve Shared Static Files
3
+ =========================
4
+
5
+ .. autoclass:: SharedDataMiddleware
6
+ :members: is_allowed
7
+
8
+ :copyright: 2007 Pallets
9
+ :license: BSD-3-Clause
10
+ """
11
+ import mimetypes
12
+ import os
13
+ import pkgutil
14
+ import posixpath
15
+ from datetime import datetime
16
+ from io import BytesIO
17
+ from time import mktime
18
+ from time import time
19
+ from zlib import adler32
20
+
21
+ from .._compat import PY2
22
+ from .._compat import string_types
23
+ from ..filesystem import get_filesystem_encoding
24
+ from ..http import http_date
25
+ from ..http import is_resource_modified
26
+ from ..security import safe_join
27
+ from ..utils import get_content_type
28
+ from ..wsgi import get_path_info
29
+ from ..wsgi import wrap_file
30
+
31
+
32
+ class SharedDataMiddleware(object):
33
+
34
+ """A WSGI middleware that provides static content for development
35
+ environments or simple server setups. Usage is quite simple::
36
+
37
+ import os
38
+ from pythonagent.vendor.werkzeug.middleware.shared_data import SharedDataMiddleware
39
+
40
+ app = SharedDataMiddleware(app, {
41
+ '/static': os.path.join(os.path.dirname(__file__), 'static')
42
+ })
43
+
44
+ The contents of the folder ``./shared`` will now be available on
45
+ ``http://example.com/shared/``. This is pretty useful during development
46
+ because a standalone media server is not required. One can also mount
47
+ files on the root folder and still continue to use the application because
48
+ the shared data middleware forwards all unhandled requests to the
49
+ application, even if the requests are below one of the shared folders.
50
+
51
+ If `pkg_resources` is available you can also tell the middleware to serve
52
+ files from package data::
53
+
54
+ app = SharedDataMiddleware(app, {
55
+ '/static': ('myapplication', 'static')
56
+ })
57
+
58
+ This will then serve the ``static`` folder in the `myapplication`
59
+ Python package.
60
+
61
+ The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch`
62
+ rules for files that are not accessible from the web. If `cache` is set to
63
+ `False` no caching headers are sent.
64
+
65
+ Currently the middleware does not support non ASCII filenames. If the
66
+ encoding on the file system happens to be the encoding of the URI it may
67
+ work but this could also be by accident. We strongly suggest using ASCII
68
+ only file names for static files.
69
+
70
+ The middleware will guess the mimetype using the Python `mimetype`
71
+ module. If it's unable to figure out the charset it will fall back
72
+ to `fallback_mimetype`.
73
+
74
+ :param app: the application to wrap. If you don't want to wrap an
75
+ application you can pass it :exc:`NotFound`.
76
+ :param exports: a list or dict of exported files and folders.
77
+ :param disallow: a list of :func:`~fnmatch.fnmatch` rules.
78
+ :param cache: enable or disable caching headers.
79
+ :param cache_timeout: the cache timeout in seconds for the headers.
80
+ :param fallback_mimetype: The fallback mimetype for unknown files.
81
+
82
+ .. versionchanged:: 1.0
83
+ The default ``fallback_mimetype`` is
84
+ ``application/octet-stream``. If a filename looks like a text
85
+ mimetype, the ``utf-8`` charset is added to it.
86
+
87
+ .. versionadded:: 0.6
88
+ Added ``fallback_mimetype``.
89
+
90
+ .. versionchanged:: 0.5
91
+ Added ``cache_timeout``.
92
+ """
93
+
94
+ def __init__(
95
+ self,
96
+ app,
97
+ exports,
98
+ disallow=None,
99
+ cache=True,
100
+ cache_timeout=60 * 60 * 12,
101
+ fallback_mimetype="application/octet-stream",
102
+ ):
103
+ self.app = app
104
+ self.exports = []
105
+ self.cache = cache
106
+ self.cache_timeout = cache_timeout
107
+
108
+ if hasattr(exports, "items"):
109
+ exports = exports.items()
110
+
111
+ for key, value in exports:
112
+ if isinstance(value, tuple):
113
+ loader = self.get_package_loader(*value)
114
+ elif isinstance(value, string_types):
115
+ if os.path.isfile(value):
116
+ loader = self.get_file_loader(value)
117
+ else:
118
+ loader = self.get_directory_loader(value)
119
+ else:
120
+ raise TypeError("unknown def %r" % value)
121
+
122
+ self.exports.append((key, loader))
123
+
124
+ if disallow is not None:
125
+ from fnmatch import fnmatch
126
+
127
+ self.is_allowed = lambda x: not fnmatch(x, disallow)
128
+
129
+ self.fallback_mimetype = fallback_mimetype
130
+
131
+ def is_allowed(self, filename):
132
+ """Subclasses can override this method to disallow the access to
133
+ certain files. However by providing `disallow` in the constructor
134
+ this method is overwritten.
135
+ """
136
+ return True
137
+
138
+ def _opener(self, filename):
139
+ return lambda: (
140
+ open(filename, "rb"),
141
+ datetime.utcfromtimestamp(os.path.getmtime(filename)),
142
+ int(os.path.getsize(filename)),
143
+ )
144
+
145
+ def get_file_loader(self, filename):
146
+ return lambda x: (os.path.basename(filename), self._opener(filename))
147
+
148
+ def get_package_loader(self, package, package_path):
149
+ loadtime = datetime.utcnow()
150
+ provider = pkgutil.get_loader(package)
151
+
152
+ if hasattr(provider, "get_resource_reader"):
153
+ # Python 3
154
+ reader = provider.get_resource_reader(package)
155
+
156
+ def loader(path):
157
+ if path is None:
158
+ return None, None
159
+
160
+ path = safe_join(package_path, path)
161
+ basename = posixpath.basename(path)
162
+
163
+ try:
164
+ resource = reader.open_resource(path)
165
+ except IOError:
166
+ return None, None
167
+
168
+ if isinstance(resource, BytesIO):
169
+ return (
170
+ basename,
171
+ lambda: (resource, loadtime, len(resource.getvalue())),
172
+ )
173
+
174
+ return (
175
+ basename,
176
+ lambda: (
177
+ resource,
178
+ datetime.utcfromtimestamp(os.path.getmtime(resource.name)),
179
+ os.path.getsize(resource.name),
180
+ ),
181
+ )
182
+
183
+ else:
184
+ # Python 2
185
+ package_filename = provider.get_filename(package)
186
+ is_filesystem = os.path.exists(package_filename)
187
+ root = os.path.join(os.path.dirname(package_filename), package_path)
188
+
189
+ def loader(path):
190
+ if path is None:
191
+ return None, None
192
+
193
+ path = safe_join(root, path)
194
+ basename = posixpath.basename(path)
195
+
196
+ if is_filesystem:
197
+ if not os.path.isfile(path):
198
+ return None, None
199
+
200
+ return basename, self._opener(path)
201
+
202
+ try:
203
+ data = provider.get_data(path)
204
+ except IOError:
205
+ return None, None
206
+
207
+ return basename, lambda: (BytesIO(data), loadtime, len(data))
208
+
209
+ return loader
210
+
211
+ def get_directory_loader(self, directory):
212
+ def loader(path):
213
+ if path is not None:
214
+ path = safe_join(directory, path)
215
+ else:
216
+ path = directory
217
+
218
+ if os.path.isfile(path):
219
+ return os.path.basename(path), self._opener(path)
220
+
221
+ return None, None
222
+
223
+ return loader
224
+
225
+ def generate_etag(self, mtime, file_size, real_filename):
226
+ if not isinstance(real_filename, bytes):
227
+ real_filename = real_filename.encode(get_filesystem_encoding())
228
+
229
+ return "wzsdm-%d-%s-%s" % (
230
+ mktime(mtime.timetuple()),
231
+ file_size,
232
+ adler32(real_filename) & 0xFFFFFFFF,
233
+ )
234
+
235
+ def __call__(self, environ, start_response):
236
+ path = get_path_info(environ)
237
+
238
+ if PY2:
239
+ path = path.encode(get_filesystem_encoding())
240
+
241
+ file_loader = None
242
+
243
+ for search_path, loader in self.exports:
244
+ if search_path == path:
245
+ real_filename, file_loader = loader(None)
246
+
247
+ if file_loader is not None:
248
+ break
249
+
250
+ if not search_path.endswith("/"):
251
+ search_path += "/"
252
+
253
+ if path.startswith(search_path):
254
+ real_filename, file_loader = loader(path[len(search_path) :])
255
+
256
+ if file_loader is not None:
257
+ break
258
+
259
+ if file_loader is None or not self.is_allowed(real_filename):
260
+ return self.app(environ, start_response)
261
+
262
+ guessed_type = mimetypes.guess_type(real_filename)
263
+ mime_type = get_content_type(guessed_type[0] or self.fallback_mimetype, "utf-8")
264
+ f, mtime, file_size = file_loader()
265
+
266
+ headers = [("Date", http_date())]
267
+
268
+ if self.cache:
269
+ timeout = self.cache_timeout
270
+ etag = self.generate_etag(mtime, file_size, real_filename)
271
+ headers += [
272
+ ("Etag", '"%s"' % etag),
273
+ ("Cache-Control", "max-age=%d, public" % timeout),
274
+ ]
275
+
276
+ if not is_resource_modified(environ, etag, last_modified=mtime):
277
+ f.close()
278
+ start_response("304 Not Modified", headers)
279
+ return []
280
+
281
+ headers.append(("Expires", http_date(time() + timeout)))
282
+ else:
283
+ headers.append(("Cache-Control", "public"))
284
+
285
+ headers.extend(
286
+ (
287
+ ("Content-Type", mime_type),
288
+ ("Content-Length", str(file_size)),
289
+ ("Last-Modified", http_date(mtime)),
290
+ )
291
+ )
292
+ start_response("200 OK", headers)
293
+ return wrap_file(environ, f)