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,1117 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ werkzeug.serving
4
+ ~~~~~~~~~~~~~~~~
5
+
6
+ There are many ways to serve a WSGI application. While you're developing
7
+ it you usually don't want a full blown webserver like Apache but a simple
8
+ standalone one. From Python 2.5 onwards there is the `wsgiref`_ server in
9
+ the standard library. If you're using older versions of Python you can
10
+ download the package from the cheeseshop.
11
+
12
+ However there are some caveats. Sourcecode won't reload itself when
13
+ changed and each time you kill the server using ``^C`` you get an
14
+ `KeyboardInterrupt` error. While the latter is easy to solve the first
15
+ one can be a pain in the ass in some situations.
16
+
17
+ The easiest way is creating a small ``start-myproject.py`` that runs the
18
+ application::
19
+
20
+ #!/usr/bin/env python
21
+ # -*- coding: utf-8 -*-
22
+ from myproject import make_app
23
+ from pythonagent.vendor.werkzeug.serving import run_simple
24
+
25
+ app = make_app(...)
26
+ run_simple('localhost', 8080, app, use_reloader=True)
27
+
28
+ You can also pass it a `extra_files` keyword argument with a list of
29
+ additional files (like configuration files) you want to observe.
30
+
31
+ For bigger applications you should consider using `click`
32
+ (http://click.pocoo.org) instead of a simple start file.
33
+
34
+
35
+ :copyright: 2007 Pallets
36
+ :license: BSD-3-Clause
37
+ """
38
+ import io
39
+ import os
40
+ import signal
41
+ import socket
42
+ import sys
43
+ from datetime import datetime as dt
44
+ from datetime import timedelta
45
+
46
+ from ._compat import PY2
47
+ from ._compat import reraise
48
+ from ._compat import WIN
49
+ from ._compat import wsgi_encoding_dance
50
+ from ._internal import _log
51
+ from .exceptions import InternalServerError
52
+ from .urls import uri_to_iri
53
+ from .urls import url_parse
54
+ from .urls import url_unquote
55
+
56
+ try:
57
+ import socketserver
58
+ from http.server import BaseHTTPRequestHandler
59
+ from http.server import HTTPServer
60
+ except ImportError:
61
+ import SocketServer as socketserver
62
+ from BaseHTTPServer import HTTPServer
63
+ from BaseHTTPServer import BaseHTTPRequestHandler
64
+
65
+ try:
66
+ import ssl
67
+ except ImportError:
68
+
69
+ class _SslDummy(object):
70
+ def __getattr__(self, name):
71
+ raise RuntimeError("SSL support unavailable")
72
+
73
+ ssl = _SslDummy()
74
+
75
+ try:
76
+ import click
77
+ except ImportError:
78
+ click = None
79
+
80
+
81
+ ThreadingMixIn = socketserver.ThreadingMixIn
82
+ can_fork = hasattr(os, "fork")
83
+
84
+ if can_fork:
85
+ ForkingMixIn = socketserver.ForkingMixIn
86
+ else:
87
+
88
+ class ForkingMixIn(object):
89
+ pass
90
+
91
+
92
+ try:
93
+ af_unix = socket.AF_UNIX
94
+ except AttributeError:
95
+ af_unix = None
96
+
97
+
98
+ LISTEN_QUEUE = 128
99
+ can_open_by_fd = not WIN and hasattr(socket, "fromfd")
100
+
101
+ # On Python 3, ConnectionError represents the same errnos as
102
+ # socket.error from Python 2, while socket.error is an alias for the
103
+ # more generic OSError.
104
+ if PY2:
105
+ _ConnectionError = socket.error
106
+ else:
107
+ _ConnectionError = ConnectionError
108
+
109
+
110
+ class DechunkedInput(io.RawIOBase):
111
+ """An input stream that handles Transfer-Encoding 'chunked'"""
112
+
113
+ def __init__(self, rfile):
114
+ self._rfile = rfile
115
+ self._done = False
116
+ self._len = 0
117
+
118
+ def readable(self):
119
+ return True
120
+
121
+ def read_chunk_len(self):
122
+ try:
123
+ line = self._rfile.readline().decode("latin1")
124
+ _len = int(line.strip(), 16)
125
+ except ValueError:
126
+ raise IOError("Invalid chunk header")
127
+ if _len < 0:
128
+ raise IOError("Negative chunk length not allowed")
129
+ return _len
130
+
131
+ def readinto(self, buf):
132
+ read = 0
133
+ while not self._done and read < len(buf):
134
+ if self._len == 0:
135
+ # This is the first chunk or we fully consumed the previous
136
+ # one. Read the next length of the next chunk
137
+ self._len = self.read_chunk_len()
138
+
139
+ if self._len == 0:
140
+ # Found the final chunk of size 0. The stream is now exhausted,
141
+ # but there is still a final newline that should be consumed
142
+ self._done = True
143
+
144
+ if self._len > 0:
145
+ # There is data (left) in this chunk, so append it to the
146
+ # buffer. If this operation fully consumes the chunk, this will
147
+ # reset self._len to 0.
148
+ n = min(len(buf), self._len)
149
+ buf[read : read + n] = self._rfile.read(n)
150
+ self._len -= n
151
+ read += n
152
+
153
+ if self._len == 0:
154
+ # Skip the terminating newline of a chunk that has been fully
155
+ # consumed. This also applies to the 0-sized final chunk
156
+ terminator = self._rfile.readline()
157
+ if terminator not in (b"\n", b"\r\n", b"\r"):
158
+ raise IOError("Missing chunk terminating newline")
159
+
160
+ return read
161
+
162
+
163
+ class WSGIRequestHandler(BaseHTTPRequestHandler, object):
164
+
165
+ """A request handler that implements WSGI dispatching."""
166
+
167
+ @property
168
+ def server_version(self):
169
+ from . import __version__
170
+
171
+ return "Werkzeug/" + __version__
172
+
173
+ def make_environ(self):
174
+ request_url = url_parse(self.path)
175
+
176
+ def shutdown_server():
177
+ self.server.shutdown_signal = True
178
+
179
+ url_scheme = "http" if self.server.ssl_context is None else "https"
180
+ if not self.client_address:
181
+ self.client_address = "<local>"
182
+ if isinstance(self.client_address, str):
183
+ self.client_address = (self.client_address, 0)
184
+ else:
185
+ pass
186
+
187
+ # If there was no scheme but the path started with two slashes,
188
+ # the first segment may have been incorrectly parsed as the
189
+ # netloc, prepend it to the path again.
190
+ if not request_url.scheme and request_url.netloc:
191
+ path_info = "/%s%s" % (request_url.netloc, request_url.path)
192
+ else:
193
+ path_info = request_url.path
194
+
195
+ path_info = url_unquote(path_info)
196
+
197
+ environ = {
198
+ "wsgi.version": (1, 0),
199
+ "wsgi.url_scheme": url_scheme,
200
+ "wsgi.input": self.rfile,
201
+ "wsgi.errors": sys.stderr,
202
+ "wsgi.multithread": self.server.multithread,
203
+ "wsgi.multiprocess": self.server.multiprocess,
204
+ "wsgi.run_once": False,
205
+ "werkzeug.server.shutdown": shutdown_server,
206
+ "SERVER_SOFTWARE": self.server_version,
207
+ "REQUEST_METHOD": self.command,
208
+ "SCRIPT_NAME": "",
209
+ "PATH_INFO": wsgi_encoding_dance(path_info),
210
+ "QUERY_STRING": wsgi_encoding_dance(request_url.query),
211
+ # Non-standard, added by mod_wsgi, uWSGI
212
+ "REQUEST_URI": wsgi_encoding_dance(self.path),
213
+ # Non-standard, added by gunicorn
214
+ "RAW_URI": wsgi_encoding_dance(self.path),
215
+ "REMOTE_ADDR": self.address_string(),
216
+ "REMOTE_PORT": self.port_integer(),
217
+ "SERVER_NAME": self.server.server_address[0],
218
+ "SERVER_PORT": str(self.server.server_address[1]),
219
+ "SERVER_PROTOCOL": self.request_version,
220
+ }
221
+
222
+ for key, value in self.get_header_items():
223
+ key = key.upper().replace("-", "_")
224
+ value = value.replace("\r\n", "")
225
+ if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"):
226
+ key = "HTTP_" + key
227
+ if key in environ:
228
+ value = "{},{}".format(environ[key], value)
229
+ environ[key] = value
230
+
231
+ if environ.get("HTTP_TRANSFER_ENCODING", "").strip().lower() == "chunked":
232
+ environ["wsgi.input_terminated"] = True
233
+ environ["wsgi.input"] = DechunkedInput(environ["wsgi.input"])
234
+
235
+ # Per RFC 2616, if the URL is absolute, use that as the host.
236
+ # We're using "has a scheme" to indicate an absolute URL.
237
+ if request_url.scheme and request_url.netloc:
238
+ environ["HTTP_HOST"] = request_url.netloc
239
+
240
+ try:
241
+ # binary_form=False gives nicer information, but wouldn't be compatible with
242
+ # what Nginx or Apache could return.
243
+ peer_cert = self.connection.getpeercert(binary_form=True)
244
+ if peer_cert is not None:
245
+ # Nginx and Apache use PEM format.
246
+ environ["SSL_CLIENT_CERT"] = ssl.DER_cert_to_PEM_cert(peer_cert)
247
+ except ValueError:
248
+ # SSL handshake hasn't finished.
249
+ self.server.log("error", "Cannot fetch SSL peer certificate info")
250
+ except AttributeError:
251
+ # Not using TLS, the socket will not have getpeercert().
252
+ pass
253
+
254
+ return environ
255
+
256
+ def run_wsgi(self):
257
+ if self.headers.get("Expect", "").lower().strip() == "100-continue":
258
+ self.wfile.write(b"HTTP/1.1 100 Continue\r\n\r\n")
259
+
260
+ self.environ = environ = self.make_environ()
261
+ headers_set = []
262
+ headers_sent = []
263
+
264
+ def write(data):
265
+ assert headers_set, "write() before start_response"
266
+ if not headers_sent:
267
+ status, response_headers = headers_sent[:] = headers_set
268
+ try:
269
+ code, msg = status.split(None, 1)
270
+ except ValueError:
271
+ code, msg = status, ""
272
+ code = int(code)
273
+ self.send_response(code, msg)
274
+ header_keys = set()
275
+ for key, value in response_headers:
276
+ self.send_header(key, value)
277
+ key = key.lower()
278
+ header_keys.add(key)
279
+ if not (
280
+ "content-length" in header_keys
281
+ or environ["REQUEST_METHOD"] == "HEAD"
282
+ or code < 200
283
+ or code in (204, 304)
284
+ ):
285
+ self.close_connection = True
286
+ self.send_header("Connection", "close")
287
+ if "server" not in header_keys:
288
+ self.send_header("Server", self.version_string())
289
+ if "date" not in header_keys:
290
+ self.send_header("Date", self.date_time_string())
291
+ self.end_headers()
292
+
293
+ assert isinstance(data, bytes), "applications must write bytes"
294
+ if data:
295
+ # Only write data if there is any to avoid Python 3.5 SSL bug
296
+ self.wfile.write(data)
297
+ self.wfile.flush()
298
+
299
+ def start_response(status, response_headers, exc_info=None):
300
+ if exc_info:
301
+ try:
302
+ if headers_sent:
303
+ reraise(*exc_info)
304
+ finally:
305
+ exc_info = None
306
+ elif headers_set:
307
+ raise AssertionError("Headers already set")
308
+ headers_set[:] = [status, response_headers]
309
+ return write
310
+
311
+ def execute(app):
312
+ application_iter = app(environ, start_response)
313
+ try:
314
+ for data in application_iter:
315
+ write(data)
316
+ if not headers_sent:
317
+ write(b"")
318
+ finally:
319
+ if hasattr(application_iter, "close"):
320
+ application_iter.close()
321
+
322
+ try:
323
+ execute(self.server.app)
324
+ except (_ConnectionError, socket.timeout) as e:
325
+ self.connection_dropped(e, environ)
326
+ except Exception:
327
+ if self.server.passthrough_errors:
328
+ raise
329
+ from .debug.tbtools import get_current_traceback
330
+
331
+ traceback = get_current_traceback(ignore_system_exceptions=True)
332
+ try:
333
+ # if we haven't yet sent the headers but they are set
334
+ # we roll back to be able to set them again.
335
+ if not headers_sent:
336
+ del headers_set[:]
337
+ execute(InternalServerError())
338
+ except Exception:
339
+ pass
340
+ self.server.log("error", "Error on request:\n%s", traceback.plaintext)
341
+
342
+ def handle(self):
343
+ """Handles a request ignoring dropped connections."""
344
+ try:
345
+ BaseHTTPRequestHandler.handle(self)
346
+ except (_ConnectionError, socket.timeout) as e:
347
+ self.connection_dropped(e)
348
+ except Exception as e:
349
+ if self.server.ssl_context is None or not is_ssl_error(e):
350
+ raise
351
+ if self.server.shutdown_signal:
352
+ self.initiate_shutdown()
353
+
354
+ def initiate_shutdown(self):
355
+ """A horrible, horrible way to kill the server for Python 2.6 and
356
+ later. It's the best we can do.
357
+ """
358
+ # Windows does not provide SIGKILL, go with SIGTERM then.
359
+ sig = getattr(signal, "SIGKILL", signal.SIGTERM)
360
+ # reloader active
361
+ if is_running_from_reloader():
362
+ os.kill(os.getpid(), sig)
363
+ # python 2.7
364
+ self.server._BaseServer__shutdown_request = True
365
+ # python 2.6
366
+ self.server._BaseServer__serving = False
367
+
368
+ def connection_dropped(self, error, environ=None):
369
+ """Called if the connection was closed by the client. By default
370
+ nothing happens.
371
+ """
372
+
373
+ def handle_one_request(self):
374
+ """Handle a single HTTP request."""
375
+ self.raw_requestline = self.rfile.readline()
376
+ if not self.raw_requestline:
377
+ self.close_connection = 1
378
+ elif self.parse_request():
379
+ return self.run_wsgi()
380
+
381
+ def send_response(self, code, message=None):
382
+ """Send the response header and log the response code."""
383
+ self.log_request(code)
384
+ if message is None:
385
+ message = code in self.responses and self.responses[code][0] or ""
386
+ if self.request_version != "HTTP/0.9":
387
+ hdr = "%s %d %s\r\n" % (self.protocol_version, code, message)
388
+ self.wfile.write(hdr.encode("ascii"))
389
+
390
+ def version_string(self):
391
+ return BaseHTTPRequestHandler.version_string(self).strip()
392
+
393
+ def address_string(self):
394
+ if getattr(self, "environ", None):
395
+ return self.environ["REMOTE_ADDR"]
396
+ elif not self.client_address:
397
+ return "<local>"
398
+ elif isinstance(self.client_address, str):
399
+ return self.client_address
400
+ else:
401
+ return self.client_address[0]
402
+
403
+ def port_integer(self):
404
+ return self.client_address[1]
405
+
406
+ def log_request(self, code="-", size="-"):
407
+ try:
408
+ path = uri_to_iri(self.path)
409
+ msg = "%s %s %s" % (self.command, path, self.request_version)
410
+ except AttributeError:
411
+ # path isn't set if the requestline was bad
412
+ msg = self.requestline
413
+
414
+ code = str(code)
415
+
416
+ if click:
417
+ color = click.style
418
+
419
+ if code[0] == "1": # 1xx - Informational
420
+ msg = color(msg, bold=True)
421
+ elif code[0] == "2": # 2xx - Success
422
+ msg = color(msg, fg="white")
423
+ elif code == "304": # 304 - Resource Not Modified
424
+ msg = color(msg, fg="cyan")
425
+ elif code[0] == "3": # 3xx - Redirection
426
+ msg = color(msg, fg="green")
427
+ elif code == "404": # 404 - Resource Not Found
428
+ msg = color(msg, fg="yellow")
429
+ elif code[0] == "4": # 4xx - Client Error
430
+ msg = color(msg, fg="red", bold=True)
431
+ else: # 5xx, or any other response
432
+ msg = color(msg, fg="magenta", bold=True)
433
+
434
+ self.log("info", '"%s" %s %s', msg, code, size)
435
+
436
+ def log_error(self, *args):
437
+ self.log("error", *args)
438
+
439
+ def log_message(self, format, *args):
440
+ self.log("info", format, *args)
441
+
442
+ def log(self, type, message, *args):
443
+ _log(
444
+ type,
445
+ "%s - - [%s] %s\n"
446
+ % (self.address_string(), self.log_date_time_string(), message % args),
447
+ )
448
+
449
+ def get_header_items(self):
450
+ """
451
+ Get an iterable list of key/value pairs representing headers.
452
+
453
+ This function provides Python 2/3 compatibility as related to the
454
+ parsing of request headers. Python 2.7 is not compliant with
455
+ RFC 3875 Section 4.1.18 which requires multiple values for headers
456
+ to be provided or RFC 2616 which allows for folding of multi-line
457
+ headers. This function will return a matching list regardless
458
+ of Python version. It can be removed once Python 2.7 support
459
+ is dropped.
460
+
461
+ :return: List of tuples containing header hey/value pairs
462
+ """
463
+ if PY2:
464
+ # For Python 2, process the headers manually according to
465
+ # W3C RFC 2616 Section 4.2.
466
+ items = []
467
+ for header in self.headers.headers:
468
+ # Remove "\r\n" from the header and split on ":" to get
469
+ # the field name and value.
470
+ try:
471
+ key, value = header[0:-2].split(":", 1)
472
+ except ValueError:
473
+ # If header could not be slit with : but starts with white
474
+ # space and it follows an existing header, it's a folded
475
+ # header.
476
+ if header[0] in ("\t", " ") and items:
477
+ # Pop off the last header
478
+ key, value = items.pop()
479
+ # Append the current header to the value of the last
480
+ # header which will be placed back on the end of the
481
+ # list
482
+ value = value + header
483
+ # Otherwise it's just a bad header and should error
484
+ else:
485
+ # Re-raise the value error
486
+ raise
487
+
488
+ # Add the key and the value once stripped of leading
489
+ # white space. The specification allows for stripping
490
+ # trailing white space but the Python 3 code does not
491
+ # strip trailing white space. Therefore, trailing space
492
+ # will be left as is to match the Python 3 behavior.
493
+ items.append((key, value.lstrip()))
494
+ else:
495
+ items = self.headers.items()
496
+
497
+ return items
498
+
499
+
500
+ #: backwards compatible name if someone is subclassing it
501
+ BaseRequestHandler = WSGIRequestHandler
502
+
503
+
504
+ def generate_adhoc_ssl_pair(cn=None):
505
+ try:
506
+ from cryptography import x509
507
+ from cryptography.x509.oid import NameOID
508
+ from cryptography.hazmat.backends import default_backend
509
+ from cryptography.hazmat.primitives import hashes
510
+ from cryptography.hazmat.primitives.asymmetric import rsa
511
+ except ImportError:
512
+ raise TypeError("Using ad-hoc certificates requires the cryptography library.")
513
+ pkey = rsa.generate_private_key(
514
+ public_exponent=65537, key_size=2048, backend=default_backend()
515
+ )
516
+
517
+ # pretty damn sure that this is not actually accepted by anyone
518
+ if cn is None:
519
+ cn = u"*"
520
+
521
+ subject = x509.Name(
522
+ [
523
+ x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"Dummy Certificate"),
524
+ x509.NameAttribute(NameOID.COMMON_NAME, cn),
525
+ ]
526
+ )
527
+
528
+ cert = (
529
+ x509.CertificateBuilder()
530
+ .subject_name(subject)
531
+ .issuer_name(subject)
532
+ .public_key(pkey.public_key())
533
+ .serial_number(x509.random_serial_number())
534
+ .not_valid_before(dt.utcnow())
535
+ .not_valid_after(dt.utcnow() + timedelta(days=365))
536
+ .add_extension(x509.ExtendedKeyUsage([x509.OID_SERVER_AUTH]), critical=False)
537
+ .add_extension(
538
+ x509.SubjectAlternativeName([x509.DNSName(u"*")]), critical=False
539
+ )
540
+ .sign(pkey, hashes.SHA256(), default_backend())
541
+ )
542
+ return cert, pkey
543
+
544
+
545
+ def make_ssl_devcert(base_path, host=None, cn=None):
546
+ """Creates an SSL key for development. This should be used instead of
547
+ the ``'adhoc'`` key which generates a new cert on each server start.
548
+ It accepts a path for where it should store the key and cert and
549
+ either a host or CN. If a host is given it will use the CN
550
+ ``*.host/CN=host``.
551
+
552
+ For more information see :func:`run_simple`.
553
+
554
+ .. versionadded:: 0.9
555
+
556
+ :param base_path: the path to the certificate and key. The extension
557
+ ``.crt`` is added for the certificate, ``.key`` is
558
+ added for the key.
559
+ :param host: the name of the host. This can be used as an alternative
560
+ for the `cn`.
561
+ :param cn: the `CN` to use.
562
+ """
563
+
564
+ if host is not None:
565
+ cn = u"*.%s/CN=%s" % (host, host)
566
+ cert, pkey = generate_adhoc_ssl_pair(cn=cn)
567
+
568
+ from cryptography.hazmat.primitives import serialization
569
+
570
+ cert_file = base_path + ".crt"
571
+ pkey_file = base_path + ".key"
572
+
573
+ with open(cert_file, "wb") as f:
574
+ f.write(cert.public_bytes(serialization.Encoding.PEM))
575
+ with open(pkey_file, "wb") as f:
576
+ f.write(
577
+ pkey.private_bytes(
578
+ encoding=serialization.Encoding.PEM,
579
+ format=serialization.PrivateFormat.TraditionalOpenSSL,
580
+ encryption_algorithm=serialization.NoEncryption(),
581
+ )
582
+ )
583
+
584
+ return cert_file, pkey_file
585
+
586
+
587
+ def generate_adhoc_ssl_context():
588
+ """Generates an adhoc SSL context for the development server."""
589
+ import tempfile
590
+ import atexit
591
+
592
+ cert, pkey = generate_adhoc_ssl_pair()
593
+
594
+ from cryptography.hazmat.primitives import serialization
595
+
596
+ cert_handle, cert_file = tempfile.mkstemp()
597
+ pkey_handle, pkey_file = tempfile.mkstemp()
598
+ atexit.register(os.remove, pkey_file)
599
+ atexit.register(os.remove, cert_file)
600
+
601
+ os.write(cert_handle, cert.public_bytes(serialization.Encoding.PEM))
602
+ os.write(
603
+ pkey_handle,
604
+ pkey.private_bytes(
605
+ encoding=serialization.Encoding.PEM,
606
+ format=serialization.PrivateFormat.TraditionalOpenSSL,
607
+ encryption_algorithm=serialization.NoEncryption(),
608
+ ),
609
+ )
610
+
611
+ os.close(cert_handle)
612
+ os.close(pkey_handle)
613
+ ctx = load_ssl_context(cert_file, pkey_file)
614
+ return ctx
615
+
616
+
617
+ def load_ssl_context(cert_file, pkey_file=None, protocol=None):
618
+ """Loads SSL context from cert/private key files and optional protocol.
619
+ Many parameters are directly taken from the API of
620
+ :py:class:`ssl.SSLContext`.
621
+
622
+ :param cert_file: Path of the certificate to use.
623
+ :param pkey_file: Path of the private key to use. If not given, the key
624
+ will be obtained from the certificate file.
625
+ :param protocol: One of the ``PROTOCOL_*`` constants in the stdlib ``ssl``
626
+ module. Defaults to ``PROTOCOL_SSLv23``.
627
+ """
628
+ if protocol is None:
629
+ try:
630
+ protocol = ssl.PROTOCOL_TLS_SERVER
631
+ except AttributeError:
632
+ # Python <= 3.5 compat
633
+ protocol = ssl.PROTOCOL_SSLv23
634
+ ctx = _SSLContext(protocol)
635
+ ctx.load_cert_chain(cert_file, pkey_file)
636
+ return ctx
637
+
638
+
639
+ class _SSLContext(object):
640
+
641
+ """A dummy class with a small subset of Python3's ``ssl.SSLContext``, only
642
+ intended to be used with and by Werkzeug."""
643
+
644
+ def __init__(self, protocol):
645
+ self._protocol = protocol
646
+ self._certfile = None
647
+ self._keyfile = None
648
+ self._password = None
649
+
650
+ def load_cert_chain(self, certfile, keyfile=None, password=None):
651
+ self._certfile = certfile
652
+ self._keyfile = keyfile or certfile
653
+ self._password = password
654
+
655
+ def wrap_socket(self, sock, **kwargs):
656
+ return ssl.wrap_socket(
657
+ sock,
658
+ keyfile=self._keyfile,
659
+ certfile=self._certfile,
660
+ ssl_version=self._protocol,
661
+ **kwargs
662
+ )
663
+
664
+
665
+ def is_ssl_error(error=None):
666
+ """Checks if the given error (or the current one) is an SSL error."""
667
+ if error is None:
668
+ error = sys.exc_info()[1]
669
+ return isinstance(error, ssl.SSLError)
670
+
671
+
672
+ def select_address_family(host, port):
673
+ """Return ``AF_INET4``, ``AF_INET6``, or ``AF_UNIX`` depending on
674
+ the host and port."""
675
+ # disabled due to problems with current ipv6 implementations
676
+ # and various operating systems. Probably this code also is
677
+ # not supposed to work, but I can't come up with any other
678
+ # ways to implement this.
679
+ # try:
680
+ # info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
681
+ # socket.SOCK_STREAM, 0,
682
+ # socket.AI_PASSIVE)
683
+ # if info:
684
+ # return info[0][0]
685
+ # except socket.gaierror:
686
+ # pass
687
+ if host.startswith("unix://"):
688
+ return socket.AF_UNIX
689
+ elif ":" in host and hasattr(socket, "AF_INET6"):
690
+ return socket.AF_INET6
691
+ return socket.AF_INET
692
+
693
+
694
+ def get_sockaddr(host, port, family):
695
+ """Return a fully qualified socket address that can be passed to
696
+ :func:`socket.bind`."""
697
+ if family == af_unix:
698
+ return host.split("://", 1)[1]
699
+ try:
700
+ res = socket.getaddrinfo(
701
+ host, port, family, socket.SOCK_STREAM, socket.IPPROTO_TCP
702
+ )
703
+ except socket.gaierror:
704
+ return host, port
705
+ return res[0][4]
706
+
707
+
708
+ class BaseWSGIServer(HTTPServer, object):
709
+
710
+ """Simple single-threaded, single-process WSGI server."""
711
+
712
+ multithread = False
713
+ multiprocess = False
714
+ request_queue_size = LISTEN_QUEUE
715
+
716
+ def __init__(
717
+ self,
718
+ host,
719
+ port,
720
+ app,
721
+ handler=None,
722
+ passthrough_errors=False,
723
+ ssl_context=None,
724
+ fd=None,
725
+ ):
726
+ if handler is None:
727
+ handler = WSGIRequestHandler
728
+
729
+ self.address_family = select_address_family(host, port)
730
+
731
+ if fd is not None:
732
+ real_sock = socket.fromfd(fd, self.address_family, socket.SOCK_STREAM)
733
+ port = 0
734
+
735
+ server_address = get_sockaddr(host, int(port), self.address_family)
736
+
737
+ # remove socket file if it already exists
738
+ if self.address_family == af_unix and os.path.exists(server_address):
739
+ os.unlink(server_address)
740
+ HTTPServer.__init__(self, server_address, handler)
741
+
742
+ self.app = app
743
+ self.passthrough_errors = passthrough_errors
744
+ self.shutdown_signal = False
745
+ self.host = host
746
+ self.port = self.socket.getsockname()[1]
747
+
748
+ # Patch in the original socket.
749
+ if fd is not None:
750
+ self.socket.close()
751
+ self.socket = real_sock
752
+ self.server_address = self.socket.getsockname()
753
+
754
+ if ssl_context is not None:
755
+ if isinstance(ssl_context, tuple):
756
+ ssl_context = load_ssl_context(*ssl_context)
757
+ if ssl_context == "adhoc":
758
+ ssl_context = generate_adhoc_ssl_context()
759
+
760
+ # If we are on Python 2 the return value from socket.fromfd
761
+ # is an internal socket object but what we need for ssl wrap
762
+ # is the wrapper around it :(
763
+ sock = self.socket
764
+ if PY2 and not isinstance(sock, socket.socket):
765
+ sock = socket.socket(sock.family, sock.type, sock.proto, sock)
766
+ self.socket = ssl_context.wrap_socket(sock, server_side=True)
767
+ self.ssl_context = ssl_context
768
+ else:
769
+ self.ssl_context = None
770
+
771
+ def log(self, type, message, *args):
772
+ _log(type, message, *args)
773
+
774
+ def serve_forever(self):
775
+ self.shutdown_signal = False
776
+ try:
777
+ HTTPServer.serve_forever(self)
778
+ except KeyboardInterrupt:
779
+ pass
780
+ finally:
781
+ self.server_close()
782
+
783
+ def handle_error(self, request, client_address):
784
+ if self.passthrough_errors:
785
+ raise
786
+ # Python 2 still causes a socket.error after the earlier
787
+ # handling, so silence it here.
788
+ if isinstance(sys.exc_info()[1], _ConnectionError):
789
+ return
790
+ return HTTPServer.handle_error(self, request, client_address)
791
+
792
+ def get_request(self):
793
+ con, info = self.socket.accept()
794
+ return con, info
795
+
796
+
797
+ class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):
798
+
799
+ """A WSGI server that does threading."""
800
+
801
+ multithread = True
802
+ daemon_threads = True
803
+
804
+
805
+ class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):
806
+
807
+ """A WSGI server that does forking."""
808
+
809
+ multiprocess = True
810
+
811
+ def __init__(
812
+ self,
813
+ host,
814
+ port,
815
+ app,
816
+ processes=40,
817
+ handler=None,
818
+ passthrough_errors=False,
819
+ ssl_context=None,
820
+ fd=None,
821
+ ):
822
+ if not can_fork:
823
+ raise ValueError("Your platform does not support forking.")
824
+ BaseWSGIServer.__init__(
825
+ self, host, port, app, handler, passthrough_errors, ssl_context, fd
826
+ )
827
+ self.max_children = processes
828
+
829
+
830
+ def make_server(
831
+ host=None,
832
+ port=None,
833
+ app=None,
834
+ threaded=False,
835
+ processes=1,
836
+ request_handler=None,
837
+ passthrough_errors=False,
838
+ ssl_context=None,
839
+ fd=None,
840
+ ):
841
+ """Create a new server instance that is either threaded, or forks
842
+ or just processes one request after another.
843
+ """
844
+ if threaded and processes > 1:
845
+ raise ValueError("cannot have a multithreaded and multi process server.")
846
+ elif threaded:
847
+ return ThreadedWSGIServer(
848
+ host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
849
+ )
850
+ elif processes > 1:
851
+ return ForkingWSGIServer(
852
+ host,
853
+ port,
854
+ app,
855
+ processes,
856
+ request_handler,
857
+ passthrough_errors,
858
+ ssl_context,
859
+ fd=fd,
860
+ )
861
+ else:
862
+ return BaseWSGIServer(
863
+ host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
864
+ )
865
+
866
+
867
+ def is_running_from_reloader():
868
+ """Checks if the application is running from within the Werkzeug
869
+ reloader subprocess.
870
+
871
+ .. versionadded:: 0.10
872
+ """
873
+ return os.environ.get("WERKZEUG_RUN_MAIN") == "true"
874
+
875
+
876
+ def run_simple(
877
+ hostname,
878
+ port,
879
+ application,
880
+ use_reloader=False,
881
+ use_debugger=False,
882
+ use_evalex=True,
883
+ extra_files=None,
884
+ reloader_interval=1,
885
+ reloader_type="auto",
886
+ threaded=False,
887
+ processes=1,
888
+ request_handler=None,
889
+ static_files=None,
890
+ passthrough_errors=False,
891
+ ssl_context=None,
892
+ ):
893
+ """Start a WSGI application. Optional features include a reloader,
894
+ multithreading and fork support.
895
+
896
+ This function has a command-line interface too::
897
+
898
+ python -m werkzeug.serving --help
899
+
900
+ .. versionadded:: 0.5
901
+ `static_files` was added to simplify serving of static files as well
902
+ as `passthrough_errors`.
903
+
904
+ .. versionadded:: 0.6
905
+ support for SSL was added.
906
+
907
+ .. versionadded:: 0.8
908
+ Added support for automatically loading a SSL context from certificate
909
+ file and private key.
910
+
911
+ .. versionadded:: 0.9
912
+ Added command-line interface.
913
+
914
+ .. versionadded:: 0.10
915
+ Improved the reloader and added support for changing the backend
916
+ through the `reloader_type` parameter. See :ref:`reloader`
917
+ for more information.
918
+
919
+ .. versionchanged:: 0.15
920
+ Bind to a Unix socket by passing a path that starts with
921
+ ``unix://`` as the ``hostname``.
922
+
923
+ :param hostname: The host to bind to, for example ``'localhost'``.
924
+ If the value is a path that starts with ``unix://`` it will bind
925
+ to a Unix socket instead of a TCP socket..
926
+ :param port: The port for the server. eg: ``8080``
927
+ :param application: the WSGI application to execute
928
+ :param use_reloader: should the server automatically restart the python
929
+ process if modules were changed?
930
+ :param use_debugger: should the werkzeug debugging system be used?
931
+ :param use_evalex: should the exception evaluation feature be enabled?
932
+ :param extra_files: a list of files the reloader should watch
933
+ additionally to the modules. For example configuration
934
+ files.
935
+ :param reloader_interval: the interval for the reloader in seconds.
936
+ :param reloader_type: the type of reloader to use. The default is
937
+ auto detection. Valid values are ``'stat'`` and
938
+ ``'watchdog'``. See :ref:`reloader` for more
939
+ information.
940
+ :param threaded: should the process handle each request in a separate
941
+ thread?
942
+ :param processes: if greater than 1 then handle each request in a new process
943
+ up to this maximum number of concurrent processes.
944
+ :param request_handler: optional parameter that can be used to replace
945
+ the default one. You can use this to replace it
946
+ with a different
947
+ :class:`~BaseHTTPServer.BaseHTTPRequestHandler`
948
+ subclass.
949
+ :param static_files: a list or dict of paths for static files. This works
950
+ exactly like :class:`SharedDataMiddleware`, it's actually
951
+ just wrapping the application in that middleware before
952
+ serving.
953
+ :param passthrough_errors: set this to `True` to disable the error catching.
954
+ This means that the server will die on errors but
955
+ it can be useful to hook debuggers in (pdb etc.)
956
+ :param ssl_context: an SSL context for the connection. Either an
957
+ :class:`ssl.SSLContext`, a tuple in the form
958
+ ``(cert_file, pkey_file)``, the string ``'adhoc'`` if
959
+ the server should automatically create one, or ``None``
960
+ to disable SSL (which is the default).
961
+ """
962
+ if not isinstance(port, int):
963
+ raise TypeError("port must be an integer")
964
+ if use_debugger:
965
+ from .debug import DebuggedApplication
966
+
967
+ application = DebuggedApplication(application, use_evalex)
968
+ if static_files:
969
+ from .middleware.shared_data import SharedDataMiddleware
970
+
971
+ application = SharedDataMiddleware(application, static_files)
972
+
973
+ def log_startup(sock):
974
+ display_hostname = hostname if hostname not in ("", "*") else "localhost"
975
+ quit_msg = "(Press CTRL+C to quit)"
976
+ if sock.family == af_unix:
977
+ _log("info", " * Running on %s %s", display_hostname, quit_msg)
978
+ else:
979
+ if ":" in display_hostname:
980
+ display_hostname = "[%s]" % display_hostname
981
+ port = sock.getsockname()[1]
982
+ _log(
983
+ "info",
984
+ " * Running on %s://%s:%d/ %s",
985
+ "http" if ssl_context is None else "https",
986
+ display_hostname,
987
+ port,
988
+ quit_msg,
989
+ )
990
+
991
+ def inner():
992
+ try:
993
+ fd = int(os.environ["WERKZEUG_SERVER_FD"])
994
+ except (LookupError, ValueError):
995
+ fd = None
996
+ srv = make_server(
997
+ hostname,
998
+ port,
999
+ application,
1000
+ threaded,
1001
+ processes,
1002
+ request_handler,
1003
+ passthrough_errors,
1004
+ ssl_context,
1005
+ fd=fd,
1006
+ )
1007
+ if fd is None:
1008
+ log_startup(srv.socket)
1009
+ srv.serve_forever()
1010
+
1011
+ if use_reloader:
1012
+ # If we're not running already in the subprocess that is the
1013
+ # reloader we want to open up a socket early to make sure the
1014
+ # port is actually available.
1015
+ if not is_running_from_reloader():
1016
+ if port == 0 and not can_open_by_fd:
1017
+ raise ValueError(
1018
+ "Cannot bind to a random port with enabled "
1019
+ "reloader if the Python interpreter does "
1020
+ "not support socket opening by fd."
1021
+ )
1022
+
1023
+ # Create and destroy a socket so that any exceptions are
1024
+ # raised before we spawn a separate Python interpreter and
1025
+ # lose this ability.
1026
+ address_family = select_address_family(hostname, port)
1027
+ server_address = get_sockaddr(hostname, port, address_family)
1028
+ s = socket.socket(address_family, socket.SOCK_STREAM)
1029
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1030
+ s.bind(server_address)
1031
+ if hasattr(s, "set_inheritable"):
1032
+ s.set_inheritable(True)
1033
+
1034
+ # If we can open the socket by file descriptor, then we can just
1035
+ # reuse this one and our socket will survive the restarts.
1036
+ if can_open_by_fd:
1037
+ os.environ["WERKZEUG_SERVER_FD"] = str(s.fileno())
1038
+ s.listen(LISTEN_QUEUE)
1039
+ log_startup(s)
1040
+ else:
1041
+ s.close()
1042
+ if address_family == af_unix:
1043
+ _log("info", "Unlinking %s" % server_address)
1044
+ os.unlink(server_address)
1045
+
1046
+ # Do not use relative imports, otherwise "python -m werkzeug.serving"
1047
+ # breaks.
1048
+ from ._reloader import run_with_reloader
1049
+
1050
+ run_with_reloader(inner, extra_files, reloader_interval, reloader_type)
1051
+ else:
1052
+ inner()
1053
+
1054
+
1055
+ def run_with_reloader(*args, **kwargs):
1056
+ # People keep using undocumented APIs. Do not use this function
1057
+ # please, we do not guarantee that it continues working.
1058
+ from ._reloader import run_with_reloader
1059
+
1060
+ return run_with_reloader(*args, **kwargs)
1061
+
1062
+
1063
+ def main():
1064
+ """A simple command-line interface for :py:func:`run_simple`."""
1065
+
1066
+ # in contrast to argparse, this works at least under Python < 2.7
1067
+ import optparse
1068
+ from .utils import import_string
1069
+
1070
+ parser = optparse.OptionParser(usage="Usage: %prog [options] app_module:app_object")
1071
+ parser.add_option(
1072
+ "-b",
1073
+ "--bind",
1074
+ dest="address",
1075
+ help="The hostname:port the app should listen on.",
1076
+ )
1077
+ parser.add_option(
1078
+ "-d",
1079
+ "--debug",
1080
+ dest="use_debugger",
1081
+ action="store_true",
1082
+ default=False,
1083
+ help="Use Werkzeug's debugger.",
1084
+ )
1085
+ parser.add_option(
1086
+ "-r",
1087
+ "--reload",
1088
+ dest="use_reloader",
1089
+ action="store_true",
1090
+ default=False,
1091
+ help="Reload Python process if modules change.",
1092
+ )
1093
+ options, args = parser.parse_args()
1094
+
1095
+ hostname, port = None, None
1096
+ if options.address:
1097
+ address = options.address.split(":")
1098
+ hostname = address[0]
1099
+ if len(address) > 1:
1100
+ port = address[1]
1101
+
1102
+ if len(args) != 1:
1103
+ sys.stdout.write("No application supplied, or too much. See --help\n")
1104
+ sys.exit(1)
1105
+ app = import_string(args[0])
1106
+
1107
+ run_simple(
1108
+ hostname=(hostname or "127.0.0.1"),
1109
+ port=int(port or 5000),
1110
+ application=app,
1111
+ use_reloader=options.use_reloader,
1112
+ use_debugger=options.use_debugger,
1113
+ )
1114
+
1115
+
1116
+ if __name__ == "__main__":
1117
+ main()