pywire 0.1.0__py3-none-any.whl → 0.1.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.
- {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/METADATA +23 -1
- pywire-0.1.1.dist-info/RECORD +9 -0
- pywire/__init__.py +0 -2
- pywire/cli/__init__.py +0 -1
- pywire/cli/generators.py +0 -48
- pywire/cli/main.py +0 -309
- pywire/cli/tui.py +0 -563
- pywire/cli/validate.py +0 -26
- pywire/client/.prettierignore +0 -8
- pywire/client/.prettierrc +0 -7
- pywire/client/build.mjs +0 -73
- pywire/client/eslint.config.js +0 -46
- pywire/client/package.json +0 -39
- pywire/client/pnpm-lock.yaml +0 -2971
- pywire/client/src/core/app.ts +0 -263
- pywire/client/src/core/dom-updater.test.ts +0 -78
- pywire/client/src/core/dom-updater.ts +0 -321
- pywire/client/src/core/index.ts +0 -5
- pywire/client/src/core/transport-manager.test.ts +0 -179
- pywire/client/src/core/transport-manager.ts +0 -159
- pywire/client/src/core/transports/base.ts +0 -122
- pywire/client/src/core/transports/http.ts +0 -142
- pywire/client/src/core/transports/index.ts +0 -13
- pywire/client/src/core/transports/websocket.ts +0 -97
- pywire/client/src/core/transports/webtransport.ts +0 -149
- pywire/client/src/dev/dev-app.ts +0 -93
- pywire/client/src/dev/error-trace.test.ts +0 -97
- pywire/client/src/dev/error-trace.ts +0 -76
- pywire/client/src/dev/index.ts +0 -4
- pywire/client/src/dev/status-overlay.ts +0 -63
- pywire/client/src/events/handler.test.ts +0 -318
- pywire/client/src/events/handler.ts +0 -454
- pywire/client/src/pywire.core.ts +0 -22
- pywire/client/src/pywire.dev.ts +0 -27
- pywire/client/tsconfig.json +0 -17
- pywire/client/vitest.config.ts +0 -15
- pywire/compiler/__init__.py +0 -6
- pywire/compiler/ast_nodes.py +0 -304
- pywire/compiler/attributes/__init__.py +0 -6
- pywire/compiler/attributes/base.py +0 -24
- pywire/compiler/attributes/conditional.py +0 -37
- pywire/compiler/attributes/events.py +0 -55
- pywire/compiler/attributes/form.py +0 -37
- pywire/compiler/attributes/loop.py +0 -75
- pywire/compiler/attributes/reactive.py +0 -34
- pywire/compiler/build.py +0 -28
- pywire/compiler/build_artifacts.py +0 -342
- pywire/compiler/codegen/__init__.py +0 -5
- pywire/compiler/codegen/attributes/__init__.py +0 -6
- pywire/compiler/codegen/attributes/base.py +0 -19
- pywire/compiler/codegen/attributes/events.py +0 -35
- pywire/compiler/codegen/directives/__init__.py +0 -6
- pywire/compiler/codegen/directives/base.py +0 -16
- pywire/compiler/codegen/directives/path.py +0 -53
- pywire/compiler/codegen/generator.py +0 -2341
- pywire/compiler/codegen/template.py +0 -2178
- pywire/compiler/directives/__init__.py +0 -7
- pywire/compiler/directives/base.py +0 -20
- pywire/compiler/directives/component.py +0 -33
- pywire/compiler/directives/context.py +0 -93
- pywire/compiler/directives/layout.py +0 -49
- pywire/compiler/directives/no_spa.py +0 -24
- pywire/compiler/directives/path.py +0 -71
- pywire/compiler/directives/props.py +0 -88
- pywire/compiler/exceptions.py +0 -19
- pywire/compiler/interpolation/__init__.py +0 -6
- pywire/compiler/interpolation/base.py +0 -28
- pywire/compiler/interpolation/jinja.py +0 -272
- pywire/compiler/parser.py +0 -750
- pywire/compiler/paths.py +0 -29
- pywire/compiler/preprocessor.py +0 -43
- pywire/core/wire.py +0 -119
- pywire/py.typed +0 -0
- pywire/runtime/__init__.py +0 -7
- pywire/runtime/aioquic_server.py +0 -194
- pywire/runtime/app.py +0 -889
- pywire/runtime/compile_error_page.py +0 -195
- pywire/runtime/debug.py +0 -203
- pywire/runtime/dev_server.py +0 -434
- pywire/runtime/dev_server.py.broken +0 -268
- pywire/runtime/error_page.py +0 -64
- pywire/runtime/error_renderer.py +0 -23
- pywire/runtime/escape.py +0 -23
- pywire/runtime/files.py +0 -40
- pywire/runtime/helpers.py +0 -97
- pywire/runtime/http_transport.py +0 -253
- pywire/runtime/loader.py +0 -272
- pywire/runtime/logging.py +0 -72
- pywire/runtime/page.py +0 -384
- pywire/runtime/pydantic_integration.py +0 -52
- pywire/runtime/router.py +0 -229
- pywire/runtime/server.py +0 -25
- pywire/runtime/style_collector.py +0 -31
- pywire/runtime/upload_manager.py +0 -76
- pywire/runtime/validation.py +0 -449
- pywire/runtime/websocket.py +0 -665
- pywire/runtime/webtransport_handler.py +0 -195
- pywire-0.1.0.dist-info/RECORD +0 -104
- {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/WHEEL +0 -0
- {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/entry_points.txt +0 -0
- {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pywire
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: HTML-over-the-wire Python web framework
|
|
5
5
|
Author-email: Reece Holmdahl <reece@pywire.dev>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -35,6 +35,28 @@ Description-Content-Type: text/markdown
|
|
|
35
35
|
|
|
36
36
|
The core framework for pywire.
|
|
37
37
|
|
|
38
|
+
<!-- INSTALL_MESSAGE_TEMPLATE_START -->
|
|
39
|
+
## 🚀 Quick Start
|
|
40
|
+
|
|
41
|
+
If you already have [uv](https://docs.astral.sh/uv/) installed, you can get started instantly:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
uvx create-pywire-app
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If you don't have `uv` installed or aren't sure, use our installer script which handles the setup for you:
|
|
48
|
+
|
|
49
|
+
### macOS / Linux
|
|
50
|
+
```bash
|
|
51
|
+
curl -fsSL pywire.dev/install | sh
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Windows (PowerShell)
|
|
55
|
+
```powershell
|
|
56
|
+
irm pywire.dev/install.ps1 | iex
|
|
57
|
+
```
|
|
58
|
+
<!-- INSTALL_MESSAGE_TEMPLATE_END -->
|
|
59
|
+
|
|
38
60
|
<!-- SUPPORT_MESSAGE_TEMPLATE_START -->
|
|
39
61
|
## ❤️ Support pywire
|
|
40
62
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
pywire/templates/error/404.html,sha256=cZavWjs-aXoSRpEy-8D0rulP7g5Sn31MsIXmkZUQnnA,256
|
|
2
|
+
pywire/templates/error/500.html,sha256=545CPh1XyhN-zdSAD8R-6Cuos6hLC--TXJy4EQHrpjM,1282
|
|
3
|
+
pywire/templates/error/base.html,sha256=nlBlBIg-VFAWtTLWky7c2igNVsFjj9M8Wyw3v3_4KGQ,4863
|
|
4
|
+
pywire/templates/error/compile_error.html,sha256=FMqU3HXYWMeiXG3eTAysNpAHH-AOPx2Apbz8f_HBvtw,837
|
|
5
|
+
pywire-0.1.1.dist-info/METADATA,sha256=xWdhI55nsp-50ExXLdhjZ1SYNtju61tkkNTXaxSzALk,2386
|
|
6
|
+
pywire-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
7
|
+
pywire-0.1.1.dist-info/entry_points.txt,sha256=L0n4cNLZIocOo6lG05uOoUN1GHgA_uQOHvapDAhULBI,47
|
|
8
|
+
pywire-0.1.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
9
|
+
pywire-0.1.1.dist-info/RECORD,,
|
pywire/__init__.py
DELETED
pywire/cli/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""CLI module."""
|
pywire/cli/generators.py
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"""Code generators for scaffolding."""
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def generate_page(name: str) -> None:
|
|
7
|
-
"""Generate a new page."""
|
|
8
|
-
pages_dir = Path("pages")
|
|
9
|
-
pages_dir.mkdir(exist_ok=True)
|
|
10
|
-
|
|
11
|
-
page_file = pages_dir / f"{name}.pywire"
|
|
12
|
-
|
|
13
|
-
if page_file.exists():
|
|
14
|
-
raise ValueError(f"Page {name} already exists")
|
|
15
|
-
|
|
16
|
-
template = f"""!path {{ '{name}': '/{name}' }}
|
|
17
|
-
|
|
18
|
-
# Page code here
|
|
19
|
-
|
|
20
|
-
---html---
|
|
21
|
-
<div>
|
|
22
|
-
<h1>{name.title()} Page</h1>
|
|
23
|
-
<p>Welcome to the {name} page!</p>
|
|
24
|
-
</div>
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
page_file.write_text(template)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def generate_component(name: str) -> None:
|
|
31
|
-
"""Generate a new component."""
|
|
32
|
-
components_dir = Path("components")
|
|
33
|
-
components_dir.mkdir(exist_ok=True)
|
|
34
|
-
|
|
35
|
-
component_file = components_dir / f"{name}.pywire"
|
|
36
|
-
|
|
37
|
-
if component_file.exists():
|
|
38
|
-
raise ValueError(f"Component {name} already exists")
|
|
39
|
-
|
|
40
|
-
template = f"""# Component code here
|
|
41
|
-
|
|
42
|
-
---html---
|
|
43
|
-
<div class="{name}">
|
|
44
|
-
<!-- Component code here -->
|
|
45
|
-
</div>
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
component_file.write_text(template)
|
pywire/cli/main.py
DELETED
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
"""Main CLI entry point."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any, Optional
|
|
7
|
-
|
|
8
|
-
import rich.panel
|
|
9
|
-
import rich_click as click
|
|
10
|
-
|
|
11
|
-
# Astro-like styling configuration (Cyan Theme)
|
|
12
|
-
click.rich_click.USE_RICH_MARKUP = True
|
|
13
|
-
click.rich_click.STYLE_HELPTEXT_FIRST = True
|
|
14
|
-
click.rich_click.STYLE_COMMANDS_TABLE_SHOW_LINES = False
|
|
15
|
-
click.rich_click.STYLE_COMMANDS_TABLE_PAD_EDGE = False
|
|
16
|
-
click.rich_click.STYLE_COMMANDS_TABLE_BOX = None
|
|
17
|
-
click.rich_click.STYLE_COMMANDS_TABLE_EXPAND = False
|
|
18
|
-
click.rich_click.STYLE_OPTIONS_TABLE_EXPAND = False
|
|
19
|
-
click.rich_click.STYLE_COMMANDS_TABLE_HEADER = "bold magenta"
|
|
20
|
-
click.rich_click.STYLE_COMMANDS_TABLE_COLUMN_WIDTH_RATIO = None
|
|
21
|
-
click.rich_click.SHOW_ARGUMENTS = True
|
|
22
|
-
click.rich_click.GROUP_ARGUMENTS_OPTIONS = True
|
|
23
|
-
click.rich_click.STYLE_ERRORS_SUGGESTION = "magenta italic"
|
|
24
|
-
click.rich_click.ERRORS_SUGGESTION = "Try running 'pywire --help' for more information."
|
|
25
|
-
click.rich_click.ERRORS_EPILOGUE = "To find out more, visit [link=https://github.com/pywire/pywire]https://github.com/pywire/pywire[/link]"
|
|
26
|
-
click.rich_click.STYLE_OPTIONS_TABLE_BOX = None
|
|
27
|
-
click.rich_click.STYLE_COMMANDS_PANEL_BOX = None
|
|
28
|
-
click.rich_click.STYLE_OPTIONS_PANEL_BOX = None
|
|
29
|
-
|
|
30
|
-
# Cyan theme
|
|
31
|
-
click.rich_click.STYLE_HEADER_TEXT = "bold cyan"
|
|
32
|
-
click.rich_click.STYLE_OPTION = "cyan"
|
|
33
|
-
click.rich_click.STYLE_SWITCH = "cyan"
|
|
34
|
-
click.rich_click.STYLE_METAVAR = "dim white"
|
|
35
|
-
click.rich_click.STYLE_USAGE_COMMAND = "cyan"
|
|
36
|
-
click.rich_click.STYLE_USAGE = "dim"
|
|
37
|
-
|
|
38
|
-
# Grouping options and commands
|
|
39
|
-
click.rich_click.OPTION_GROUPS = {
|
|
40
|
-
"pywire": [
|
|
41
|
-
{
|
|
42
|
-
"name": "Global Flags",
|
|
43
|
-
"options": ["--help", "--version"],
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
click.rich_click.COMMAND_GROUPS = {
|
|
49
|
-
"pywire": [
|
|
50
|
-
{
|
|
51
|
-
"name": "Commands",
|
|
52
|
-
"commands": ["dev", "run", "build"],
|
|
53
|
-
}
|
|
54
|
-
]
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def import_app(app_str: str) -> Any:
|
|
59
|
-
"""Import application from string (e.g. 'main:app')."""
|
|
60
|
-
if ":" not in app_str:
|
|
61
|
-
raise click.BadParameter("App must be in format 'module:app'", param_hint="APP")
|
|
62
|
-
|
|
63
|
-
module_name, app_name = app_str.split(":", 1)
|
|
64
|
-
|
|
65
|
-
# Add current directory to path so we can import local modules
|
|
66
|
-
sys.path.insert(0, os.getcwd())
|
|
67
|
-
|
|
68
|
-
try:
|
|
69
|
-
import importlib
|
|
70
|
-
|
|
71
|
-
module = importlib.import_module(module_name)
|
|
72
|
-
except ImportError as e:
|
|
73
|
-
raise click.BadParameter(
|
|
74
|
-
f"Could not import module '{module_name}': {e}", param_hint="APP"
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
try:
|
|
78
|
-
app = getattr(module, app_name)
|
|
79
|
-
except AttributeError:
|
|
80
|
-
raise click.BadParameter(
|
|
81
|
-
f"Attribute '{app_name}' not found in module '{module_name}'",
|
|
82
|
-
param_hint="APP",
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
return app
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def _discover_app_str() -> str:
|
|
89
|
-
"""Try to discover the app string automatically."""
|
|
90
|
-
cwd = Path(os.getcwd())
|
|
91
|
-
|
|
92
|
-
# Priority: main.py, app.py, api.py
|
|
93
|
-
# Also check src/ directory
|
|
94
|
-
search_paths = [cwd, cwd / "src"]
|
|
95
|
-
|
|
96
|
-
for path in search_paths:
|
|
97
|
-
if not path.exists():
|
|
98
|
-
continue
|
|
99
|
-
|
|
100
|
-
for filename in ["main.py", "app.py", "api.py"]:
|
|
101
|
-
if (path / filename).exists():
|
|
102
|
-
# Check for common app instance names: app, api
|
|
103
|
-
module_name = filename[:-3]
|
|
104
|
-
|
|
105
|
-
# Construct module path (e.g. src.main)
|
|
106
|
-
if path.name == "src":
|
|
107
|
-
module_path = f"src.{module_name}"
|
|
108
|
-
else:
|
|
109
|
-
module_path = module_name
|
|
110
|
-
|
|
111
|
-
# Simple check: try to import and look for app
|
|
112
|
-
try:
|
|
113
|
-
sys.path.insert(0, str(cwd))
|
|
114
|
-
import importlib
|
|
115
|
-
|
|
116
|
-
module = importlib.import_module(module_path)
|
|
117
|
-
|
|
118
|
-
if hasattr(module, "app"):
|
|
119
|
-
return f"{module_path}:app"
|
|
120
|
-
if hasattr(module, "api"):
|
|
121
|
-
return f"{module_path}:api"
|
|
122
|
-
|
|
123
|
-
except ImportError:
|
|
124
|
-
continue
|
|
125
|
-
|
|
126
|
-
raise click.UsageError(
|
|
127
|
-
"Could not auto-discover app. Please provide 'APP' argument (e.g. 'main:app')."
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# Workaround: rich-click wraps tables in Panels which default to expand=True.
|
|
132
|
-
# We monkeypatch Panel to default expand=False to allow natural resizing.
|
|
133
|
-
original_panel_init = rich.panel.Panel.__init__
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def panel_init(self, *args, **kwargs):
|
|
137
|
-
kwargs.setdefault("expand", False)
|
|
138
|
-
original_panel_init(self, *args, **kwargs)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
rich.panel.Panel.__init__ = panel_init # type: ignore[method-assign]
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
@click.group()
|
|
145
|
-
@click.version_option()
|
|
146
|
-
def cli() -> None:
|
|
147
|
-
"""
|
|
148
|
-
[bold white on cyan] pywire [/] [bold cyan]v0.1.0[/] Build faster python web apps.
|
|
149
|
-
|
|
150
|
-
Run [bold cyan]pywire dev APP[/] to start development server.
|
|
151
|
-
Run [bold cyan]pywire run APP[/] to start production server.
|
|
152
|
-
|
|
153
|
-
[dim]APP should be a string in format 'module:instance', e.g. 'src.main:app' or 'main:app'
|
|
154
|
-
If not provided, pywire tries to discover it in main.py, app.py, etc.[/dim]
|
|
155
|
-
"""
|
|
156
|
-
pass
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
@cli.command()
|
|
160
|
-
@click.argument("app", required=False)
|
|
161
|
-
@click.option("--host", default="127.0.0.1", help="Host to bind to")
|
|
162
|
-
@click.option("--port", default=3000, type=int, help="Port to bind to")
|
|
163
|
-
@click.option("--ssl-keyfile", default=None, help="SSL key file")
|
|
164
|
-
@click.option("--ssl-certfile", default=None, help="SSL certificate file")
|
|
165
|
-
@click.option("--env-file", default=None, help="Environment configuration file")
|
|
166
|
-
@click.option("--no-tui", is_flag=True, help="Disable TUI dashboard")
|
|
167
|
-
def dev(
|
|
168
|
-
app: Optional[str],
|
|
169
|
-
host: str,
|
|
170
|
-
port: int,
|
|
171
|
-
ssl_keyfile: Optional[str],
|
|
172
|
-
ssl_certfile: Optional[str],
|
|
173
|
-
env_file: Optional[str],
|
|
174
|
-
no_tui: bool,
|
|
175
|
-
) -> None:
|
|
176
|
-
"""Start development server."""
|
|
177
|
-
import asyncio
|
|
178
|
-
|
|
179
|
-
from pywire.runtime.dev_server import run_dev_server
|
|
180
|
-
|
|
181
|
-
if not app:
|
|
182
|
-
app = _discover_app_str()
|
|
183
|
-
if no_tui:
|
|
184
|
-
click.echo(f"🔍 Auto-discovered app: {app}")
|
|
185
|
-
|
|
186
|
-
# Verify import
|
|
187
|
-
import_app(app)
|
|
188
|
-
|
|
189
|
-
if no_tui:
|
|
190
|
-
click.echo(f"🚀 Starting pywire dev server on http://{host}:{port}")
|
|
191
|
-
if ssl_certfile:
|
|
192
|
-
click.echo("🔒 SSL enabled")
|
|
193
|
-
|
|
194
|
-
asyncio.run(
|
|
195
|
-
run_dev_server(
|
|
196
|
-
app_str=app, # Pass string for reloadability hooks if needed
|
|
197
|
-
host=host,
|
|
198
|
-
port=port,
|
|
199
|
-
ssl_keyfile=ssl_keyfile,
|
|
200
|
-
ssl_certfile=ssl_certfile,
|
|
201
|
-
)
|
|
202
|
-
)
|
|
203
|
-
else:
|
|
204
|
-
from pywire.cli.tui import start_tui
|
|
205
|
-
|
|
206
|
-
start_tui(
|
|
207
|
-
app_path=app,
|
|
208
|
-
host=host,
|
|
209
|
-
port=port,
|
|
210
|
-
ssl_keyfile=ssl_keyfile,
|
|
211
|
-
ssl_certfile=ssl_certfile,
|
|
212
|
-
env_file=env_file,
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
@cli.command()
|
|
217
|
-
@click.argument("app", required=False)
|
|
218
|
-
@click.option(
|
|
219
|
-
"--optimize",
|
|
220
|
-
is_flag=True,
|
|
221
|
-
help="Compile bytecode artifacts for faster import.",
|
|
222
|
-
)
|
|
223
|
-
@click.option(
|
|
224
|
-
"--out-dir",
|
|
225
|
-
default=".pywire/build",
|
|
226
|
-
help="Output directory for build artifacts.",
|
|
227
|
-
)
|
|
228
|
-
@click.option(
|
|
229
|
-
"--pages-dir",
|
|
230
|
-
default=None,
|
|
231
|
-
help="Override pages directory (default: app.pages_dir).",
|
|
232
|
-
)
|
|
233
|
-
def build(
|
|
234
|
-
app: Optional[str], optimize: bool, out_dir: str, pages_dir: Optional[str]
|
|
235
|
-
) -> None:
|
|
236
|
-
"""Build the application for production."""
|
|
237
|
-
if not app:
|
|
238
|
-
app = _discover_app_str()
|
|
239
|
-
|
|
240
|
-
click.echo(f"🔨 Building {app}...")
|
|
241
|
-
|
|
242
|
-
app_instance = import_app(app)
|
|
243
|
-
|
|
244
|
-
if pages_dir:
|
|
245
|
-
resolved_pages_dir = Path(pages_dir)
|
|
246
|
-
elif hasattr(app_instance, "pages_dir"):
|
|
247
|
-
resolved_pages_dir = Path(app_instance.pages_dir)
|
|
248
|
-
else:
|
|
249
|
-
resolved_pages_dir = Path("pages")
|
|
250
|
-
|
|
251
|
-
from pywire.compiler.build import build_project
|
|
252
|
-
|
|
253
|
-
summary = build_project(
|
|
254
|
-
optimize=optimize,
|
|
255
|
-
pages_dir=resolved_pages_dir,
|
|
256
|
-
out_dir=Path(out_dir),
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
click.echo(
|
|
260
|
-
"✅ Build complete "
|
|
261
|
-
f"(pages={summary.pages}, layouts={summary.layouts}, "
|
|
262
|
-
f"components={summary.components}, out={summary.out_dir})"
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
@cli.command()
|
|
267
|
-
@click.argument("app", required=False)
|
|
268
|
-
@click.option("--host", default="0.0.0.0", help="Host to bind to")
|
|
269
|
-
@click.option("--port", default=8000, type=int, help="Port to bind to")
|
|
270
|
-
@click.option("--workers", default=None, type=int, help="Number of worker processes")
|
|
271
|
-
@click.option("--no-access-log", is_flag=True, help="Disable access logging")
|
|
272
|
-
def run(
|
|
273
|
-
app: Optional[str],
|
|
274
|
-
host: str,
|
|
275
|
-
port: int,
|
|
276
|
-
workers: Optional[int],
|
|
277
|
-
no_access_log: bool,
|
|
278
|
-
) -> None:
|
|
279
|
-
"""Run production server using Uvicorn."""
|
|
280
|
-
import multiprocessing
|
|
281
|
-
|
|
282
|
-
import uvicorn
|
|
283
|
-
|
|
284
|
-
if not app:
|
|
285
|
-
app = _discover_app_str()
|
|
286
|
-
click.echo(f"🔍 Auto-discovered app: {app}")
|
|
287
|
-
|
|
288
|
-
if workers is None:
|
|
289
|
-
workers = (multiprocessing.cpu_count() * 2) + 1
|
|
290
|
-
|
|
291
|
-
click.echo(f"🚀 Starting production server for {app}")
|
|
292
|
-
click.echo(f"🌍 Listening on http://{host}:{port}")
|
|
293
|
-
click.echo(f"👷 Workers: {workers}")
|
|
294
|
-
|
|
295
|
-
# Locate the app object to verify, but pass string to uvicorn
|
|
296
|
-
import_app(app)
|
|
297
|
-
|
|
298
|
-
uvicorn.run(
|
|
299
|
-
app,
|
|
300
|
-
host=host,
|
|
301
|
-
port=port,
|
|
302
|
-
workers=workers,
|
|
303
|
-
access_log=not no_access_log,
|
|
304
|
-
factory=False,
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if __name__ == "__main__":
|
|
309
|
-
cli()
|