mod-wsgi-standalone 6.0.0.dev3__tar.gz → 6.0.0.dev5__tar.gz
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.
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/PKG-INFO +1 -1
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/PKG-INFO +1 -1
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/diagnostics/environ.py +16 -19
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/apache.py +13 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/options.py +35 -2
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/scripts.py +21 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/server.py +52 -1
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_daemon.c +195 -2
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_interp.c +85 -83
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_interp.h +13 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_logger.c +308 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_logger.h +7 -4
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_metrics.c +123 -3
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_metrics.h +9 -5
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_module.c +12 -3
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_module.h +1 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_server.c +1 -1
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_version.h +1 -1
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/CREDITS.rst +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/LICENSE +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/MANIFEST.in +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/Makefile.in +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/README-express.rst +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/README-standalone.rst +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/README.rst +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/configure +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/configure.ac +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/images/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/images/snake-whiskey.jpg +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/SOURCES.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/dependency_links.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/entry_points.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/not-zip-safe +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/requires.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/top_level.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/pyproject.toml +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/setup.cfg +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/setup.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/diagnostics/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/diagnostics/hello.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/cli.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/management/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/management/commands/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/management/commands/runmodwsgi.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/platform.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/reloader.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/express/runtime.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/management/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/management/commands/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/management/commands/runmodwsgi.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/mod_wsgi.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_adapter.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_adapter.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_apache.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_apache.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_auth.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_auth.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_buckets.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_buckets.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_config.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_config.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_convert.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_convert.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_daemon.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_dispatch.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_dispatch.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_environ.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_environ.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_execute.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_execute.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_gc.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_gc.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_input.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_input.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_memory.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_memory.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_python.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_remote.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_remote.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_restrict.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_restrict.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_server.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_shutdown.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_shutdown.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_signal.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_signal.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_stream.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_stream.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_telemetry.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_telemetry.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_thread.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_thread.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_validate.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_validate.h +0 -0
{mod_wsgi_standalone-6.0.0.dev3 → mod_wsgi_standalone-6.0.0.dev5}/src/diagnostics/environ.py
RENAMED
|
@@ -15,10 +15,16 @@ def application(environ, start_response):
|
|
|
15
15
|
input = environ["wsgi.input"]
|
|
16
16
|
output = StringIO()
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
if os.name != "nt":
|
|
19
|
+
print(f"PID: {os.getpid()}", file=output)
|
|
20
|
+
print(f"UID: {os.getuid()}", file=output)
|
|
21
|
+
print(f"GID: {os.getgid()}", file=output)
|
|
22
|
+
print(f"CWD: {os.getcwd()}", file=output)
|
|
23
|
+
print(file=output)
|
|
24
|
+
|
|
25
|
+
print(f"STDOUT: {sys.stdout.name}", file=output)
|
|
26
|
+
print(f"STDERR: {sys.stderr.name}", file=output)
|
|
27
|
+
print(f'ERRORS: {environ["wsgi.errors"].name}', file=output)
|
|
22
28
|
print(file=output)
|
|
23
29
|
|
|
24
30
|
print(f"python.version: {sys.version!r}", file=output)
|
|
@@ -36,19 +42,8 @@ def application(environ, start_response):
|
|
|
36
42
|
|
|
37
43
|
print(f"mod_wsgi.maximum_processes: {mod_wsgi.maximum_processes}", file=output)
|
|
38
44
|
print(f"mod_wsgi.threads_per_process: {mod_wsgi.threads_per_process}", file=output)
|
|
39
|
-
print(f"mod_wsgi.process_metrics: {mod_wsgi.process_metrics()}", file=output)
|
|
40
|
-
print(f"mod_wsgi.server_metrics: {mod_wsgi.server_metrics()}", file=output)
|
|
41
45
|
print(file=output)
|
|
42
46
|
|
|
43
|
-
metrics = mod_wsgi.server_metrics()
|
|
44
|
-
|
|
45
|
-
if metrics:
|
|
46
|
-
for process in metrics["processes"]:
|
|
47
|
-
for worker in process["workers"]:
|
|
48
|
-
print(worker["status"], file=output, end="")
|
|
49
|
-
print(file=output)
|
|
50
|
-
print(file=output)
|
|
51
|
-
|
|
52
47
|
print(f"apache.description: {apache.description}", file=output)
|
|
53
48
|
print(f"apache.build_date: {apache.build_date}", file=output)
|
|
54
49
|
print(f"apache.mpm_name: {apache.mpm_name}", file=output)
|
|
@@ -56,16 +51,18 @@ def application(environ, start_response):
|
|
|
56
51
|
print(f"apache.threads_per_process: {apache.threads_per_process}", file=output)
|
|
57
52
|
print(file=output)
|
|
58
53
|
|
|
59
|
-
print(f
|
|
54
|
+
print(f'PATH: {os.environ.get("PATH")}', file=output)
|
|
60
55
|
print(file=output)
|
|
61
56
|
|
|
62
|
-
print(f
|
|
63
|
-
print(f
|
|
57
|
+
print(f'LANG: {os.environ.get("LANG")}', file=output)
|
|
58
|
+
print(f'LC_ALL: {os.environ.get("LC_ALL")}', file=output)
|
|
64
59
|
print(f"sys.getdefaultencoding(): {sys.getdefaultencoding()}", file=output)
|
|
65
60
|
print(f"sys.getfilesystemencoding(): {sys.getfilesystemencoding()}", file=output)
|
|
66
61
|
print(f"locale.getlocale(): {locale.getlocale()}", file=output)
|
|
67
62
|
print(f"locale.getdefaultlocale(): {locale.getdefaultlocale()}", file=output)
|
|
68
|
-
print(
|
|
63
|
+
print(
|
|
64
|
+
f"locale.getpreferredencoding(): {locale.getpreferredencoding()}", file=output
|
|
65
|
+
)
|
|
69
66
|
print(file=output)
|
|
70
67
|
|
|
71
68
|
for key in sorted(environ):
|
|
@@ -317,6 +317,10 @@ WSGISlowRequests %(slow_requests)s
|
|
|
317
317
|
WSGISwitchInterval %(switch_interval)s
|
|
318
318
|
</IfDefine>
|
|
319
319
|
|
|
320
|
+
<IfDefine MOD_WSGI_FREE_THREADING>
|
|
321
|
+
WSGIFreeThreading On
|
|
322
|
+
</IfDefine>
|
|
323
|
+
|
|
320
324
|
<IfDefine MOD_WSGI_TELEMETRY_OPTIONS>
|
|
321
325
|
%(telemetry_options)s
|
|
322
326
|
</IfDefine>
|
|
@@ -812,6 +816,10 @@ APACHE_SETENV_CONFIG = """
|
|
|
812
816
|
SetEnv '%(name)s' '%(value)s'
|
|
813
817
|
"""
|
|
814
818
|
|
|
819
|
+
APACHE_PYTHON_WARNINGS_CONFIG = """
|
|
820
|
+
WSGIPythonWarnings '%(spec)s'
|
|
821
|
+
"""
|
|
822
|
+
|
|
815
823
|
APACHE_PASSENV_CONFIG = """
|
|
816
824
|
PassEnv '%(name)s'
|
|
817
825
|
"""
|
|
@@ -978,6 +986,11 @@ def generate_apache_config(options):
|
|
|
978
986
|
print(APACHE_SETENV_CONFIG % dict(name=name, value=value),
|
|
979
987
|
file=fp)
|
|
980
988
|
|
|
989
|
+
if options['python_warnings']:
|
|
990
|
+
for spec in options['python_warnings']:
|
|
991
|
+
print(APACHE_PYTHON_WARNINGS_CONFIG % dict(spec=spec),
|
|
992
|
+
file=fp)
|
|
993
|
+
|
|
981
994
|
if options['passenv_variables']:
|
|
982
995
|
for name in options['passenv_variables']:
|
|
983
996
|
print(APACHE_PASSENV_CONFIG % dict(name=name), file=fp)
|
|
@@ -540,10 +540,25 @@ add_option('all', '--telemetry-service', metavar='TARGET',
|
|
|
540
540
|
'ingester). Remote "udp:host:port" targets are not supported. '
|
|
541
541
|
'Off by default.')
|
|
542
542
|
|
|
543
|
+
add_option('unix', '--enable-telemetry', action='store_true', default=False,
|
|
544
|
+
help='Bundle a telemetry ingester and web UI alongside the WSGI '
|
|
545
|
+
'application. Generates a service-script daemon that runs the '
|
|
546
|
+
'mod_wsgi-telemetry ingester on a UNIX socket inside the server '
|
|
547
|
+
'root and emits WSGITelemetryService pointing at that socket so '
|
|
548
|
+
'the WSGI processes report to it. Requires the mod_wsgi-telemetry '
|
|
549
|
+
'package to be installed. Mutually exclusive with '
|
|
550
|
+
'--telemetry-service.')
|
|
551
|
+
|
|
552
|
+
add_option('unix', '--telemetry-ui-port', type='int', default=8888,
|
|
553
|
+
metavar='NUMBER', help='HTTP port the telemetry web UI binds to '
|
|
554
|
+
'on 127.0.0.1 when --enable-telemetry is set. Defaults to '
|
|
555
|
+
'%default. Two express instances on the same host need distinct '
|
|
556
|
+
'ports to avoid a bind collision.')
|
|
557
|
+
|
|
543
558
|
add_option('all', '--telemetry-interval', type='float', default=1.0,
|
|
544
559
|
metavar='SECONDS', help='Metrics reporter sampling interval '
|
|
545
|
-
'in seconds. Only applies when --telemetry-service
|
|
546
|
-
'Defaults to %default.')
|
|
560
|
+
'in seconds. Only applies when --telemetry-service or '
|
|
561
|
+
'--enable-telemetry is set. Defaults to %default.')
|
|
547
562
|
|
|
548
563
|
add_option('all', '--slow-requests', type='float', default=None,
|
|
549
564
|
metavar='SECONDS', help='Enable slow-request reporting and set '
|
|
@@ -558,6 +573,13 @@ add_option('all', '--switch-interval', type='float', default=None,
|
|
|
558
573
|
'switch-interval option on WSGIDaemonProcess. Defaults to '
|
|
559
574
|
'Python\'s built-in 0.005 (5 ms) when unset.')
|
|
560
575
|
|
|
576
|
+
add_option('all', '--free-threading', action='store_true', default=False,
|
|
577
|
+
help='Emit WSGIFreeThreading On in the generated configuration to '
|
|
578
|
+
'run the Python interpreter without the GIL. Requires a Python '
|
|
579
|
+
'build with free-threading support (PEP 703, --disable-gil); '
|
|
580
|
+
'mod_wsgi-express will exit with an error if the running Python '
|
|
581
|
+
'does not support it.')
|
|
582
|
+
|
|
561
583
|
add_option('all', '--telemetry-options', action='append', default=[],
|
|
562
584
|
metavar='ARGS', help='Apache-Options-style metrics-capture '
|
|
563
585
|
'toggle, passed verbatim to a WSGITelemetryOptions directive in '
|
|
@@ -729,6 +751,17 @@ add_option('all', '--python-eggs', metavar='DIRECTORY-PATH',
|
|
|
729
751
|
'unpacking of Python eggs. Defaults to a sub directory of '
|
|
730
752
|
'the server root directory.')
|
|
731
753
|
|
|
754
|
+
add_option('all', '--python-warnings', action='append', default=[],
|
|
755
|
+
metavar='SPEC', help='Add an entry to the Python warnings filter '
|
|
756
|
+
'using the standard -W syntax (action:message:category:module:lineno). '
|
|
757
|
+
'May be specified multiple times; each entry is emitted as a '
|
|
758
|
+
'separate WSGIPythonWarnings directive in the generated '
|
|
759
|
+
'configuration. The most common form is just an action: '
|
|
760
|
+
'default, error, always, module, once, or ignore. For example '
|
|
761
|
+
'--python-warnings error converts every warning into an '
|
|
762
|
+
'exception, which is useful for catching deprecation regressions '
|
|
763
|
+
'in CI.')
|
|
764
|
+
|
|
732
765
|
add_option('unix', '--shell-executable', default=SHELL,
|
|
733
766
|
metavar='FILE-PATH', help='Override the path to the shell '
|
|
734
767
|
'used in the \'apachectl\' script. The \'bash\' shell will '
|
|
@@ -166,6 +166,27 @@ def generate_wsgi_handler_script(options):
|
|
|
166
166
|
with open(path, 'w') as fp:
|
|
167
167
|
print(WSGI_DEFAULT_SCRIPT % options, file=fp)
|
|
168
168
|
|
|
169
|
+
TELEMETRY_SERVICE_SCRIPT = '''
|
|
170
|
+
# Auto-generated by mod_wsgi-express --enable-telemetry. Runs the
|
|
171
|
+
# mod_wsgi-telemetry ingester and web UI inside a WSGIDaemonProcess
|
|
172
|
+
# threads=0 service daemon. The WSGI processes report to the UNIX
|
|
173
|
+
# socket below via the WSGITelemetryService directive in httpd.conf.
|
|
174
|
+
|
|
175
|
+
from mod_wsgi.telemetry.server import main
|
|
176
|
+
|
|
177
|
+
main([
|
|
178
|
+
"--listen", "unix:%(telemetry_socket)s",
|
|
179
|
+
"--http-host", "127.0.0.1",
|
|
180
|
+
"--http-port", "%(telemetry_ui_port)s",
|
|
181
|
+
"--log-level", "INFO",
|
|
182
|
+
])
|
|
183
|
+
'''
|
|
184
|
+
|
|
185
|
+
def generate_telemetry_service_script(options):
|
|
186
|
+
path = os.path.join(options['server_root'], 'telemetry-service.py')
|
|
187
|
+
with open(path, 'w') as fp:
|
|
188
|
+
print(TELEMETRY_SERVICE_SCRIPT % options, file=fp)
|
|
189
|
+
|
|
169
190
|
WSGI_CONTROL_SCRIPT = """
|
|
170
191
|
#!%(shell_executable)s
|
|
171
192
|
|
|
@@ -5,12 +5,16 @@ import math
|
|
|
5
5
|
import os
|
|
6
6
|
import posixpath
|
|
7
7
|
import sys
|
|
8
|
+
import sysconfig
|
|
8
9
|
import tempfile
|
|
9
10
|
|
|
10
11
|
from . import apxs_config
|
|
11
12
|
from .platform import find_program, MOD_WSGI_SO, PYTHON_DYLIB
|
|
12
13
|
from .apache import generate_apache_config
|
|
13
|
-
from .scripts import
|
|
14
|
+
from .scripts import (
|
|
15
|
+
generate_wsgi_handler_script, generate_control_scripts,
|
|
16
|
+
generate_telemetry_service_script,
|
|
17
|
+
)
|
|
14
18
|
|
|
15
19
|
def _mpm_module_defines(modules_directory, preferred=None):
|
|
16
20
|
if os.name == 'nt':
|
|
@@ -367,6 +371,41 @@ def setup_server(command, args, options):
|
|
|
367
371
|
else:
|
|
368
372
|
options['server_metrics_flag'] = 'Off'
|
|
369
373
|
|
|
374
|
+
if options['enable_telemetry']:
|
|
375
|
+
if options['telemetry_service']:
|
|
376
|
+
raise ConfigurationError(
|
|
377
|
+
"--enable-telemetry and --telemetry-service are "
|
|
378
|
+
"mutually exclusive: --enable-telemetry generates an "
|
|
379
|
+
"ingester service and points the WSGI processes at "
|
|
380
|
+
"it, so an explicit telemetry target would conflict.")
|
|
381
|
+
|
|
382
|
+
try:
|
|
383
|
+
import mod_wsgi.telemetry.server # noqa: F401
|
|
384
|
+
except ImportError:
|
|
385
|
+
raise ConfigurationError(
|
|
386
|
+
"--enable-telemetry requires the mod_wsgi-telemetry "
|
|
387
|
+
"package: install it with "
|
|
388
|
+
"'pip install mod_wsgi-telemetry'.")
|
|
389
|
+
|
|
390
|
+
for name, _ in options['service_scripts'] or []:
|
|
391
|
+
if name == 'telemetry':
|
|
392
|
+
raise ConfigurationError(
|
|
393
|
+
"--enable-telemetry generates a service named "
|
|
394
|
+
"'telemetry'; rename the conflicting "
|
|
395
|
+
"--service-script entry or drop "
|
|
396
|
+
"--enable-telemetry.")
|
|
397
|
+
|
|
398
|
+
socket_path = posixpath.join(options['server_root'],
|
|
399
|
+
'telemetry.sock')
|
|
400
|
+
options['telemetry_socket'] = socket_path
|
|
401
|
+
options['telemetry_service'] = 'unix:%s' % socket_path
|
|
402
|
+
|
|
403
|
+
script_path = posixpath.join(options['server_root'],
|
|
404
|
+
'telemetry-service.py')
|
|
405
|
+
options['service_scripts'] = list(
|
|
406
|
+
options['service_scripts'] or [])
|
|
407
|
+
options['service_scripts'].append(('telemetry', script_path))
|
|
408
|
+
|
|
370
409
|
if options['telemetry_service']:
|
|
371
410
|
target = options['telemetry_service']
|
|
372
411
|
if not target.startswith('unix:'):
|
|
@@ -396,6 +435,13 @@ def setup_server(command, args, options):
|
|
|
396
435
|
options['switch_interval'] = ''
|
|
397
436
|
options['daemon_switch_interval_option'] = ''
|
|
398
437
|
|
|
438
|
+
if options['free_threading']:
|
|
439
|
+
if not sysconfig.get_config_var('Py_GIL_DISABLED'):
|
|
440
|
+
raise ConfigurationError(
|
|
441
|
+
"--free-threading requires a Python build with "
|
|
442
|
+
"free-threading support (PEP 703). Rebuild Python "
|
|
443
|
+
"with --disable-gil to use it.")
|
|
444
|
+
|
|
399
445
|
if options['handler_scripts']:
|
|
400
446
|
handler_scripts = []
|
|
401
447
|
for extension, script in options['handler_scripts']:
|
|
@@ -781,6 +827,8 @@ def setup_server(command, args, options):
|
|
|
781
827
|
options['httpd_arguments_list'].append('-DMOD_WSGI_SLOW_REQUESTS')
|
|
782
828
|
if options['switch_interval'] != '':
|
|
783
829
|
options['httpd_arguments_list'].append('-DMOD_WSGI_SWITCH_INTERVAL')
|
|
830
|
+
if options['free_threading']:
|
|
831
|
+
options['httpd_arguments_list'].append('-DMOD_WSGI_FREE_THREADING')
|
|
784
832
|
if options['telemetry_options']:
|
|
785
833
|
options['httpd_arguments_list'].append('-DMOD_WSGI_TELEMETRY_OPTIONS')
|
|
786
834
|
options['telemetry_options'] = '\n'.join(
|
|
@@ -856,6 +904,9 @@ def setup_server(command, args, options):
|
|
|
856
904
|
|
|
857
905
|
generate_wsgi_handler_script(options)
|
|
858
906
|
|
|
907
|
+
if options['enable_telemetry']:
|
|
908
|
+
generate_telemetry_service_script(options)
|
|
909
|
+
|
|
859
910
|
print('Server URL :', options['url'])
|
|
860
911
|
|
|
861
912
|
if options['https_url']:
|
|
@@ -886,6 +886,23 @@ const char *wsgi_set_accept_mutex(cmd_parms *cmd, void *mconfig,
|
|
|
886
886
|
static apr_file_t *wsgi_signal_pipe_in = NULL;
|
|
887
887
|
static apr_file_t *wsgi_signal_pipe_out = NULL;
|
|
888
888
|
|
|
889
|
+
/*
|
|
890
|
+
* Dedicated pipe carrying SIGHUP / SIGUSR2 bytes from the signal
|
|
891
|
+
* handler to the signal dispatcher thread. Independent of the
|
|
892
|
+
* shutdown pipe above so that long-running subscriber callbacks
|
|
893
|
+
* cannot delay the daemon main thread's detection of shutdown
|
|
894
|
+
* signals. Write end is set non-blocking after creation so the
|
|
895
|
+
* signal handler can never deadlock on a full pipe buffer; the
|
|
896
|
+
* trade-off is that signals arriving faster than subscribers
|
|
897
|
+
* consume them are silently dropped, which matches the
|
|
898
|
+
* not-refcounted semantics of Unix signals themselves.
|
|
899
|
+
*/
|
|
900
|
+
|
|
901
|
+
static apr_file_t *wsgi_signal_event_pipe_in = NULL;
|
|
902
|
+
static apr_file_t *wsgi_signal_event_pipe_out = NULL;
|
|
903
|
+
|
|
904
|
+
static apr_thread_t *wsgi_signal_dispatcher = NULL;
|
|
905
|
+
|
|
889
906
|
static void wsgi_signal_handler(int signum)
|
|
890
907
|
{
|
|
891
908
|
apr_size_t nbytes = 1;
|
|
@@ -909,6 +926,22 @@ static void wsgi_signal_handler(int signum)
|
|
|
909
926
|
apr_file_write(wsgi_signal_pipe_out, "C", &nbytes);
|
|
910
927
|
apr_file_flush(wsgi_signal_pipe_out);
|
|
911
928
|
}
|
|
929
|
+
else if (signum == SIGHUP)
|
|
930
|
+
{
|
|
931
|
+
if (wsgi_signal_event_pipe_out)
|
|
932
|
+
{
|
|
933
|
+
apr_file_write(wsgi_signal_event_pipe_out, "H", &nbytes);
|
|
934
|
+
apr_file_flush(wsgi_signal_event_pipe_out);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
else if (signum == SIGUSR2)
|
|
938
|
+
{
|
|
939
|
+
if (wsgi_signal_event_pipe_out)
|
|
940
|
+
{
|
|
941
|
+
apr_file_write(wsgi_signal_event_pipe_out, "U", &nbytes);
|
|
942
|
+
apr_file_flush(wsgi_signal_event_pipe_out);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
912
945
|
else
|
|
913
946
|
{
|
|
914
947
|
wsgi_daemon_shutdown++;
|
|
@@ -918,6 +951,69 @@ static void wsgi_signal_handler(int signum)
|
|
|
918
951
|
}
|
|
919
952
|
}
|
|
920
953
|
|
|
954
|
+
static void *wsgi_signal_dispatcher_thread(apr_thread_t *thd, void *data)
|
|
955
|
+
{
|
|
956
|
+
WSGIDaemonProcess *daemon = data;
|
|
957
|
+
|
|
958
|
+
apr_pollfd_t poll_fd;
|
|
959
|
+
apr_int32_t poll_count = 0;
|
|
960
|
+
apr_status_t rv;
|
|
961
|
+
|
|
962
|
+
poll_fd.desc_type = APR_POLL_FILE;
|
|
963
|
+
poll_fd.reqevents = APR_POLLIN;
|
|
964
|
+
poll_fd.desc.f = wsgi_signal_event_pipe_in;
|
|
965
|
+
|
|
966
|
+
while (1)
|
|
967
|
+
{
|
|
968
|
+
char buf[1];
|
|
969
|
+
apr_size_t nbytes = 1;
|
|
970
|
+
int signum;
|
|
971
|
+
const char *signame;
|
|
972
|
+
|
|
973
|
+
rv = apr_poll(&poll_fd, 1, &poll_count, -1);
|
|
974
|
+
if (APR_STATUS_IS_EINTR(rv))
|
|
975
|
+
continue;
|
|
976
|
+
if (rv != APR_SUCCESS)
|
|
977
|
+
break;
|
|
978
|
+
|
|
979
|
+
rv = apr_file_read(wsgi_signal_event_pipe_in, buf, &nbytes);
|
|
980
|
+
|
|
981
|
+
if (APR_STATUS_IS_EOF(rv))
|
|
982
|
+
break;
|
|
983
|
+
|
|
984
|
+
if (rv != APR_SUCCESS || nbytes != 1)
|
|
985
|
+
{
|
|
986
|
+
if (!wsgi_daemon_shutdown)
|
|
987
|
+
{
|
|
988
|
+
wsgi_log_error(APLOG_ALERT, 0, wsgi_server, WSGI_APLOGNO(0208) "Read failed on signal event pipe in "
|
|
989
|
+
"daemon process '%s'; signal dispatcher "
|
|
990
|
+
"exiting.",
|
|
991
|
+
daemon->group->name);
|
|
992
|
+
}
|
|
993
|
+
break;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
switch (buf[0])
|
|
997
|
+
{
|
|
998
|
+
case 'H':
|
|
999
|
+
signum = SIGHUP;
|
|
1000
|
+
signame = "SIGHUP";
|
|
1001
|
+
break;
|
|
1002
|
+
case 'U':
|
|
1003
|
+
signum = SIGUSR2;
|
|
1004
|
+
signame = "SIGUSR2";
|
|
1005
|
+
break;
|
|
1006
|
+
default:
|
|
1007
|
+
/* Unknown tag byte; defensive skip. */
|
|
1008
|
+
continue;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
wsgi_publish_process_signal(signum, signame);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
return NULL;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
921
1017
|
static void wsgi_exit_daemon_process(int status)
|
|
922
1018
|
{
|
|
923
1019
|
if (wsgi_server && wsgi_daemon_group)
|
|
@@ -2991,9 +3087,34 @@ static void wsgi_daemon_main(apr_pool_t *p, WSGIDaemonProcess *daemon)
|
|
|
2991
3087
|
}
|
|
2992
3088
|
}
|
|
2993
3089
|
|
|
2994
|
-
/*
|
|
3090
|
+
/*
|
|
3091
|
+
* Spawn the signal dispatcher thread that drains the signal
|
|
3092
|
+
* event pipe and publishes process_signal events to registered
|
|
3093
|
+
* Python subscribers. Created unconditionally since subscribers
|
|
3094
|
+
* can register at any point during the daemon's lifetime.
|
|
3095
|
+
*/
|
|
3096
|
+
|
|
3097
|
+
rv = apr_thread_create(&wsgi_signal_dispatcher, thread_attr,
|
|
3098
|
+
wsgi_signal_dispatcher_thread, daemon, p);
|
|
3099
|
+
|
|
3100
|
+
if (rv != APR_SUCCESS)
|
|
3101
|
+
{
|
|
3102
|
+
wsgi_log_error(APLOG_ERR, rv, wsgi_server, WSGI_APLOGNO(0207) "Unable to create signal dispatcher thread in "
|
|
3103
|
+
"daemon process '%s'; SIGHUP and SIGUSR2 signals "
|
|
3104
|
+
"will not be delivered to mod_wsgi.subscribe_signals "
|
|
3105
|
+
"subscribers.",
|
|
3106
|
+
daemon->group->name);
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
/*
|
|
3110
|
+
* Start telemetry reporter if configured. Skip for service-script
|
|
3111
|
+
* daemons (threads=0): they handle no requests, so there is
|
|
3112
|
+
* nothing to accumulate, and an ingester-hosting service script
|
|
3113
|
+
* would otherwise report telemetry datagrams to itself.
|
|
3114
|
+
*/
|
|
2995
3115
|
|
|
2996
|
-
|
|
3116
|
+
if (daemon->group->threads != 0)
|
|
3117
|
+
wsgi_telemetry_start_reporter(p);
|
|
2997
3118
|
|
|
2998
3119
|
/* Initialise worker stack. */
|
|
2999
3120
|
|
|
@@ -3248,6 +3369,40 @@ static void wsgi_daemon_main(apr_pool_t *p, WSGIDaemonProcess *daemon)
|
|
|
3248
3369
|
}
|
|
3249
3370
|
}
|
|
3250
3371
|
|
|
3372
|
+
/*
|
|
3373
|
+
* Stop the signal dispatcher before publishing process_stopping
|
|
3374
|
+
* so the dispatcher is not still inside a sub-interpreter GIL
|
|
3375
|
+
* acquire when the shutdown publish walks the same interpreter
|
|
3376
|
+
* table. Ignoring SIGHUP/SIGUSR2 first means no new bytes can
|
|
3377
|
+
* appear in the pipe; closing the write end then makes the
|
|
3378
|
+
* dispatcher's pending read return EOF and exit cleanly. The
|
|
3379
|
+
* join is best-effort: if a subscriber callback is wedged the
|
|
3380
|
+
* shutdown-timeout reaper still bounds the total wait via
|
|
3381
|
+
* exit().
|
|
3382
|
+
*/
|
|
3383
|
+
|
|
3384
|
+
apr_signal(SIGHUP, SIG_IGN);
|
|
3385
|
+
apr_signal(SIGUSR2, SIG_IGN);
|
|
3386
|
+
|
|
3387
|
+
if (wsgi_signal_event_pipe_out)
|
|
3388
|
+
{
|
|
3389
|
+
apr_file_close(wsgi_signal_event_pipe_out);
|
|
3390
|
+
wsgi_signal_event_pipe_out = NULL;
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
if (wsgi_signal_dispatcher)
|
|
3394
|
+
{
|
|
3395
|
+
rv = apr_thread_join(&thread_rv, wsgi_signal_dispatcher);
|
|
3396
|
+
if (rv != APR_SUCCESS)
|
|
3397
|
+
{
|
|
3398
|
+
wsgi_log_error(APLOG_WARNING, rv, wsgi_server,
|
|
3399
|
+
"Unable to join with signal dispatcher thread "
|
|
3400
|
+
"in daemon process '%s' during shutdown.",
|
|
3401
|
+
daemon->group->name);
|
|
3402
|
+
}
|
|
3403
|
+
wsgi_signal_dispatcher = NULL;
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3251
3406
|
/*
|
|
3252
3407
|
* If shutting down process due to reaching request time
|
|
3253
3408
|
* limit, then try and dump out stack traces of any threads
|
|
@@ -3540,6 +3695,31 @@ static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon)
|
|
|
3540
3695
|
wsgi_daemon_init_failure_exit();
|
|
3541
3696
|
}
|
|
3542
3697
|
|
|
3698
|
+
/*
|
|
3699
|
+
* Dedicated pipe for the application-facing signal events
|
|
3700
|
+
* (SIGHUP, SIGUSR2). Separate from the shutdown signal pipe
|
|
3701
|
+
* so that the daemon main thread reading shutdown bytes is
|
|
3702
|
+
* never delayed by subscriber callback work. Write end is
|
|
3703
|
+
* set non-blocking so the in-signal-handler write cannot
|
|
3704
|
+
* deadlock if subscribers are slow enough to fill the
|
|
3705
|
+
* kernel pipe buffer.
|
|
3706
|
+
*/
|
|
3707
|
+
|
|
3708
|
+
status = apr_file_pipe_create(&wsgi_signal_event_pipe_in,
|
|
3709
|
+
&wsgi_signal_event_pipe_out, p);
|
|
3710
|
+
|
|
3711
|
+
if (status != APR_SUCCESS)
|
|
3712
|
+
{
|
|
3713
|
+
wsgi_log_error(APLOG_ALERT, status, wsgi_server, WSGI_APLOGNO(0206) "Unable to initialise signal event pipe in "
|
|
3714
|
+
"daemon process '%s'. Daemon process will "
|
|
3715
|
+
"exit and be restarted after a delay.",
|
|
3716
|
+
daemon->group->name);
|
|
3717
|
+
|
|
3718
|
+
wsgi_daemon_init_failure_exit();
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3721
|
+
apr_file_pipe_timeout_set(wsgi_signal_event_pipe_out, 0);
|
|
3722
|
+
|
|
3543
3723
|
wsgi_daemon_shutdown = 0;
|
|
3544
3724
|
|
|
3545
3725
|
wsgi_daemon_pid = getpid();
|
|
@@ -3553,6 +3733,19 @@ static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon)
|
|
|
3553
3733
|
apr_signal(SIGXCPU, wsgi_signal_handler);
|
|
3554
3734
|
#endif
|
|
3555
3735
|
|
|
3736
|
+
/*
|
|
3737
|
+
* Application-facing signal handlers. SIGHUP and SIGUSR2
|
|
3738
|
+
* are not used by Apache for child process management
|
|
3739
|
+
* (Apache uses SIGHUP at the parent only, and translates
|
|
3740
|
+
* parent shutdown to SIGTERM/SIGUSR1 for the daemon); they
|
|
3741
|
+
* are routed through the signal event pipe to the
|
|
3742
|
+
* dispatcher thread which calls registered Python
|
|
3743
|
+
* subscribers via mod_wsgi.subscribe_signals.
|
|
3744
|
+
*/
|
|
3745
|
+
|
|
3746
|
+
apr_signal(SIGHUP, wsgi_signal_handler);
|
|
3747
|
+
apr_signal(SIGUSR2, wsgi_signal_handler);
|
|
3748
|
+
|
|
3556
3749
|
/* Set limits on amount of CPU time that can be used. */
|
|
3557
3750
|
|
|
3558
3751
|
if (daemon->group->cpu_time_limit > 0)
|