Refactor documentation for reverse proxy and routing guides
Deploy to Production / deploy (push) Successful in 5s

This commit is contained in:
Илья Глазунов
2025-12-08 01:05:52 +03:00
parent 58660ec8d4
commit 00119ce463
12 changed files with 521 additions and 517 deletions
+63 -63
View File
@@ -31,18 +31,18 @@
<h4>ASGIAppLoader</h4>
<p>Loads and manages ASGI/WSGI applications from Python import paths.</p>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> ASGIAppLoader
<pre><code class="language-python">from pyserve import ASGIAppLoader
loader = ASGIAppLoader()
<span class="comment"># Load an ASGI app</span>
# Load an ASGI app
app = loader.load_app(
app_path=<span class="value">"mymodule:app"</span>,
app_type=<span class="value">"asgi"</span>,
module_path=<span class="value">"/path/to/project"</span>,
factory=<span class="value">False</span>,
factory_args=<span class="value">None</span>
)</pre>
app_path="mymodule:app",
app_type="asgi",
module_path="/path/to/project",
factory=False,
factory_args=None
)</code></pre>
<h5>Methods</h5>
<dl>
@@ -69,14 +69,14 @@ app = loader.load_app(
<h4>MountedApp</h4>
<p>Represents an application mounted at a specific path.</p>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> MountedApp
<pre><code class="language-python">from pyserve import MountedApp
mount = MountedApp(
path=<span class="value">"/api"</span>,
path="/api",
app=my_asgi_app,
name=<span class="value">"my-api"</span>,
strip_path=<span class="value">True</span>
)</pre>
name="my-api",
strip_path=True
)</code></pre>
<h5>Attributes</h5>
<dl>
@@ -105,19 +105,19 @@ mount = MountedApp(
<h4>ASGIMountManager</h4>
<p>Manages multiple mounted applications and routes requests.</p>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> ASGIMountManager
<pre><code class="language-python">from pyserve import ASGIMountManager
manager = ASGIMountManager()
<span class="comment"># Mount using app instance</span>
manager.mount(path=<span class="value">"/api"</span>, app=my_app)
# Mount using app instance
manager.mount(path="/api", app=my_app)
<span class="comment"># Mount using import path</span>
# Mount using import path
manager.mount(
path=<span class="value">"/flask"</span>,
app_path=<span class="value">"myapp:flask_app"</span>,
app_type=<span class="value">"wsgi"</span>
)</pre>
path="/flask",
app_path="myapp:flask_app",
app_type="wsgi"
)</code></pre>
<h5>Methods</h5>
<dl>
@@ -150,80 +150,80 @@ manager.mount(
<p>Convenience functions for loading specific framework applications:</p>
<h4>create_fastapi_app()</h4>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> create_fastapi_app
<pre><code class="language-python">from pyserve import create_fastapi_app
app = create_fastapi_app(
app_path=<span class="value">"myapp.api:app"</span>,
module_path=<span class="value">None</span>,
factory=<span class="value">False</span>,
factory_args=<span class="value">None</span>
)</pre>
app_path="myapp.api:app",
module_path=None,
factory=False,
factory_args=None
)</code></pre>
<h4>create_flask_app()</h4>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> create_flask_app
<pre><code class="language-python">from pyserve import create_flask_app
app = create_flask_app(
app_path=<span class="value">"myapp.web:app"</span>,
module_path=<span class="value">None</span>,
factory=<span class="value">False</span>,
factory_args=<span class="value">None</span>
)</pre>
app_path="myapp.web:app",
module_path=None,
factory=False,
factory_args=None
)</code></pre>
<p>Automatically wraps the WSGI app for ASGI compatibility.</p>
<h4>create_django_app()</h4>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> create_django_app
<pre><code class="language-python">from pyserve import create_django_app
app = create_django_app(
settings_module=<span class="value">"myproject.settings"</span>,
module_path=<span class="value">"/path/to/project"</span>
)</pre>
settings_module="myproject.settings",
module_path="/path/to/project"
)</code></pre>
<p>Sets <code>DJANGO_SETTINGS_MODULE</code> and returns Django's ASGI application.</p>
<h4>create_starlette_app()</h4>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> create_starlette_app
<pre><code class="language-python">from pyserve import create_starlette_app
app = create_starlette_app(
app_path=<span class="value">"myapp:starlette_app"</span>,
module_path=<span class="value">None</span>,
factory=<span class="value">False</span>,
factory_args=<span class="value">None</span>
)</pre>
app_path="myapp:starlette_app",
module_path=None,
factory=False,
factory_args=None
)</code></pre>
<h3>Usage Example</h3>
<p>Complete example mounting multiple applications:</p>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> (
<pre><code class="language-python">from pyserve import (
PyServeServer,
ASGIMountManager,
create_fastapi_app,
create_flask_app
)
<span class="comment"># Create mount manager</span>
# Create mount manager
mounts = ASGIMountManager()
<span class="comment"># Mount FastAPI</span>
api_app = create_fastapi_app(<span class="value">"myapp.api:app"</span>)
<span class="keyword">if</span> api_app:
mounts.mount(<span class="value">"/api"</span>, app=api_app, name=<span class="value">"api"</span>)
# Mount FastAPI
api_app = create_fastapi_app("myapp.api:app")
if api_app:
mounts.mount("/api", app=api_app, name="api")
<span class="comment"># Mount Flask</span>
admin_app = create_flask_app(<span class="value">"myapp.admin:app"</span>)
<span class="keyword">if</span> admin_app:
mounts.mount(<span class="value">"/admin"</span>, app=admin_app, name=<span class="value">"admin"</span>)
# Mount Flask
admin_app = create_flask_app("myapp.admin:app")
if admin_app:
mounts.mount("/admin", app=admin_app, name="admin")
<span class="comment"># List mounts</span>
<span class="keyword">for</span> mount <span class="keyword">in</span> mounts.list_mounts():
print(f<span class="value">"Mounted {mount['name']} at {mount['path']}"</span>)</pre>
# List mounts
for mount in mounts.list_mounts():
print(f"Mounted {mount['name']} at {mount['path']}")</code></pre>
<h3>Error Handling</h3>
<p>All loader functions return <code>None</code> on failure and log errors.
Check the return value before using:</p>
<pre>app = create_fastapi_app(<span class="value">"nonexistent:app"</span>)
<span class="keyword">if</span> app <span class="keyword">is None</span>:
<span class="comment"># Handle error - check logs for details</span>
print(<span class="value">"Failed to load application"</span>)</pre>
<pre><code class="language-bash">app = create_fastapi_app("nonexistent:app")
if app is None:
# Handle error - check logs for details
print("Failed to load application")</code></pre>
<h3>WSGI Compatibility</h3>
<p>For WSGI applications, pyserve uses adapters in this priority:</p>
@@ -233,9 +233,9 @@ admin_app = create_flask_app(<span class="value">"myapp.admin:app"</span>)
</ol>
<p>Install an adapter:</p>
<pre>pip install a2wsgi <span class="comment"># recommended</span>
<span class="comment"># or</span>
pip install asgiref</pre>
<pre><code class="language-bash">pip install a2wsgi # recommended
# or
pip install asgiref</code></pre>
<div class="note">
<strong>See Also:</strong>
+16 -16
View File
@@ -26,7 +26,7 @@
<p>pyserve provides a command-line interface for server management.</p>
<h3>Synopsis</h3>
<pre>pyserve [OPTIONS]</pre>
<pre><code class="language-bash">pyserve [OPTIONS]</code></pre>
<h3>Options</h3>
@@ -66,20 +66,20 @@
<h3>Examples</h3>
<p><strong>Start with default configuration:</strong></p>
<pre>pyserve</pre>
<pre><code class="language-bash">pyserve</code></pre>
<p><strong>Start with custom config file:</strong></p>
<pre>pyserve -c /path/to/config.yaml</pre>
<pre><code class="language-bash">pyserve -c /path/to/config.yaml</code></pre>
<p><strong>Override host and port:</strong></p>
<pre>pyserve --host 127.0.0.1 --port 9000</pre>
<pre><code class="language-bash">pyserve --host 127.0.0.1 --port 9000</code></pre>
<p><strong>Enable debug mode:</strong></p>
<pre>pyserve --debug</pre>
<pre><code class="language-bash">pyserve --debug</code></pre>
<p><strong>Show version:</strong></p>
<pre>pyserve --version
<span class="comment"># Output: pyserve 0.7.0</span></pre>
<pre><code class="language-bash">pyserve --version
# Output: pyserve 0.7.0</code></pre>
<h3>Configuration Priority</h3>
<p>Settings are applied in the following order (later overrides earlier):</p>
@@ -125,15 +125,15 @@
<h3>Development Commands (Makefile)</h3>
<p>When working with the source repository, use make commands:</p>
<pre>make run <span class="comment"># Start in development mode</span>
make run-prod <span class="comment"># Start in production mode</span>
make test <span class="comment"># Run tests</span>
make test-cov <span class="comment"># Tests with coverage</span>
make lint <span class="comment"># Check code with linters</span>
make format <span class="comment"># Format code</span>
make build <span class="comment"># Build wheel package</span>
make clean <span class="comment"># Clean temporary files</span>
make help <span class="comment"># Show all commands</span></pre>
<pre><code class="language-bash">make run # Start in development mode
make run-prod # Start in production mode
make test # Run tests
make test-cov # Tests with coverage
make lint # Check code with linters
make format # Format code
make build # Build wheel package
make clean # Clean temporary files
make help # Show all commands</code></pre>
</div>
<div id="footer">
+74 -74
View File
@@ -58,27 +58,27 @@
<h3>Extension Configuration</h3>
<p>Extensions are configured in the <code>extensions</code> section:</p>
<pre><span class="directive">extensions:</span>
- <span class="directive">type:</span> <span class="value">routing</span>
<span class="directive">config:</span>
<span class="comment"># extension-specific configuration</span>
<pre><code class="language-yaml">extensions:
- type: routing
config:
# extension-specific configuration
- <span class="directive">type:</span> <span class="value">security</span>
<span class="directive">config:</span>
<span class="comment"># ...</span></pre>
- type: security
config:
# ...</code></pre>
<h3>Routing Extension</h3>
<p>The primary extension for URL routing. See <a href="../guides/routing.html">Routing Guide</a> for full documentation.</p>
<pre><span class="directive">- type:</span> <span class="value">routing</span>
<span class="directive">config:</span>
<span class="directive">regex_locations:</span>
<span class="value">"=/health"</span>:
<span class="directive">return:</span> <span class="value">"200 OK"</span>
<span class="value">"~^/api/"</span>:
<span class="directive">proxy_pass:</span> <span class="value">"http://backend:9001"</span>
<span class="value">"__default__"</span>:
<span class="directive">root:</span> <span class="value">"./static"</span></pre>
<pre><code class="language-python">- type: routing
config:
regex_locations:
"=/health":
return: "200 OK"
"~^/api/":
proxy_pass: "http://backend:9001"
"__default__":
root: "./static"</code></pre>
<h3>Security Extension</h3>
<p>Adds security headers and IP-based access control.</p>
@@ -95,16 +95,16 @@
<dd>List of blocked IP addresses (blacklist mode)</dd>
</dl>
<pre><span class="directive">- type:</span> <span class="value">security</span>
<span class="directive">config:</span>
<span class="directive">security_headers:</span>
<span class="directive">X-Frame-Options:</span> <span class="value">DENY</span>
<span class="directive">X-Content-Type-Options:</span> <span class="value">nosniff</span>
<span class="directive">X-XSS-Protection:</span> <span class="value">"1; mode=block"</span>
<span class="directive">Strict-Transport-Security:</span> <span class="value">"max-age=31536000"</span>
<span class="directive">blocked_ips:</span>
- <span class="value">"192.168.1.100"</span>
- <span class="value">"10.0.0.50"</span></pre>
<pre><code class="language-bash">- type: security
config:
security_headers:
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: "1; mode=block"
Strict-Transport-Security: "max-age=31536000"
blocked_ips:
- "192.168.1.100"
- "10.0.0.50"</code></pre>
<p>Default security headers if not specified:</p>
<ul class="indent">
@@ -125,11 +125,11 @@
<dd>Default cache TTL in seconds. Default: <code>3600</code></dd>
</dl>
<pre><span class="directive">- type:</span> <span class="value">caching</span>
<span class="directive">config:</span>
<span class="directive">cache_ttl:</span> <span class="value">3600</span>
<span class="directive">cache_patterns:</span>
- <span class="value">"/api/public/*"</span></pre>
<pre><code class="language-bash">- type: caching
config:
cache_ttl: 3600
cache_patterns:
- "/api/public/*"</code></pre>
<h3>Monitoring Extension</h3>
<p>Collects request metrics and provides statistics.</p>
@@ -140,9 +140,9 @@
<dd>Enable metrics collection. Default: <code>true</code></dd>
</dl>
<pre><span class="directive">- type:</span> <span class="value">monitoring</span>
<span class="directive">config:</span>
<span class="directive">enable_metrics:</span> <span class="value">true</span></pre>
<pre><code class="language-bash">- type: monitoring
config:
enable_metrics: true</code></pre>
<p>Collected metrics (available at <code>/metrics</code>):</p>
<ul class="indent">
@@ -220,28 +220,28 @@
<dd>Django settings module (for Django apps only)</dd>
</dl>
<pre><span class="directive">- type:</span> <span class="value">asgi</span>
<span class="directive">config:</span>
<span class="directive">mounts:</span>
<span class="comment"># FastAPI application</span>
- <span class="directive">path:</span> <span class="value">"/api"</span>
<span class="directive">app_path:</span> <span class="value">"myapp.api:app"</span>
<span class="directive">app_type:</span> <span class="value">asgi</span>
<span class="directive">name:</span> <span class="value">"api"</span>
<pre><code class="language-bash">- type: asgi
config:
mounts:
# FastAPI application
- path: "/api"
app_path: "myapp.api:app"
app_type: asgi
name: "api"
<span class="comment"># Flask application (WSGI)</span>
- <span class="directive">path:</span> <span class="value">"/admin"</span>
<span class="directive">app_path:</span> <span class="value">"myapp.admin:app"</span>
<span class="directive">app_type:</span> <span class="value">wsgi</span>
<span class="directive">name:</span> <span class="value">"admin"</span>
# Flask application (WSGI)
- path: "/admin"
app_path: "myapp.admin:app"
app_type: wsgi
name: "admin"
<span class="comment"># Factory pattern with arguments</span>
- <span class="directive">path:</span> <span class="value">"/api/v2"</span>
<span class="directive">app_path:</span> <span class="value">"myapp.api:create_app"</span>
<span class="directive">factory:</span> <span class="value">true</span>
<span class="directive">factory_args:</span>
<span class="directive">debug:</span> <span class="value">true</span>
<span class="directive">version:</span> <span class="value">"2.0"</span></pre>
# Factory pattern with arguments
- path: "/api/v2"
app_path: "myapp.api:create_app"
factory: true
factory_args:
debug: true
version: "2.0"</code></pre>
<p>Supported frameworks:</p>
<ul class="indent">
@@ -271,27 +271,27 @@
<li>Request tracing with X-Request-ID</li>
</ul>
<pre><span class="directive">- type:</span> <span class="value">process_orchestration</span>
<span class="directive">config:</span>
<span class="directive">port_range:</span> <span class="value">[9000, 9999]</span>
<span class="directive">health_check_enabled:</span> <span class="value">true</span>
<span class="directive">proxy_timeout:</span> <span class="value">60.0</span>
<span class="directive">logging:</span>
<span class="directive">httpx_level:</span> <span class="value">warning</span>
<span class="directive">proxy_logs:</span> <span class="value">true</span>
<span class="directive">health_check_logs:</span> <span class="value">false</span>
<span class="directive">apps:</span>
- <span class="directive">name:</span> <span class="value">api</span>
<span class="directive">path:</span> <span class="value">/api</span>
<span class="directive">app_path:</span> <span class="value">myapp.api:app</span>
<span class="directive">workers:</span> <span class="value">4</span>
<span class="directive">health_check_path:</span> <span class="value">/health</span>
<pre><code class="language-yaml">- type: process_orchestration
config:
port_range: [9000, 9999]
health_check_enabled: true
proxy_timeout: 60.0
logging:
httpx_level: warning
proxy_logs: true
health_check_logs: false
apps:
- name: api
path: /api
app_path: myapp.api:app
workers: 4
health_check_path: /health
- <span class="directive">name:</span> <span class="value">admin</span>
<span class="directive">path:</span> <span class="value">/admin</span>
<span class="directive">app_path:</span> <span class="value">myapp.admin:app</span>
<span class="directive">app_type:</span> <span class="value">wsgi</span>
<span class="directive">workers:</span> <span class="value">2</span></pre>
- name: admin
path: /admin
app_path: myapp.admin:app
app_type: wsgi
workers: 2</code></pre>
<h4>App Configuration</h4>
<dl>