codekingpro/portable-devtools
114k
1"""A small application that can be used to test a WSGI server and check2it for WSGI compliance.3"""4 5from __future__ import annotations6 7import importlib.metadata8import os9import sys10import typing as t11from textwrap import wrap12 13from markupsafe import escape14 15from .wrappers.request import Request16from .wrappers.response import Response17 18TEMPLATE = """\19<!doctype html>20<html lang=en>21<title>WSGI Information</title>22<style type="text/css">23 @import url(https://fonts.googleapis.com/css?family=Ubuntu);24 25 body { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',26 'Verdana', sans-serif; background-color: white; color: #000;27 font-size: 15px; text-align: center; }28 div.box { text-align: left; width: 45em; margin: auto; padding: 50px 0;29 background-color: white; }30 h1, h2 { font-family: 'Ubuntu', 'Lucida Grande', 'Lucida Sans Unicode',31 'Geneva', 'Verdana', sans-serif; font-weight: normal; }32 h1 { margin: 0 0 30px 0; }33 h2 { font-size: 1.4em; margin: 1em 0 0.5em 0; }34 table { width: 100%%; border-collapse: collapse; border: 1px solid #AFC5C9 }35 table th { background-color: #AFC1C4; color: white; font-size: 0.72em;36 font-weight: normal; width: 18em; vertical-align: top;37 padding: 0.5em 0 0.1em 0.5em; }38 table td { border: 1px solid #AFC5C9; padding: 0.1em 0 0.1em 0.5em; }39 code { font-family: 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono',40 monospace; font-size: 0.7em; }41 ul li { line-height: 1.5em; }42 ul.path { font-size: 0.7em; margin: 0 -30px; padding: 8px 30px;43 list-style: none; background: #E8EFF0; }44 ul.path li { line-height: 1.6em; }45 li.virtual { color: #999; text-decoration: underline; }46 li.exp { background: white; }47</style>48<div class="box">49 <h1>WSGI Information</h1>50 <p>51 This page displays all available information about the WSGI server and52 the underlying Python interpreter.53 <h2 id="python-interpreter">Python Interpreter</h2>54 <table>55 <tr>56 <th>Python Version57 <td>%(python_version)s58 <tr>59 <th>Platform60 <td>%(platform)s [%(os)s]61 <tr>62 <th>API Version63 <td>%(api_version)s64 <tr>65 <th>Byteorder66 <td>%(byteorder)s67 <tr>68 <th>Werkzeug Version69 <td>%(werkzeug_version)s70 </table>71 <h2 id="wsgi-environment">WSGI Environment</h2>72 <table>%(wsgi_env)s</table>73 <h2 id="installed-eggs">Installed Eggs</h2>74 <p>75 The following python packages were installed on the system as76 Python eggs:77 <ul>%(python_eggs)s</ul>78 <h2 id="sys-path">System Path</h2>79 <p>80 The following paths are the current contents of the load path. The81 following entries are looked up for Python packages. Note that not82 all items in this path are folders. Gray and underlined items are83 entries pointing to invalid resources or used by custom import hooks84 such as the zip importer.85 <p>86 Items with a bright background were expanded for display from a relative87 path. If you encounter such paths in the output you might want to check88 your setup as relative paths are usually problematic in multithreaded89 environments.90 <ul class="path">%(sys_path)s</ul>91</div>92"""93 94 95def iter_sys_path() -> t.Iterator[tuple[str, bool, bool]]:96 if os.name == "posix":97 98 def strip(x: str) -> str:99 prefix = os.path.expanduser("~")100 if x.startswith(prefix):101 x = f"~{x[len(prefix) :]}"102 return x103 104 else:105 106 def strip(x: str) -> str:107 return x108 109 cwd = os.path.abspath(os.getcwd())110 for item in sys.path:111 path = os.path.join(cwd, item or os.path.curdir)112 yield strip(os.path.normpath(path)), not os.path.isdir(path), path != item113 114 115@Request.application116def test_app(req: Request) -> Response:117 """Simple test application that dumps the environment. You can use118 it to check if Werkzeug is working properly:119 120 .. sourcecode:: pycon121 122 >>> from werkzeug.serving import run_simple123 >>> from werkzeug.testapp import test_app124 >>> run_simple('localhost', 3000, test_app)125 * Running on http://localhost:3000/126 127 The application displays important information from the WSGI environment,128 the Python interpreter and the installed libraries.129 """130 try:131 import pkg_resources132 except ImportError:133 eggs: t.Iterable[t.Any] = ()134 else:135 eggs = sorted(136 pkg_resources.working_set,137 key=lambda x: x.project_name.lower(),138 )139 python_eggs = []140 for egg in eggs:141 try:142 version = egg.version143 except (ValueError, AttributeError):144 version = "unknown"145 python_eggs.append(146 f"<li>{escape(egg.project_name)} <small>[{escape(version)}]</small>"147 )148 149 wsgi_env = []150 sorted_environ = sorted(req.environ.items(), key=lambda x: repr(x[0]).lower())151 for key, value in sorted_environ:152 value = "".join(wrap(str(escape(repr(value)))))153 wsgi_env.append(f"<tr><th>{escape(key)}<td><code>{value}</code>")154 155 sys_path = []156 for item, virtual, expanded in iter_sys_path():157 css = []158 if virtual:159 css.append("virtual")160 if expanded:161 css.append("exp")162 class_str = f' class="{" ".join(css)}"' if css else ""163 sys_path.append(f"<li{class_str}>{escape(item)}")164 165 context = {166 "python_version": "<br>".join(escape(sys.version).splitlines()),167 "platform": escape(sys.platform),168 "os": escape(os.name),169 "api_version": sys.api_version,170 "byteorder": sys.byteorder,171 "werkzeug_version": _get_werkzeug_version(),172 "python_eggs": "\n".join(python_eggs),173 "wsgi_env": "\n".join(wsgi_env),174 "sys_path": "\n".join(sys_path),175 }176 return Response(TEMPLATE % context, mimetype="text/html")177 178 179_werkzeug_version = ""180 181 182def _get_werkzeug_version() -> str:183 global _werkzeug_version184 185 if not _werkzeug_version:186 _werkzeug_version = importlib.metadata.version("werkzeug")187 188 return _werkzeug_version189 190 191if __name__ == "__main__":192 from .serving import run_simple193 194 run_simple("localhost", 5000, test_app, use_reloader=True)195 