uvpy 0.1.0__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.
- uvpy/__init__.py +29 -0
- uvpy/__main__.py +13 -0
- uvpy/app_runner.py +89 -0
- uvpy/cli.py +767 -0
- uvpy/imports.py +258 -0
- uvpy/sandbox.py +274 -0
- uvpy/venv.py +623 -0
- uvpy-0.1.0.dist-info/METADATA +156 -0
- uvpy-0.1.0.dist-info/RECORD +12 -0
- uvpy-0.1.0.dist-info/WHEEL +4 -0
- uvpy-0.1.0.dist-info/entry_points.txt +2 -0
- uvpy-0.1.0.dist-info/licenses/LICENSE +21 -0
uvpy/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
uvpy - Portable Python App Framework
|
|
4
|
+
|
|
5
|
+
A framework for isolated Python apps with:
|
|
6
|
+
- Portable Python (optional)
|
|
7
|
+
- Isolated venvs per app (via uv)
|
|
8
|
+
- Offline installation from local pypi/
|
|
9
|
+
- Sandbox: localhost only, no telemetry
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from importlib.metadata import version as _get_version
|
|
14
|
+
__version__ = _get_version("uvpy")
|
|
15
|
+
except Exception:
|
|
16
|
+
__version__ = "0.1.0"
|
|
17
|
+
|
|
18
|
+
from .sandbox import activate as activate_sandbox
|
|
19
|
+
from .imports import try_import, require, is_available, LazyModule, optional_import
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"__version__",
|
|
23
|
+
"activate_sandbox",
|
|
24
|
+
"try_import",
|
|
25
|
+
"require",
|
|
26
|
+
"is_available",
|
|
27
|
+
"LazyModule",
|
|
28
|
+
"optional_import",
|
|
29
|
+
]
|
uvpy/__main__.py
ADDED
uvpy/app_runner.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
uvpy App Runner - Executes apps in their venv.
|
|
4
|
+
|
|
5
|
+
Called by uvpy via `uv run`:
|
|
6
|
+
uv run --directory apps/<app> python /path/to/app_runner.py <app_path> [args...]
|
|
7
|
+
|
|
8
|
+
This script:
|
|
9
|
+
1. Activates the sandbox (security)
|
|
10
|
+
2. Loads the app from main.py
|
|
11
|
+
3. Parses CLI arguments
|
|
12
|
+
4. Executes app.run(args)
|
|
13
|
+
"""
|
|
14
|
+
import sys
|
|
15
|
+
import os
|
|
16
|
+
import argparse
|
|
17
|
+
import importlib.util
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def activate_sandbox() -> None:
|
|
22
|
+
"""Activate the security sandbox."""
|
|
23
|
+
# sandbox.py is in the same directory as app_runner.py
|
|
24
|
+
runner_dir = Path(__file__).parent
|
|
25
|
+
sandbox_path = runner_dir / "sandbox.py"
|
|
26
|
+
|
|
27
|
+
if sandbox_path.exists():
|
|
28
|
+
spec = importlib.util.spec_from_file_location("sandbox", sandbox_path)
|
|
29
|
+
sandbox = importlib.util.module_from_spec(spec)
|
|
30
|
+
spec.loader.exec_module(sandbox)
|
|
31
|
+
sandbox.activate()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def load_app_module(app_path: Path):
|
|
35
|
+
"""Load main.py of the app."""
|
|
36
|
+
main_py = app_path / "main.py"
|
|
37
|
+
|
|
38
|
+
if not main_py.exists():
|
|
39
|
+
print(f"ERROR: {main_py} not found", file=sys.stderr)
|
|
40
|
+
sys.exit(1)
|
|
41
|
+
|
|
42
|
+
spec = importlib.util.spec_from_file_location("main", main_py)
|
|
43
|
+
module = importlib.util.module_from_spec(spec)
|
|
44
|
+
|
|
45
|
+
# Add app directory to sys.path
|
|
46
|
+
sys.path.insert(0, str(app_path))
|
|
47
|
+
|
|
48
|
+
spec.loader.exec_module(module)
|
|
49
|
+
return module
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main() -> int:
|
|
53
|
+
"""Main function of the App Runner."""
|
|
54
|
+
if len(sys.argv) < 2:
|
|
55
|
+
print("Usage: app_runner.py <app_path> [args...]", file=sys.stderr)
|
|
56
|
+
return 1
|
|
57
|
+
|
|
58
|
+
app_path = Path(sys.argv[1]).resolve()
|
|
59
|
+
app_args = sys.argv[2:]
|
|
60
|
+
|
|
61
|
+
# Activate sandbox (security)
|
|
62
|
+
activate_sandbox()
|
|
63
|
+
|
|
64
|
+
# Load app module
|
|
65
|
+
module = load_app_module(app_path)
|
|
66
|
+
|
|
67
|
+
# Create parser and register arguments
|
|
68
|
+
parser = argparse.ArgumentParser(
|
|
69
|
+
prog=app_path.name,
|
|
70
|
+
description=getattr(module, "__doc__", None)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if hasattr(module, "register"):
|
|
74
|
+
module.register(parser)
|
|
75
|
+
|
|
76
|
+
# Parse arguments
|
|
77
|
+
args = parser.parse_args(app_args)
|
|
78
|
+
|
|
79
|
+
# Execute app
|
|
80
|
+
if hasattr(module, "run"):
|
|
81
|
+
result = module.run(args)
|
|
82
|
+
return result if isinstance(result, int) else 0
|
|
83
|
+
else:
|
|
84
|
+
print(f"ERROR: App '{app_path.name}' has no run() function", file=sys.stderr)
|
|
85
|
+
return 1
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
if __name__ == "__main__":
|
|
89
|
+
sys.exit(main())
|