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,23 @@
1
+ """
2
+ Compatibility layer to allow Pympler being used from different Python versions.
3
+ """
4
+
5
+ from typing import Any, Iterable
6
+
7
+ try:
8
+ import tkinter
9
+ except ImportError:
10
+ tkinter = None # type: ignore
11
+
12
+
13
+ # Helper functions
14
+
15
+ def object_in_list(obj: Any, l: Iterable) -> bool:
16
+ """Returns True if object o is in list.
17
+
18
+ Required compatibility function to handle WeakSet objects.
19
+ """
20
+ for o in l:
21
+ if o is obj:
22
+ return True
23
+ return False
@@ -0,0 +1,77 @@
1
+ """
2
+ String utility functions.
3
+ """
4
+
5
+ from typing import Any, Optional, Union
6
+
7
+
8
+ def safe_repr(obj: Any, clip: Optional[int] = None) -> str:
9
+ """
10
+ Convert object to string representation, yielding the same result a `repr`
11
+ but catches all exceptions and returns 'N/A' instead of raising the
12
+ exception. Strings may be truncated by providing `clip`.
13
+
14
+ >>> safe_repr(42)
15
+ '42'
16
+ >>> safe_repr('Clipped text', clip=8)
17
+ 'Clip..xt'
18
+ >>> safe_repr([1,2,3,4], clip=8)
19
+ '[1,2..4]'
20
+ """
21
+ try:
22
+ s = repr(obj)
23
+ if not clip or len(s) <= clip:
24
+ return s
25
+ else:
26
+ return s[:clip - 4] + '..' + s[-2:]
27
+ except:
28
+ return 'N/A'
29
+
30
+
31
+ def trunc(obj: str, max: int, left: bool = False) -> str:
32
+ """
33
+ Convert `obj` to string, eliminate newlines and truncate the string to
34
+ `max` characters. If there are more characters in the string add ``...`` to
35
+ the string. With `left=True`, the string can be truncated at the beginning.
36
+
37
+ @note: Does not catch exceptions when converting `obj` to string with
38
+ `str`.
39
+
40
+ >>> trunc('This is a long text.', 8)
41
+ This ...
42
+ >>> trunc('This is a long text.', 8, left=True)
43
+ ...text.
44
+ """
45
+ s = str(obj)
46
+ s = s.replace('\n', '|')
47
+ if len(s) > max:
48
+ if left:
49
+ return '...' + s[len(s) - max + 3:]
50
+ else:
51
+ return s[:(max - 3)] + '...'
52
+ else:
53
+ return s
54
+
55
+
56
+ def pp(i: Union[int, float], base: int = 1024) -> str:
57
+ """
58
+ Pretty-print the integer `i` as a human-readable size representation.
59
+ """
60
+ degree = 0
61
+ pattern = "%4d %s"
62
+ while i > base:
63
+ pattern = "%7.2f %s"
64
+ i = i / float(base)
65
+ degree += 1
66
+ scales = ['B', 'KB', 'MB', 'GB', 'TB', 'EB']
67
+ return pattern % (i, scales[degree])
68
+
69
+
70
+ def pp_timestamp(t: Optional[float]) -> str:
71
+ """
72
+ Get a friendly timestamp represented as a string.
73
+ """
74
+ if t is None:
75
+ return ''
76
+ h, m, s = int(t / 3600), int(t / 60 % 60), t % 60
77
+ return "%02d:%02d:%05.2f" % (h, m, s)
@@ -0,0 +1,346 @@
1
+ """
2
+ This module provides a web-based memory profiling interface. The Pympler web
3
+ frontend exposes process information, tracker statistics, and garbage graphs.
4
+ The web frontend uses `Bottle <http://bottlepy.org>`_, a lightweight Python
5
+ web framework. Bottle is packaged with Pympler.
6
+
7
+ The web server can be invoked almost as easily as setting a breakpoint using
8
+ *pdb*::
9
+
10
+ from pympler.web import start_profiler
11
+ start_profiler()
12
+
13
+ Calling ``start_profiler`` suspends the current thread and executes the Pympler
14
+ web server, exposing profiling data and various facilities of the Pympler
15
+ library via a graphic interface.
16
+ """
17
+
18
+ import sys
19
+ import os
20
+ import threading
21
+
22
+ from inspect import getouterframes
23
+ from json import dumps
24
+ from shutil import rmtree
25
+ from tempfile import mkdtemp
26
+ from threading import Thread
27
+ from weakref import WeakValueDictionary
28
+ from wsgiref.simple_server import make_server
29
+
30
+ from . import asizeof
31
+ from .garbagegraph import GarbageGraph
32
+ from .process import get_current_threads, ProcessMemoryInfo
33
+
34
+ from .util.stringutils import safe_repr
35
+
36
+ # Prefer the installed version of bottle.py. If bottle.py is not installed
37
+ # fallback to the vendored version.
38
+ try:
39
+ import bottle
40
+ except ImportError:
41
+ from .util import bottle
42
+
43
+
44
+ class ServerState(threading.local):
45
+ """
46
+ Represents the state of a running server. Needs to be thread local so
47
+ multiple servers can be started in different threads without interfering
48
+ with each other.
49
+
50
+ Cache internal structures (garbage graphs, tracker statistics).
51
+ """
52
+ def __init__(self):
53
+ self.server = None
54
+ self.stats = None
55
+ self.garbage_graphs = None
56
+ self.id2ref = WeakValueDictionary()
57
+ self.id2obj = dict()
58
+
59
+ def clear_cache(self):
60
+ self.garbage_graphs = None
61
+
62
+
63
+ server = ServerState()
64
+
65
+
66
+ def get_ref(obj):
67
+ """
68
+ Get string reference to object. Stores a weak reference in a dictionary
69
+ using the object's id as the key. If the object cannot be weakly
70
+ referenced (e.g. dictionaries, frame objects), store a strong references
71
+ in a classic dictionary.
72
+
73
+ Returns the object's id as a string.
74
+ """
75
+ oid = id(obj)
76
+ try:
77
+ server.id2ref[oid] = obj
78
+ except TypeError:
79
+ server.id2obj[oid] = obj
80
+ return str(oid)
81
+
82
+
83
+ def get_obj(ref):
84
+ """Get object from string reference."""
85
+ oid = int(ref)
86
+ return server.id2ref.get(oid) or server.id2obj[oid]
87
+
88
+
89
+ pympler_path = os.path.dirname(os.path.abspath(__file__))
90
+ static_files = os.path.join(pympler_path, 'templates')
91
+
92
+ bottle.TEMPLATE_PATH.append(static_files)
93
+
94
+
95
+ @bottle.route('/')
96
+ @bottle.view('index')
97
+ def root():
98
+ """Get overview."""
99
+ pmi = ProcessMemoryInfo()
100
+ return dict(processinfo=pmi)
101
+
102
+
103
+ @bottle.route('/process')
104
+ @bottle.view('process')
105
+ def process():
106
+ """Get process overview."""
107
+ pmi = ProcessMemoryInfo()
108
+ threads = get_current_threads()
109
+ return dict(info=pmi, threads=threads)
110
+
111
+
112
+ @bottle.route('/tracker')
113
+ @bottle.view('tracker')
114
+ def tracker_index():
115
+ """Get tracker overview."""
116
+ stats = server.stats
117
+ if stats and stats.snapshots:
118
+ stats.annotate()
119
+ timeseries = []
120
+ for cls in stats.tracked_classes:
121
+ series = []
122
+ for snapshot in stats.snapshots:
123
+ series.append(snapshot.classes.get(cls, {}).get('sum', 0))
124
+ timeseries.append((cls, series))
125
+
126
+ series = [s.overhead for s in stats.snapshots]
127
+ timeseries.append(("Profiling overhead", series))
128
+
129
+ if stats.snapshots[0].system_total.data_segment:
130
+ # Assume tracked data resides in the data segment
131
+ series = [s.system_total.data_segment - s.tracked_total - s.overhead
132
+ for s in stats.snapshots]
133
+ timeseries.append(("Data segment", series))
134
+ series = [s.system_total.code_segment for s in stats.snapshots]
135
+ timeseries.append(("Code segment", series))
136
+ series = [s.system_total.stack_segment for s in stats.snapshots]
137
+ timeseries.append(("Stack segment", series))
138
+ series = [s.system_total.shared_segment for s in stats.snapshots]
139
+ timeseries.append(("Shared memory", series))
140
+ else:
141
+ series = [s.total - s.tracked_total - s.overhead
142
+ for s in stats.snapshots]
143
+ timeseries.append(("Other", series))
144
+ timeseries = [dict(label=label, data=list(enumerate(data)))
145
+ for label, data in timeseries]
146
+ return dict(snapshots=stats.snapshots, timeseries=dumps(timeseries))
147
+ else:
148
+ return dict(snapshots=[])
149
+
150
+
151
+ @bottle.route('/tracker/class/<clsname>')
152
+ @bottle.view('tracker_class')
153
+ def tracker_class(clsname):
154
+ """Get class instance details."""
155
+ stats = server.stats
156
+ if not stats:
157
+ bottle.redirect('/tracker')
158
+ stats.annotate()
159
+ return dict(stats=stats, clsname=clsname)
160
+
161
+
162
+ @bottle.route('/refresh')
163
+ def refresh():
164
+ """Clear all cached information."""
165
+ server.clear_cache()
166
+ bottle.redirect('/')
167
+
168
+
169
+ @bottle.route('/traceback/<threadid>')
170
+ @bottle.view('stacktrace')
171
+ def get_traceback(threadid):
172
+ threadid = int(threadid)
173
+ frames = sys._current_frames()
174
+ if threadid in frames:
175
+ frame = frames[threadid]
176
+ stack = getouterframes(frame, 5)
177
+ stack.reverse()
178
+ stack = [(get_ref(f[0].f_locals),) + f[1:] for f in stack]
179
+ else:
180
+ stack = []
181
+ return dict(stack=stack, threadid=threadid)
182
+
183
+
184
+ @bottle.route('/objects/<oid>')
185
+ @bottle.view('referents')
186
+ def get_obj_referents(oid):
187
+ referents = {}
188
+ obj = get_obj(oid)
189
+ if type(obj) is dict:
190
+ named_objects = asizeof.named_refs(obj)
191
+ else:
192
+ refs = asizeof._getreferents(obj)
193
+ named_objects = [(repr(type(x)), x) for x in refs]
194
+ for name, o in named_objects:
195
+ referents[name] = (get_ref(o), type(o).__name__,
196
+ safe_repr(o, clip=48), asizeof.asizeof(o))
197
+ return dict(referents=referents)
198
+
199
+
200
+ @bottle.route('/static/<filename>')
201
+ def static_file(filename):
202
+ """Get static files (CSS-files)."""
203
+ return bottle.static_file(filename, root=static_files)
204
+
205
+
206
+ def _compute_garbage_graphs():
207
+ """
208
+ Retrieve garbage graph objects from cache, compute if cache is cold.
209
+ """
210
+ if server.garbage_graphs is None:
211
+ server.garbage_graphs = GarbageGraph().split_and_sort()
212
+ return server.garbage_graphs
213
+
214
+
215
+ @bottle.route('/garbage')
216
+ @bottle.view('garbage_index')
217
+ def garbage_index():
218
+ """Get garbage overview."""
219
+ garbage_graphs = _compute_garbage_graphs()
220
+ return dict(graphs=garbage_graphs)
221
+
222
+
223
+ @bottle.route('/garbage/<index:int>')
224
+ @bottle.view('garbage')
225
+ def garbage_cycle(index):
226
+ """Get reference cycle details."""
227
+ graph = _compute_garbage_graphs()[int(index)]
228
+ graph.reduce_to_cycles()
229
+ objects = graph.metadata
230
+ objects.sort(key=lambda x: -x.size)
231
+ return dict(objects=objects, index=index)
232
+
233
+
234
+ def _get_graph(graph, filename):
235
+ """Retrieve or render a graph."""
236
+ try:
237
+ rendered = graph.rendered_file
238
+ except AttributeError:
239
+ try:
240
+ graph.render(os.path.join(server.tmpdir, filename), format='png')
241
+ rendered = filename
242
+ except OSError:
243
+ rendered = None
244
+ graph.rendered_file = rendered
245
+ return rendered
246
+
247
+
248
+ @bottle.route('/garbage/graph/<index:int>')
249
+ def garbage_graph(index):
250
+ """Get graph representation of reference cycle."""
251
+ graph = _compute_garbage_graphs()[int(index)]
252
+ reduce_graph = bottle.request.GET.get('reduce', '')
253
+ if reduce_graph:
254
+ graph = graph.reduce_to_cycles()
255
+ if not graph:
256
+ return None
257
+ filename = 'garbage%so%s.png' % (index, reduce_graph)
258
+ rendered_file = _get_graph(graph, filename)
259
+ if rendered_file:
260
+ return bottle.static_file(rendered_file, root=server.tmpdir)
261
+ else:
262
+ return None
263
+
264
+
265
+ @bottle.route('/help')
266
+ def show_documentation():
267
+ """Redirect to online documentation."""
268
+ bottle.redirect('https://pympler.readthedocs.io/en/latest/')
269
+
270
+
271
+ class PymplerServer(bottle.ServerAdapter):
272
+ """Simple WSGI server."""
273
+ def run(self, handler):
274
+ self.server = make_server(self.host, self.port, handler)
275
+ self.server.serve_forever()
276
+
277
+
278
+ def start_profiler(host='localhost', port=8090, tracker=None, stats=None,
279
+ debug=False, **kwargs):
280
+ """
281
+ Start the web server to show profiling data. The function suspends the
282
+ Python application (the current thread) until the web server is stopped.
283
+
284
+ The only way to stop the server is to signal the running thread, e.g. press
285
+ Ctrl+C in the console. If this isn't feasible for your application use
286
+ `start_in_background` instead.
287
+
288
+ During the execution of the web server, profiling data is (lazily) cached
289
+ to improve performance. For example, garbage graphs are rendered when the
290
+ garbage profiling data is requested and are simply retransmitted upon later
291
+ requests.
292
+
293
+ The web server can display profiling data from previously taken snapshots
294
+ when `tracker` or `stats` is specified. The former is useful for profiling
295
+ a running application, the latter for off-line analysis. Requires existing
296
+ snapshots taken with
297
+ :py:meth:`~pympler.classtracker.ClassTracker.create_snapshot` or
298
+ :py:meth:`~pympler.classtracker.ClassTracker.start_periodic_snapshots`.
299
+
300
+ :param host: the host where the server shall run, default is localhost
301
+ :param port: server listens on the specified port, default is 8090 to allow
302
+ coexistance with common web applications
303
+ :param tracker: `ClassTracker` instance, browse profiling data (on-line
304
+ analysis)
305
+ :param stats: `Stats` instance, analyze `ClassTracker` profiling dumps
306
+ (useful for off-line analysis)
307
+ """
308
+ if tracker and not stats:
309
+ server.stats = tracker.stats
310
+ else:
311
+ server.stats = stats
312
+ try:
313
+ server.tmpdir = mkdtemp(prefix='pympler')
314
+ server.server = PymplerServer(host=host, port=port, **kwargs)
315
+ bottle.debug(debug)
316
+ bottle.run(server=server.server)
317
+ finally:
318
+ rmtree(server.tmpdir)
319
+
320
+
321
+ class ProfilerThread(Thread):
322
+ """Encapsulates a thread to run the web server."""
323
+ def __init__(self, group=None, target=None, name='Pympler web frontend',
324
+ **kwargs):
325
+ super(ProfilerThread, self).__init__(group=group,
326
+ target=target,
327
+ name=name)
328
+ self.kwargs = kwargs
329
+ self.daemon = True
330
+
331
+ def run(self):
332
+ start_profiler(**self.kwargs)
333
+
334
+
335
+ def start_in_background(**kwargs):
336
+ """
337
+ Start the web server in the background. A new thread is created which
338
+ serves the profiling interface without suspending the current application.
339
+
340
+ For the documentation of the parameters see `start_profiler`.
341
+
342
+ Returns the created thread object.
343
+ """
344
+ thread = ProfilerThread(**kwargs)
345
+ thread.start()
346
+ return thread
@@ -0,0 +1,20 @@
1
+ """
2
+ werkzeug
3
+ ~~~~~~~~
4
+
5
+ Werkzeug is the Swiss Army knife of Python web development.
6
+
7
+ It provides useful classes and functions for any WSGI application to
8
+ make the life of a Python web developer much easier. All of the provided
9
+ classes are independent from each other so you can mix it with any other
10
+ library.
11
+
12
+ :copyright: 2007 Pallets
13
+ :license: BSD-3-Clause
14
+ """
15
+ from .serving import run_simple
16
+ from .test import Client
17
+ from .wrappers import Request
18
+ from .wrappers import Response
19
+
20
+ __version__ = "1.0.1"
@@ -0,0 +1,228 @@
1
+ # flake8: noqa
2
+ # This whole file is full of lint errors
3
+ import functools
4
+ import operator
5
+ import sys
6
+
7
+ try:
8
+ import builtins
9
+ except ImportError:
10
+ import __builtin__ as builtins
11
+
12
+
13
+ PY2 = sys.version_info[0] == 2
14
+ WIN = sys.platform.startswith("win")
15
+
16
+ _identity = lambda x: x
17
+
18
+ if PY2:
19
+ unichr = unichr
20
+ text_type = unicode
21
+ string_types = (str, unicode)
22
+ integer_types = (int, long)
23
+
24
+ iterkeys = lambda d, *args, **kwargs: d.iterkeys(*args, **kwargs)
25
+ itervalues = lambda d, *args, **kwargs: d.itervalues(*args, **kwargs)
26
+ iteritems = lambda d, *args, **kwargs: d.iteritems(*args, **kwargs)
27
+
28
+ iterlists = lambda d, *args, **kwargs: d.iterlists(*args, **kwargs)
29
+ iterlistvalues = lambda d, *args, **kwargs: d.iterlistvalues(*args, **kwargs)
30
+
31
+ int_to_byte = chr
32
+ iter_bytes = iter
33
+
34
+ import collections as collections_abc
35
+
36
+ exec("def reraise(tp, value, tb=None):\n raise tp, value, tb")
37
+
38
+ def fix_tuple_repr(obj):
39
+ def __repr__(self):
40
+ cls = self.__class__
41
+ return "%s(%s)" % (
42
+ cls.__name__,
43
+ ", ".join(
44
+ "%s=%r" % (field, self[index])
45
+ for index, field in enumerate(cls._fields)
46
+ ),
47
+ )
48
+
49
+ obj.__repr__ = __repr__
50
+ return obj
51
+
52
+ def implements_iterator(cls):
53
+ cls.next = cls.__next__
54
+ del cls.__next__
55
+ return cls
56
+
57
+ def implements_to_string(cls):
58
+ cls.__unicode__ = cls.__str__
59
+ cls.__str__ = lambda x: x.__unicode__().encode("utf-8")
60
+ return cls
61
+
62
+ def native_string_result(func):
63
+ def wrapper(*args, **kwargs):
64
+ return func(*args, **kwargs).encode("utf-8")
65
+
66
+ return functools.update_wrapper(wrapper, func)
67
+
68
+ def implements_bool(cls):
69
+ cls.__nonzero__ = cls.__bool__
70
+ del cls.__bool__
71
+ return cls
72
+
73
+ from itertools import imap, izip, ifilter
74
+
75
+ range_type = xrange
76
+
77
+ from StringIO import StringIO
78
+ from cStringIO import StringIO as BytesIO
79
+
80
+ NativeStringIO = BytesIO
81
+
82
+ def make_literal_wrapper(reference):
83
+ return _identity
84
+
85
+ def normalize_string_tuple(tup):
86
+ """Normalizes a string tuple to a common type. Following Python 2
87
+ rules, upgrades to unicode are implicit.
88
+ """
89
+ if any(isinstance(x, text_type) for x in tup):
90
+ return tuple(to_unicode(x) for x in tup)
91
+ return tup
92
+
93
+ def try_coerce_native(s):
94
+ """Try to coerce a unicode string to native if possible. Otherwise,
95
+ leave it as unicode.
96
+ """
97
+ try:
98
+ return to_native(s)
99
+ except UnicodeError:
100
+ return s
101
+
102
+ wsgi_get_bytes = _identity
103
+
104
+ def wsgi_decoding_dance(s, charset="utf-8", errors="replace"):
105
+ return s.decode(charset, errors)
106
+
107
+ def wsgi_encoding_dance(s, charset="utf-8", errors="replace"):
108
+ if isinstance(s, bytes):
109
+ return s
110
+ return s.encode(charset, errors)
111
+
112
+ def to_bytes(x, charset=sys.getdefaultencoding(), errors="strict"):
113
+ if x is None:
114
+ return None
115
+ if isinstance(x, (bytes, bytearray, buffer)):
116
+ return bytes(x)
117
+ if isinstance(x, unicode):
118
+ return x.encode(charset, errors)
119
+ raise TypeError("Expected bytes")
120
+
121
+ def to_native(x, charset=sys.getdefaultencoding(), errors="strict"):
122
+ if x is None or isinstance(x, str):
123
+ return x
124
+ return x.encode(charset, errors)
125
+
126
+
127
+ else:
128
+ unichr = chr
129
+ text_type = str
130
+ string_types = (str,)
131
+ integer_types = (int,)
132
+
133
+ iterkeys = lambda d, *args, **kwargs: iter(d.keys(*args, **kwargs))
134
+ itervalues = lambda d, *args, **kwargs: iter(d.values(*args, **kwargs))
135
+ iteritems = lambda d, *args, **kwargs: iter(d.items(*args, **kwargs))
136
+
137
+ iterlists = lambda d, *args, **kwargs: iter(d.lists(*args, **kwargs))
138
+ iterlistvalues = lambda d, *args, **kwargs: iter(d.listvalues(*args, **kwargs))
139
+
140
+ int_to_byte = operator.methodcaller("to_bytes", 1, "big")
141
+ iter_bytes = functools.partial(map, int_to_byte)
142
+
143
+ import collections.abc as collections_abc
144
+
145
+ def reraise(tp, value, tb=None):
146
+ if value.__traceback__ is not tb:
147
+ raise value.with_traceback(tb)
148
+ raise value
149
+
150
+ fix_tuple_repr = _identity
151
+ implements_iterator = _identity
152
+ implements_to_string = _identity
153
+ implements_bool = _identity
154
+ native_string_result = _identity
155
+ imap = map
156
+ izip = zip
157
+ ifilter = filter
158
+ range_type = range
159
+
160
+ from io import StringIO, BytesIO
161
+
162
+ NativeStringIO = StringIO
163
+
164
+ _latin1_encode = operator.methodcaller("encode", "latin1")
165
+
166
+ def make_literal_wrapper(reference):
167
+ if isinstance(reference, text_type):
168
+ return _identity
169
+ return _latin1_encode
170
+
171
+ def normalize_string_tuple(tup):
172
+ """Ensures that all types in the tuple are either strings
173
+ or bytes.
174
+ """
175
+ tupiter = iter(tup)
176
+ is_text = isinstance(next(tupiter, None), text_type)
177
+ for arg in tupiter:
178
+ if isinstance(arg, text_type) != is_text:
179
+ raise TypeError(
180
+ "Cannot mix str and bytes arguments (got %s)" % repr(tup)
181
+ )
182
+ return tup
183
+
184
+ try_coerce_native = _identity
185
+ wsgi_get_bytes = _latin1_encode
186
+
187
+ def wsgi_decoding_dance(s, charset="utf-8", errors="replace"):
188
+ return s.encode("latin1").decode(charset, errors)
189
+
190
+ def wsgi_encoding_dance(s, charset="utf-8", errors="replace"):
191
+ if isinstance(s, text_type):
192
+ s = s.encode(charset)
193
+ return s.decode("latin1", errors)
194
+
195
+ def to_bytes(x, charset=sys.getdefaultencoding(), errors="strict"):
196
+ if x is None:
197
+ return None
198
+ if isinstance(x, (bytes, bytearray, memoryview)): # noqa
199
+ return bytes(x)
200
+ if isinstance(x, str):
201
+ return x.encode(charset, errors)
202
+ raise TypeError("Expected bytes")
203
+
204
+ def to_native(x, charset=sys.getdefaultencoding(), errors="strict"):
205
+ if x is None or isinstance(x, str):
206
+ return x
207
+ return x.decode(charset, errors)
208
+
209
+
210
+ def to_unicode(
211
+ x, charset=sys.getdefaultencoding(), errors="strict", allow_none_charset=False
212
+ ):
213
+ if x is None:
214
+ return None
215
+ if not isinstance(x, bytes):
216
+ return text_type(x)
217
+ if charset is None and allow_none_charset:
218
+ return x
219
+ return x.decode(charset, errors)
220
+
221
+
222
+ try:
223
+ from os import fspath
224
+ except ImportError:
225
+ # Python < 3.6
226
+ # https://www.python.org/dev/peps/pep-0519/#backwards-compatibility
227
+ def fspath(path):
228
+ return path.__fspath__() if hasattr(path, "__fspath__") else path