initial commit

This commit is contained in:
Илья Глазунов
2025-12-05 12:57:41 +03:00
commit 1f25033d2d
16 changed files with 2554 additions and 0 deletions
+251
View File
@@ -0,0 +1,251 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ASGI Mount API - pyserve</title>
<link rel="stylesheet" href="../style.css">
</head>
<body>
<div id="container">
<div id="header">
<h1>pyserve</h1>
<div class="tagline">python application orchestrator</div>
</div>
<div class="breadcrumb">
<a href="../index.html">pyserve</a> » <a href="/reference/">Reference</a> » ASGI Mount API
</div>
<div id="content">
<h2>ASGI Mount API Reference</h2>
<p>The <code>pyserve.asgi_mount</code> module provides a Python API for mounting
ASGI and WSGI applications programmatically.</p>
<h3>Classes</h3>
<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
loader = ASGIAppLoader()
<span class="comment"># Load an ASGI app</span>
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>
<h5>Methods</h5>
<dl>
<dt>load_app(app_path, app_type="asgi", module_path=None, factory=False, factory_args=None)</dt>
<dd>
Load an application from an import path.
<ul class="indent">
<li><code>app_path</code>: Import path in format <code>module:attribute</code></li>
<li><code>app_type</code>: <code>"asgi"</code> or <code>"wsgi"</code></li>
<li><code>module_path</code>: Optional path to add to <code>sys.path</code></li>
<li><code>factory</code>: If True, call the attribute as a factory function</li>
<li><code>factory_args</code>: Dict of arguments for factory function</li>
</ul>
Returns the loaded ASGI application or <code>None</code> on error.
</dd>
<dt>get_app(app_path)</dt>
<dd>Get a previously loaded application by its path.</dd>
<dt>reload_app(app_path, **kwargs)</dt>
<dd>Reload an application, useful for development hot-reloading.</dd>
</dl>
<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
mount = MountedApp(
path=<span class="value">"/api"</span>,
app=my_asgi_app,
name=<span class="value">"my-api"</span>,
strip_path=<span class="value">True</span>
)</pre>
<h5>Attributes</h5>
<dl>
<dt>path: str</dt>
<dd>The mount path (without trailing slash).</dd>
<dt>app: ASGIApp</dt>
<dd>The ASGI application.</dd>
<dt>name: str</dt>
<dd>Friendly name for logging.</dd>
<dt>strip_path: bool</dt>
<dd>Whether to strip the mount path from requests.</dd>
</dl>
<h5>Methods</h5>
<dl>
<dt>matches(request_path) → bool</dt>
<dd>Check if a request path matches this mount.</dd>
<dt>get_modified_path(original_path) → str</dt>
<dd>Get the modified path after stripping mount prefix.</dd>
</dl>
<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
manager = ASGIMountManager()
<span class="comment"># Mount using app instance</span>
manager.mount(path=<span class="value">"/api"</span>, app=my_app)
<span class="comment"># Mount using import path</span>
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>
<h5>Methods</h5>
<dl>
<dt>mount(path, app=None, app_path=None, app_type="asgi", module_path=None, factory=False, factory_args=None, name="", strip_path=True) → bool</dt>
<dd>
Mount an application at a path. Either <code>app</code> or <code>app_path</code> must be provided.
Returns <code>True</code> on success.
</dd>
<dt>unmount(path) → bool</dt>
<dd>Remove a mounted application. Returns <code>True</code> if found and removed.</dd>
<dt>get_mount(request_path) → Optional[MountedApp]</dt>
<dd>Get the mount that matches a request path.</dd>
<dt>handle_request(scope, receive, send) → bool</dt>
<dd>Handle an ASGI request. Returns <code>True</code> if handled by a mounted app.</dd>
<dt>list_mounts() → List[Dict]</dt>
<dd>Get a list of all mounts with their configuration.</dd>
</dl>
<h5>Properties</h5>
<dl>
<dt>mounts: List[MountedApp]</dt>
<dd>Copy of the current mounts list (sorted by path length, longest first).</dd>
</dl>
<h3>Helper Functions</h3>
<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
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>
<h4>create_flask_app()</h4>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> 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>
<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
app = create_django_app(
settings_module=<span class="value">"myproject.settings"</span>,
module_path=<span class="value">"/path/to/project"</span>
)</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
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>
<h3>Usage Example</h3>
<p>Complete example mounting multiple applications:</p>
<pre><span class="keyword">from</span> pyserve <span class="keyword">import</span> (
PyServeServer,
ASGIMountManager,
create_fastapi_app,
create_flask_app
)
<span class="comment"># Create mount manager</span>
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>)
<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>)
<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>
<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>
<h3>WSGI Compatibility</h3>
<p>For WSGI applications, pyserve uses adapters in this priority:</p>
<ol class="indent">
<li><code>a2wsgi.WSGIMiddleware</code> (recommended)</li>
<li><code>asgiref.wsgi.WsgiToAsgi</code> (fallback)</li>
</ol>
<p>Install an adapter:</p>
<pre>pip install a2wsgi <span class="comment"># recommended</span>
<span class="comment"># or</span>
pip install asgiref</pre>
<div class="note">
<strong>See Also:</strong>
<ul class="indent">
<li><a href="../guides/asgi-mount.html">ASGI Mounting Guide</a> — Configuration-based mounting</li>
<li><a href="extensions.html">Extensions</a> — ASGI extension configuration</li>
</ul>
</div>
</div>
<div id="footer">
<p>pyserve &copy; 2024-2025 | MIT License</p>
</div>
</div>
</body>
</html>
+141
View File
@@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CLI Reference - pyserve</title>
<link rel="stylesheet" href="../style.css">
</head>
<body>
<div id="container">
<div id="header">
<h1>pyserve</h1>
<div class="tagline">python application orchestrator</div>
</div>
<div class="breadcrumb">
<a href="../index.html">pyserve</a> » <a href="/reference/">Reference</a> » CLI
</div>
<div id="content">
<h2>CLI Reference</h2>
<p>pyserve provides a command-line interface for server management.</p>
<h3>Synopsis</h3>
<pre>pyserve [OPTIONS]</pre>
<h3>Options</h3>
<table class="dirindex">
<tr>
<td><code>-c, --config FILE</code></td>
<td>Path to configuration file</td>
<td class="desc">Default: <code>config.yaml</code></td>
</tr>
<tr>
<td><code>--host HOST</code></td>
<td>Bind address</td>
<td class="desc">Overrides config value</td>
</tr>
<tr>
<td><code>--port PORT</code></td>
<td>Listen port</td>
<td class="desc">Overrides config value</td>
</tr>
<tr>
<td><code>--debug</code></td>
<td>Enable debug mode</td>
<td class="desc">Sets log level to DEBUG</td>
</tr>
<tr>
<td><code>--version</code></td>
<td>Show version and exit</td>
<td class="desc"></td>
</tr>
<tr>
<td><code>--help</code></td>
<td>Show help message and exit</td>
<td class="desc"></td>
</tr>
</table>
<h3>Examples</h3>
<p><strong>Start with default configuration:</strong></p>
<pre>pyserve</pre>
<p><strong>Start with custom config file:</strong></p>
<pre>pyserve -c /path/to/config.yaml</pre>
<p><strong>Override host and port:</strong></p>
<pre>pyserve --host 127.0.0.1 --port 9000</pre>
<p><strong>Enable debug mode:</strong></p>
<pre>pyserve --debug</pre>
<p><strong>Show version:</strong></p>
<pre>pyserve --version
<span class="comment"># Output: pyserve 0.7.0</span></pre>
<h3>Configuration Priority</h3>
<p>Settings are applied in the following order (later overrides earlier):</p>
<ol class="indent">
<li>Default values</li>
<li>Configuration file (<code>config.yaml</code>)</li>
<li>Command-line options</li>
</ol>
<h3>Default Configuration</h3>
<p>If no configuration file is found, pyserve uses default settings:</p>
<ul class="indent">
<li>Host: <code>0.0.0.0</code></li>
<li>Port: <code>8080</code></li>
<li>Log level: <code>INFO</code></li>
</ul>
<h3>Exit Codes</h3>
<table class="dirindex">
<tr>
<td><code>0</code></td>
<td>Success / Clean shutdown</td>
</tr>
<tr>
<td><code>1</code></td>
<td>Configuration error or startup failure</td>
</tr>
</table>
<h3>Signals</h3>
<p>pyserve handles the following signals:</p>
<table class="dirindex">
<tr>
<td><code>SIGINT</code> (Ctrl+C)</td>
<td>Graceful shutdown</td>
</tr>
<tr>
<td><code>SIGTERM</code></td>
<td>Graceful shutdown</td>
</tr>
</table>
<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>
</div>
<div id="footer">
<p>pyserve &copy; 2024-2025 | MIT License</p>
</div>
</div>
</body>
</html>
+325
View File
@@ -0,0 +1,325 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Extensions - pyserve</title>
<link rel="stylesheet" href="../style.css">
</head>
<body>
<div id="container">
<div id="header">
<h1>pyserve</h1>
<div class="tagline">python application orchestrator</div>
</div>
<div class="breadcrumb">
<a href="../index.html">pyserve</a> » <a href="/reference/">Reference</a> » Extensions
</div>
<div id="content">
<h2>Extensions</h2>
<p>pyserve uses a modular extension system for adding functionality. Extensions
are loaded in order and can process requests and modify responses.</p>
<h3>Built-in Extensions</h3>
<table class="dirindex">
<tr>
<td><code>process_orchestration</code></td>
<td>Run ASGI/WSGI apps in isolated processes with health monitoring</td>
</tr>
<tr>
<td><code>routing</code></td>
<td>nginx-style URL routing with regex patterns</td>
</tr>
<tr>
<td><code>asgi</code></td>
<td>Mount ASGI/WSGI applications in-process</td>
</tr>
<tr>
<td><code>security</code></td>
<td>Security headers and IP filtering</td>
</tr>
<tr>
<td><code>caching</code></td>
<td>Response caching (in development)</td>
</tr>
<tr>
<td><code>monitoring</code></td>
<td>Request metrics and statistics</td>
</tr>
</table>
<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>
- <span class="directive">type:</span> <span class="value">security</span>
<span class="directive">config:</span>
<span class="comment"># ...</span></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>
<h3>Security Extension</h3>
<p>Adds security headers and IP-based access control.</p>
<h4>Configuration Options</h4>
<dl>
<dt>security_headers</dt>
<dd>Dictionary of security headers to add to all responses</dd>
<dt>allowed_ips</dt>
<dd>List of allowed IP addresses (whitelist mode)</dd>
<dt>blocked_ips</dt>
<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>
<p>Default security headers if not specified:</p>
<ul class="indent">
<li><code>X-Content-Type-Options: nosniff</code></li>
<li><code>X-Frame-Options: DENY</code></li>
<li><code>X-XSS-Protection: 1; mode=block</code></li>
</ul>
<h3>Caching Extension</h3>
<p>Response caching for improved performance. <em>(Currently in development)</em></p>
<h4>Configuration Options</h4>
<dl>
<dt>cache_patterns</dt>
<dd>URL patterns to cache</dd>
<dt>cache_ttl</dt>
<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>
<h3>Monitoring Extension</h3>
<p>Collects request metrics and provides statistics.</p>
<h4>Configuration Options</h4>
<dl>
<dt>enable_metrics</dt>
<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>
<p>Collected metrics (available at <code>/metrics</code>):</p>
<ul class="indent">
<li><code>request_count</code> — Total number of requests</li>
<li><code>error_count</code> — Number of requests with 4xx/5xx status</li>
<li><code>error_rate</code> — Error rate (errors / total)</li>
<li><code>avg_response_time</code> — Average response time in seconds</li>
</ul>
<h3>Built-in Endpoints</h3>
<p>pyserve provides built-in endpoints regardless of extensions:</p>
<table class="dirindex">
<tr>
<td><code>/health</code></td>
<td>Health check endpoint, returns <code>200 OK</code></td>
</tr>
<tr>
<td><code>/metrics</code></td>
<td>JSON metrics from all extensions</td>
</tr>
</table>
<h3>Extension Processing Order</h3>
<p>Extensions process requests in the order they are defined:</p>
<ol class="indent">
<li>Request comes in</li>
<li>Each extension's <code>process_request</code> is called in order</li>
<li>First extension to return a response wins</li>
<li>Response passes through each extension's <code>process_response</code></li>
<li>Response is sent to client</li>
</ol>
<div class="note">
<strong>Note:</strong> Place the <code>routing</code> extension last if you want
other extensions (like security) to process requests first.
</div>
<h3>ASGI Extension</h3>
<p>Mount external ASGI/WSGI applications (FastAPI, Flask, Django, etc.) at specified paths.</p>
<h4>Configuration Options</h4>
<dl>
<dt>mounts</dt>
<dd>List of mount configurations (see below)</dd>
</dl>
<h4>Mount Configuration</h4>
<dl>
<dt>path</dt>
<dd>URL path where the app will be mounted. Example: <code>/api</code></dd>
<dt>app_path</dt>
<dd>Python import path. Format: <code>module:attribute</code></dd>
<dt>app_type</dt>
<dd>Application type: <code>asgi</code> or <code>wsgi</code>. Default: <code>asgi</code></dd>
<dt>module_path</dt>
<dd>Optional path to add to <code>sys.path</code></dd>
<dt>factory</dt>
<dd>If <code>true</code>, call as factory function. Default: <code>false</code></dd>
<dt>factory_args</dt>
<dd>Arguments to pass to factory function</dd>
<dt>name</dt>
<dd>Friendly name for logging</dd>
<dt>strip_path</dt>
<dd>Remove mount path from request URL. Default: <code>true</code></dd>
<dt>django_settings</dt>
<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>
<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>
<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>
<p>Supported frameworks:</p>
<ul class="indent">
<li><strong>FastAPI</strong> — Native ASGI (<code>app_type: asgi</code>)</li>
<li><strong>Starlette</strong> — Native ASGI (<code>app_type: asgi</code>)</li>
<li><strong>Flask</strong> — WSGI, auto-wrapped (<code>app_type: wsgi</code>)</li>
<li><strong>Django</strong> — Use <code>django_settings</code> parameter</li>
<li><strong>Custom ASGI</strong> — Any ASGI-compatible application</li>
</ul>
<div class="note">
<strong>Note:</strong> For WSGI applications, install <code>a2wsgi</code> or <code>asgiref</code>:
<code>pip install a2wsgi</code>
</div>
<p>See <a href="../guides/asgi-mount.html">ASGI Mounting Guide</a> for detailed documentation.</p>
<h3>Process Orchestration Extension</h3>
<p>The flagship extension for running apps in isolated subprocesses. <strong>Recommended for production.</strong></p>
<h4>Key Features</h4>
<ul class="indent">
<li>Process isolation — each app runs in its own subprocess</li>
<li>Health monitoring with automatic restart</li>
<li>Multi-worker support per application</li>
<li>Dynamic port allocation (9000-9999)</li>
<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>
- <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>
<h4>App Configuration</h4>
<dl>
<dt>name</dt>
<dd>Unique identifier (required)</dd>
<dt>path</dt>
<dd>URL path prefix (required)</dd>
<dt>app_path</dt>
<dd>Python import path (required)</dd>
<dt>app_type</dt>
<dd><code>asgi</code> or <code>wsgi</code>. Default: <code>asgi</code></dd>
<dt>workers</dt>
<dd>Number of uvicorn workers. Default: <code>1</code></dd>
<dt>health_check_path</dt>
<dd>Health endpoint. Default: <code>/health</code></dd>
<dt>max_restart_count</dt>
<dd>Max restart attempts. Default: <code>5</code></dd>
</dl>
<p>See <a href="../guides/process-orchestration.html">Process Orchestration Guide</a> for full documentation.</p>
</div>
<div id="footer">
<p>pyserve &copy; 2024-2025 | MIT License</p>
</div>
</div>
</body>
</html>
+49
View File
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reference - pyserve</title>
<link rel="stylesheet" href="../style.css">
</head>
<body>
<div id="container">
<div id="header">
<h1>pyserve</h1>
<div class="tagline">python application orchestrator</div>
</div>
<div class="breadcrumb">
<a href="../index.html">pyserve</a> » Reference
</div>
<div id="content">
<h2>Reference</h2>
<p>API and CLI reference documentation.</p>
<table class="dirindex">
<tr>
<td class="icon"><span class="file">📄</span></td>
<td><a href="cli.html">CLI Reference</a></td>
<td class="desc">Command-line interface options</td>
</tr>
<tr>
<td class="icon"><span class="file">📄</span></td>
<td><a href="extensions.html">Extensions</a></td>
<td class="desc">Built-in extension modules</td>
</tr>
<tr>
<td class="icon"><span class="file">📄</span></td>
<td><a href="asgi-mount.html">ASGI Mount API</a></td>
<td class="desc">Python API for mounting ASGI/WSGI applications</td>
</tr>
</table>
</div>
<div id="footer">
<p>pyserve &copy; 2024-2025 | MIT License</p>
</div>
</div>
</body>
</html>