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.
- cavisson_pythonagent-0.0.1.dist-info/METADATA +32 -0
- cavisson_pythonagent-0.0.1.dist-info/RECORD +180 -0
- cavisson_pythonagent-0.0.1.dist-info/WHEEL +5 -0
- cavisson_pythonagent-0.0.1.dist-info/licenses/LICENSE +19 -0
- cavisson_pythonagent-0.0.1.dist-info/top_level.txt +1 -0
- pythonagent/__init__.py +22 -0
- pythonagent/agent/__init__.py +219 -0
- pythonagent/agent/internal/__init__.py +1 -0
- pythonagent/agent/internal/agent.py +1651 -0
- pythonagent/agent/internal/framesinfo.py +152 -0
- pythonagent/agent/internal/heap_dump.py +39 -0
- pythonagent/agent/internal/intercept_module.py +70 -0
- pythonagent/agent/internal/logs.py +122 -0
- pythonagent/agent/internal/metadata/__init__.py +0 -0
- pythonagent/agent/internal/metadata/agent_meta_data.py +276 -0
- pythonagent/agent/internal/proc_compat.py +75 -0
- pythonagent/agent/internal/profile.py +47 -0
- pythonagent/agent/internal/provider.py +83 -0
- pythonagent/agent/internal/thread_dump.py +85 -0
- pythonagent/agent/internal/udp.py +245 -0
- pythonagent/agent/internal/udp_message.py +800 -0
- pythonagent/agent/probes/Instrumentation/__init__.py +340 -0
- pythonagent/agent/probes/Instrumentation/find.py +243 -0
- pythonagent/agent/probes/Instrumentation/module_version_resolver.py +155 -0
- pythonagent/agent/probes/Instrumentation/new_parser.py +64 -0
- pythonagent/agent/probes/Instrumentation/parser.py +129 -0
- pythonagent/agent/probes/__init__.py +219 -0
- pythonagent/agent/probes/base.py +303 -0
- pythonagent/agent/probes/cache/__init__.py +51 -0
- pythonagent/agent/probes/cache/redis.py +119 -0
- pythonagent/agent/probes/cache/redis_asyncio.py +83 -0
- pythonagent/agent/probes/coroutines/__init__.py +1 -0
- pythonagent/agent/probes/coroutines/asyncio.py +63 -0
- pythonagent/agent/probes/elasticdb/__init__.py +7 -0
- pythonagent/agent/probes/elasticdb/aelastic.py +54 -0
- pythonagent/agent/probes/frameworks/__init__.py +27 -0
- pythonagent/agent/probes/frameworks/agentprofiler.py +105 -0
- pythonagent/agent/probes/frameworks/aiohttp_web.py +155 -0
- pythonagent/agent/probes/frameworks/aisess.py +151 -0
- pythonagent/agent/probes/frameworks/asgi.py +340 -0
- pythonagent/agent/probes/frameworks/bottle.py +27 -0
- pythonagent/agent/probes/frameworks/cherry.py +25 -0
- pythonagent/agent/probes/frameworks/django.py +128 -0
- pythonagent/agent/probes/frameworks/falcon.py +21 -0
- pythonagent/agent/probes/frameworks/fastapi.py +35 -0
- pythonagent/agent/probes/frameworks/flask.py +30 -0
- pythonagent/agent/probes/frameworks/pyramid.py +56 -0
- pythonagent/agent/probes/frameworks/test.py +108 -0
- pythonagent/agent/probes/frameworks/tornado_async_web.py +117 -0
- pythonagent/agent/probes/frameworks/tornado_web.py +133 -0
- pythonagent/agent/probes/frameworks/wsgi.py +353 -0
- pythonagent/agent/probes/grpc/__init__.py +76 -0
- pythonagent/agent/probes/grpc/client_interceptor.py +132 -0
- pythonagent/agent/probes/grpc/server_interceptor.py +129 -0
- pythonagent/agent/probes/havoc/__init__.py +0 -0
- pythonagent/agent/probes/havoc/custom_memory_stress.py +186 -0
- pythonagent/agent/probes/havoc/custom_thread_stress.py +187 -0
- pythonagent/agent/probes/havoc/havoc_constants.py +218 -0
- pythonagent/agent/probes/havoc/havoc_manager.py +981 -0
- pythonagent/agent/probes/http/__init__.py +49 -0
- pythonagent/agent/probes/http/aiohttp_client.py +59 -0
- pythonagent/agent/probes/http/boto.py +12 -0
- pythonagent/agent/probes/http/httplib.py +110 -0
- pythonagent/agent/probes/http/httpx_client.py +116 -0
- pythonagent/agent/probes/http/requests.py +15 -0
- pythonagent/agent/probes/http/tornado_httpclient.py +85 -0
- pythonagent/agent/probes/http/urllib3.py +16 -0
- pythonagent/agent/probes/langchain/__init__.py +21 -0
- pythonagent/agent/probes/langchain/base_tool.py +95 -0
- pythonagent/agent/probes/langchain/langchain_community.py +136 -0
- pythonagent/agent/probes/langchain/langchain_core.py +32 -0
- pythonagent/agent/probes/langchain/langchain_openai.py +110 -0
- pythonagent/agent/probes/logging/__init__.py +106 -0
- pythonagent/agent/probes/message_brokers/__init__.py +4 -0
- pythonagent/agent/probes/message_brokers/pika.py +126 -0
- pythonagent/agent/probes/mongodb/__init__.py +6 -0
- pythonagent/agent/probes/mongodb/pymongo.py +286 -0
- pythonagent/agent/probes/openai/__init__.py +3 -0
- pythonagent/agent/probes/openai/openai.py +797 -0
- pythonagent/agent/probes/span.py +101 -0
- pythonagent/agent/probes/sql/__init__.py +13 -0
- pythonagent/agent/probes/sql/botocores3.py +51 -0
- pythonagent/agent/probes/sql/dbapi.py +285 -0
- pythonagent/agent/probes/sql/dynamodb.py +90 -0
- pythonagent/agent/probes/sql/mysql_connector.py +24 -0
- pythonagent/agent/probes/sql/mysql_connector_cext.py +24 -0
- pythonagent/agent/probes/sql/mysqldb.py +43 -0
- pythonagent/agent/probes/sql/psycopg2.py +174 -0
- pythonagent/agent/probes/sql/pymysql.py +25 -0
- pythonagent/bootstrap/__init__.py +0 -0
- pythonagent/bootstrap/cav_gunicorn.py +26 -0
- pythonagent/bootstrap/cavagent_lambda_wrapper.py +291 -0
- pythonagent/bootstrap/run.py +47 -0
- pythonagent/bootstrap/sitecustomize.py +287 -0
- pythonagent/cavisson/netdiagnostics/CavAgent/instrumentationprofile.json +26 -0
- pythonagent/cavisson/netdiagnostics/CavAgent/interceptor_points.txt +29 -0
- pythonagent/cavisson/netdiagnostics/python/CavAgent/instrumentationprofile.json +42 -0
- pythonagent/cavisson/netdiagnostics/python/CavAgent/interceptor_points.txt +29 -0
- pythonagent/cavisson/netdiagnostics/python/config/ndsettings.conf +6 -0
- pythonagent/config.py +279 -0
- pythonagent/find.py +72 -0
- pythonagent/find_mod_cls_name.py +54 -0
- pythonagent/lang.py +131 -0
- pythonagent/lib.py +91 -0
- pythonagent/main/__init__.py +0 -0
- pythonagent/main/pytrace/__init__.py +79 -0
- pythonagent/main/pytrace/commands/__init__.py +0 -0
- pythonagent/main/pytrace/commands/auto_discovery.py +25 -0
- pythonagent/main/pytrace/commands/run.py +401 -0
- pythonagent/main/pytrace/pytrace.py +133 -0
- pythonagent/main/wsgi.py +6 -0
- pythonagent/main.py +27 -0
- pythonagent/run.py +46 -0
- pythonagent/sqins.py +8 -0
- pythonagent/test.py +73 -0
- pythonagent/utils.py +168 -0
- pythonagent/vendor/__init__.py +0 -0
- pythonagent/vendor/pympler/__init__.py +1 -0
- pythonagent/vendor/pympler/asizeof.py +2810 -0
- pythonagent/vendor/pympler/charts.py +62 -0
- pythonagent/vendor/pympler/classtracker.py +590 -0
- pythonagent/vendor/pympler/classtracker_stats.py +780 -0
- pythonagent/vendor/pympler/garbagegraph.py +80 -0
- pythonagent/vendor/pympler/mprofile.py +97 -0
- pythonagent/vendor/pympler/muppy.py +275 -0
- pythonagent/vendor/pympler/panels.py +115 -0
- pythonagent/vendor/pympler/process.py +238 -0
- pythonagent/vendor/pympler/py.typed +0 -0
- pythonagent/vendor/pympler/refbrowser.py +451 -0
- pythonagent/vendor/pympler/refgraph.py +350 -0
- pythonagent/vendor/pympler/summary.py +321 -0
- pythonagent/vendor/pympler/tracker.py +267 -0
- pythonagent/vendor/pympler/util/__init__.py +0 -0
- pythonagent/vendor/pympler/util/bottle.py +3809 -0
- pythonagent/vendor/pympler/util/compat.py +23 -0
- pythonagent/vendor/pympler/util/stringutils.py +77 -0
- pythonagent/vendor/pympler/web.py +346 -0
- pythonagent/vendor/werkzeug/__init__.py +20 -0
- pythonagent/vendor/werkzeug/_compat.py +228 -0
- pythonagent/vendor/werkzeug/_internal.py +473 -0
- pythonagent/vendor/werkzeug/_reloader.py +341 -0
- pythonagent/vendor/werkzeug/datastructures.py +3120 -0
- pythonagent/vendor/werkzeug/debug/__init__.py +498 -0
- pythonagent/vendor/werkzeug/debug/console.py +218 -0
- pythonagent/vendor/werkzeug/debug/repr.py +297 -0
- pythonagent/vendor/werkzeug/debug/tbtools.py +628 -0
- pythonagent/vendor/werkzeug/exceptions.py +829 -0
- pythonagent/vendor/werkzeug/filesystem.py +64 -0
- pythonagent/vendor/werkzeug/formparser.py +584 -0
- pythonagent/vendor/werkzeug/http.py +1307 -0
- pythonagent/vendor/werkzeug/local.py +420 -0
- pythonagent/vendor/werkzeug/middleware/__init__.py +25 -0
- pythonagent/vendor/werkzeug/middleware/dispatcher.py +66 -0
- pythonagent/vendor/werkzeug/middleware/http_proxy.py +219 -0
- pythonagent/vendor/werkzeug/middleware/lint.py +408 -0
- pythonagent/vendor/werkzeug/middleware/profiler.py +132 -0
- pythonagent/vendor/werkzeug/middleware/proxy_fix.py +169 -0
- pythonagent/vendor/werkzeug/middleware/shared_data.py +293 -0
- pythonagent/vendor/werkzeug/posixemulation.py +117 -0
- pythonagent/vendor/werkzeug/routing.py +2210 -0
- pythonagent/vendor/werkzeug/security.py +249 -0
- pythonagent/vendor/werkzeug/serving.py +1117 -0
- pythonagent/vendor/werkzeug/test.py +1123 -0
- pythonagent/vendor/werkzeug/testapp.py +241 -0
- pythonagent/vendor/werkzeug/urls.py +1138 -0
- pythonagent/vendor/werkzeug/useragents.py +202 -0
- pythonagent/vendor/werkzeug/utils.py +778 -0
- pythonagent/vendor/werkzeug/wrappers/__init__.py +36 -0
- pythonagent/vendor/werkzeug/wrappers/accept.py +50 -0
- pythonagent/vendor/werkzeug/wrappers/auth.py +33 -0
- pythonagent/vendor/werkzeug/wrappers/base_request.py +673 -0
- pythonagent/vendor/werkzeug/wrappers/base_response.py +700 -0
- pythonagent/vendor/werkzeug/wrappers/common_descriptors.py +341 -0
- pythonagent/vendor/werkzeug/wrappers/cors.py +100 -0
- pythonagent/vendor/werkzeug/wrappers/etag.py +304 -0
- pythonagent/vendor/werkzeug/wrappers/json.py +145 -0
- pythonagent/vendor/werkzeug/wrappers/request.py +49 -0
- pythonagent/vendor/werkzeug/wrappers/response.py +84 -0
- pythonagent/vendor/werkzeug/wrappers/user_agent.py +14 -0
- pythonagent/vendor/werkzeug/wsgi.py +1000 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
from itertools import chain
|
|
7
|
+
|
|
8
|
+
from ._compat import iteritems
|
|
9
|
+
from ._compat import PY2
|
|
10
|
+
from ._compat import text_type
|
|
11
|
+
from ._internal import _log
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _iter_module_files():
|
|
15
|
+
"""This iterates over all relevant Python files. It goes through all
|
|
16
|
+
loaded files from modules, all files in folders of already loaded modules
|
|
17
|
+
as well as all files reachable through a package.
|
|
18
|
+
"""
|
|
19
|
+
# The list call is necessary on Python 3 in case the module
|
|
20
|
+
# dictionary modifies during iteration.
|
|
21
|
+
for module in list(sys.modules.values()):
|
|
22
|
+
if module is None:
|
|
23
|
+
continue
|
|
24
|
+
filename = getattr(module, "__file__", None)
|
|
25
|
+
if filename:
|
|
26
|
+
if os.path.isdir(filename) and os.path.exists(
|
|
27
|
+
os.path.join(filename, "__init__.py")
|
|
28
|
+
):
|
|
29
|
+
filename = os.path.join(filename, "__init__.py")
|
|
30
|
+
|
|
31
|
+
old = None
|
|
32
|
+
while not os.path.isfile(filename):
|
|
33
|
+
old = filename
|
|
34
|
+
filename = os.path.dirname(filename)
|
|
35
|
+
if filename == old:
|
|
36
|
+
break
|
|
37
|
+
else:
|
|
38
|
+
if filename[-4:] in (".pyc", ".pyo"):
|
|
39
|
+
filename = filename[:-1]
|
|
40
|
+
yield filename
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _find_observable_paths(extra_files=None):
|
|
44
|
+
"""Finds all paths that should be observed."""
|
|
45
|
+
rv = set(
|
|
46
|
+
os.path.dirname(os.path.abspath(x)) if os.path.isfile(x) else os.path.abspath(x)
|
|
47
|
+
for x in sys.path
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
for filename in extra_files or ():
|
|
51
|
+
rv.add(os.path.dirname(os.path.abspath(filename)))
|
|
52
|
+
|
|
53
|
+
for module in list(sys.modules.values()):
|
|
54
|
+
fn = getattr(module, "__file__", None)
|
|
55
|
+
if fn is None:
|
|
56
|
+
continue
|
|
57
|
+
fn = os.path.abspath(fn)
|
|
58
|
+
rv.add(os.path.dirname(fn))
|
|
59
|
+
|
|
60
|
+
return _find_common_roots(rv)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _get_args_for_reloading():
|
|
64
|
+
"""Determine how the script was executed, and return the args needed
|
|
65
|
+
to execute it again in a new process.
|
|
66
|
+
"""
|
|
67
|
+
rv = [sys.executable]
|
|
68
|
+
py_script = sys.argv[0]
|
|
69
|
+
args = sys.argv[1:]
|
|
70
|
+
# Need to look at main module to determine how it was executed.
|
|
71
|
+
__main__ = sys.modules["__main__"]
|
|
72
|
+
|
|
73
|
+
# The value of __package__ indicates how Python was called. It may
|
|
74
|
+
# not exist if a setuptools script is installed as an egg. It may be
|
|
75
|
+
# set incorrectly for entry points created with pip on Windows.
|
|
76
|
+
if getattr(__main__, "__package__", None) is None or (
|
|
77
|
+
os.name == "nt"
|
|
78
|
+
and __main__.__package__ == ""
|
|
79
|
+
and not os.path.exists(py_script)
|
|
80
|
+
and os.path.exists(py_script + ".exe")
|
|
81
|
+
):
|
|
82
|
+
# Executed a file, like "python app.py".
|
|
83
|
+
py_script = os.path.abspath(py_script)
|
|
84
|
+
|
|
85
|
+
if os.name == "nt":
|
|
86
|
+
# Windows entry points have ".exe" extension and should be
|
|
87
|
+
# called directly.
|
|
88
|
+
if not os.path.exists(py_script) and os.path.exists(py_script + ".exe"):
|
|
89
|
+
py_script += ".exe"
|
|
90
|
+
|
|
91
|
+
if (
|
|
92
|
+
os.path.splitext(sys.executable)[1] == ".exe"
|
|
93
|
+
and os.path.splitext(py_script)[1] == ".exe"
|
|
94
|
+
):
|
|
95
|
+
rv.pop(0)
|
|
96
|
+
|
|
97
|
+
rv.append(py_script)
|
|
98
|
+
else:
|
|
99
|
+
# Executed a module, like "python -m werkzeug.serving".
|
|
100
|
+
if sys.argv[0] == "-m":
|
|
101
|
+
# Flask works around previous behavior by putting
|
|
102
|
+
# "-m flask" in sys.argv.
|
|
103
|
+
# TODO remove this once Flask no longer misbehaves
|
|
104
|
+
args = sys.argv
|
|
105
|
+
else:
|
|
106
|
+
if os.path.isfile(py_script):
|
|
107
|
+
# Rewritten by Python from "-m script" to "/path/to/script.py".
|
|
108
|
+
py_module = __main__.__package__
|
|
109
|
+
name = os.path.splitext(os.path.basename(py_script))[0]
|
|
110
|
+
|
|
111
|
+
if name != "__main__":
|
|
112
|
+
py_module += "." + name
|
|
113
|
+
else:
|
|
114
|
+
# Incorrectly rewritten by pydevd debugger from "-m script" to "script".
|
|
115
|
+
py_module = py_script
|
|
116
|
+
|
|
117
|
+
rv.extend(("-m", py_module.lstrip(".")))
|
|
118
|
+
|
|
119
|
+
rv.extend(args)
|
|
120
|
+
return rv
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _find_common_roots(paths):
|
|
124
|
+
"""Out of some paths it finds the common roots that need monitoring."""
|
|
125
|
+
paths = [x.split(os.path.sep) for x in paths]
|
|
126
|
+
root = {}
|
|
127
|
+
for chunks in sorted(paths, key=len, reverse=True):
|
|
128
|
+
node = root
|
|
129
|
+
for chunk in chunks:
|
|
130
|
+
node = node.setdefault(chunk, {})
|
|
131
|
+
node.clear()
|
|
132
|
+
|
|
133
|
+
rv = set()
|
|
134
|
+
|
|
135
|
+
def _walk(node, path):
|
|
136
|
+
for prefix, child in iteritems(node):
|
|
137
|
+
_walk(child, path + (prefix,))
|
|
138
|
+
if not node:
|
|
139
|
+
rv.add("/".join(path))
|
|
140
|
+
|
|
141
|
+
_walk(root, ())
|
|
142
|
+
return rv
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class ReloaderLoop(object):
|
|
146
|
+
name = None
|
|
147
|
+
|
|
148
|
+
# monkeypatched by testsuite. wrapping with `staticmethod` is required in
|
|
149
|
+
# case time.sleep has been replaced by a non-c function (e.g. by
|
|
150
|
+
# `eventlet.monkey_patch`) before we get here
|
|
151
|
+
_sleep = staticmethod(time.sleep)
|
|
152
|
+
|
|
153
|
+
def __init__(self, extra_files=None, interval=1):
|
|
154
|
+
self.extra_files = set(os.path.abspath(x) for x in extra_files or ())
|
|
155
|
+
self.interval = interval
|
|
156
|
+
|
|
157
|
+
def run(self):
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
def restart_with_reloader(self):
|
|
161
|
+
"""Spawn a new Python interpreter with the same arguments as this one,
|
|
162
|
+
but running the reloader thread.
|
|
163
|
+
"""
|
|
164
|
+
while 1:
|
|
165
|
+
_log("info", " * Restarting with %s" % self.name)
|
|
166
|
+
args = _get_args_for_reloading()
|
|
167
|
+
|
|
168
|
+
# a weird bug on windows. sometimes unicode strings end up in the
|
|
169
|
+
# environment and subprocess.call does not like this, encode them
|
|
170
|
+
# to latin1 and continue.
|
|
171
|
+
if os.name == "nt" and PY2:
|
|
172
|
+
new_environ = {}
|
|
173
|
+
for key, value in iteritems(os.environ):
|
|
174
|
+
if isinstance(key, text_type):
|
|
175
|
+
key = key.encode("iso-8859-1")
|
|
176
|
+
if isinstance(value, text_type):
|
|
177
|
+
value = value.encode("iso-8859-1")
|
|
178
|
+
new_environ[key] = value
|
|
179
|
+
else:
|
|
180
|
+
new_environ = os.environ.copy()
|
|
181
|
+
|
|
182
|
+
new_environ["WERKZEUG_RUN_MAIN"] = "true"
|
|
183
|
+
exit_code = subprocess.call(args, env=new_environ, close_fds=False)
|
|
184
|
+
if exit_code != 3:
|
|
185
|
+
return exit_code
|
|
186
|
+
|
|
187
|
+
def trigger_reload(self, filename):
|
|
188
|
+
self.log_reload(filename)
|
|
189
|
+
sys.exit(3)
|
|
190
|
+
|
|
191
|
+
def log_reload(self, filename):
|
|
192
|
+
filename = os.path.abspath(filename)
|
|
193
|
+
_log("info", " * Detected change in %r, reloading" % filename)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class StatReloaderLoop(ReloaderLoop):
|
|
197
|
+
name = "stat"
|
|
198
|
+
|
|
199
|
+
def run(self):
|
|
200
|
+
mtimes = {}
|
|
201
|
+
while 1:
|
|
202
|
+
for filename in chain(_iter_module_files(), self.extra_files):
|
|
203
|
+
try:
|
|
204
|
+
mtime = os.stat(filename).st_mtime
|
|
205
|
+
except OSError:
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
old_time = mtimes.get(filename)
|
|
209
|
+
if old_time is None:
|
|
210
|
+
mtimes[filename] = mtime
|
|
211
|
+
continue
|
|
212
|
+
elif mtime > old_time:
|
|
213
|
+
self.trigger_reload(filename)
|
|
214
|
+
self._sleep(self.interval)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class WatchdogReloaderLoop(ReloaderLoop):
|
|
218
|
+
def __init__(self, *args, **kwargs):
|
|
219
|
+
ReloaderLoop.__init__(self, *args, **kwargs)
|
|
220
|
+
from watchdog.observers import Observer
|
|
221
|
+
from watchdog.events import FileSystemEventHandler
|
|
222
|
+
|
|
223
|
+
self.observable_paths = set()
|
|
224
|
+
|
|
225
|
+
def _check_modification(filename):
|
|
226
|
+
if filename in self.extra_files:
|
|
227
|
+
self.trigger_reload(filename)
|
|
228
|
+
dirname = os.path.dirname(filename)
|
|
229
|
+
if dirname.startswith(tuple(self.observable_paths)):
|
|
230
|
+
if filename.endswith((".pyc", ".pyo", ".py")):
|
|
231
|
+
self.trigger_reload(filename)
|
|
232
|
+
|
|
233
|
+
class _CustomHandler(FileSystemEventHandler):
|
|
234
|
+
def on_created(self, event):
|
|
235
|
+
_check_modification(event.src_path)
|
|
236
|
+
|
|
237
|
+
def on_modified(self, event):
|
|
238
|
+
_check_modification(event.src_path)
|
|
239
|
+
|
|
240
|
+
def on_moved(self, event):
|
|
241
|
+
_check_modification(event.src_path)
|
|
242
|
+
_check_modification(event.dest_path)
|
|
243
|
+
|
|
244
|
+
def on_deleted(self, event):
|
|
245
|
+
_check_modification(event.src_path)
|
|
246
|
+
|
|
247
|
+
reloader_name = Observer.__name__.lower()
|
|
248
|
+
if reloader_name.endswith("observer"):
|
|
249
|
+
reloader_name = reloader_name[:-8]
|
|
250
|
+
reloader_name += " reloader"
|
|
251
|
+
|
|
252
|
+
self.name = reloader_name
|
|
253
|
+
|
|
254
|
+
self.observer_class = Observer
|
|
255
|
+
self.event_handler = _CustomHandler()
|
|
256
|
+
self.should_reload = False
|
|
257
|
+
|
|
258
|
+
def trigger_reload(self, filename):
|
|
259
|
+
# This is called inside an event handler, which means throwing
|
|
260
|
+
# SystemExit has no effect.
|
|
261
|
+
# https://github.com/gorakhargosh/watchdog/issues/294
|
|
262
|
+
self.should_reload = True
|
|
263
|
+
self.log_reload(filename)
|
|
264
|
+
|
|
265
|
+
def run(self):
|
|
266
|
+
watches = {}
|
|
267
|
+
observer = self.observer_class()
|
|
268
|
+
observer.start()
|
|
269
|
+
|
|
270
|
+
try:
|
|
271
|
+
while not self.should_reload:
|
|
272
|
+
to_delete = set(watches)
|
|
273
|
+
paths = _find_observable_paths(self.extra_files)
|
|
274
|
+
for path in paths:
|
|
275
|
+
if path not in watches:
|
|
276
|
+
try:
|
|
277
|
+
watches[path] = observer.schedule(
|
|
278
|
+
self.event_handler, path, recursive=True
|
|
279
|
+
)
|
|
280
|
+
except OSError:
|
|
281
|
+
# Clear this path from list of watches We don't want
|
|
282
|
+
# the same error message showing again in the next
|
|
283
|
+
# iteration.
|
|
284
|
+
watches[path] = None
|
|
285
|
+
to_delete.discard(path)
|
|
286
|
+
for path in to_delete:
|
|
287
|
+
watch = watches.pop(path, None)
|
|
288
|
+
if watch is not None:
|
|
289
|
+
observer.unschedule(watch)
|
|
290
|
+
self.observable_paths = paths
|
|
291
|
+
self._sleep(self.interval)
|
|
292
|
+
finally:
|
|
293
|
+
observer.stop()
|
|
294
|
+
observer.join()
|
|
295
|
+
|
|
296
|
+
sys.exit(3)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
reloader_loops = {"stat": StatReloaderLoop, "watchdog": WatchdogReloaderLoop}
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
__import__("watchdog.observers")
|
|
303
|
+
except ImportError:
|
|
304
|
+
reloader_loops["auto"] = reloader_loops["stat"]
|
|
305
|
+
else:
|
|
306
|
+
reloader_loops["auto"] = reloader_loops["watchdog"]
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def ensure_echo_on():
|
|
310
|
+
"""Ensure that echo mode is enabled. Some tools such as PDB disable
|
|
311
|
+
it which causes usability issues after reload."""
|
|
312
|
+
# tcgetattr will fail if stdin isn't a tty
|
|
313
|
+
if not sys.stdin.isatty():
|
|
314
|
+
return
|
|
315
|
+
try:
|
|
316
|
+
import termios
|
|
317
|
+
except ImportError:
|
|
318
|
+
return
|
|
319
|
+
attributes = termios.tcgetattr(sys.stdin)
|
|
320
|
+
if not attributes[3] & termios.ECHO:
|
|
321
|
+
attributes[3] |= termios.ECHO
|
|
322
|
+
termios.tcsetattr(sys.stdin, termios.TCSANOW, attributes)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def run_with_reloader(main_func, extra_files=None, interval=1, reloader_type="auto"):
|
|
326
|
+
"""Run the given function in an independent python interpreter."""
|
|
327
|
+
import signal
|
|
328
|
+
|
|
329
|
+
reloader = reloader_loops[reloader_type](extra_files, interval)
|
|
330
|
+
signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
|
|
331
|
+
try:
|
|
332
|
+
if os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
|
333
|
+
ensure_echo_on()
|
|
334
|
+
t = threading.Thread(target=main_func, args=())
|
|
335
|
+
t.setDaemon(True)
|
|
336
|
+
t.start()
|
|
337
|
+
reloader.run()
|
|
338
|
+
else:
|
|
339
|
+
sys.exit(reloader.restart_with_reloader())
|
|
340
|
+
except KeyboardInterrupt:
|
|
341
|
+
pass
|