python-script-launcher 0.0.10__py3-none-any.whl → 0.0.11__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.
- python_script_launcher/app.py +28 -5
- python_script_launcher/make_exe.py +62 -26
- python_script_launcher/scripts/multi.py +2 -2
- {python_script_launcher-0.0.10.dist-info → python_script_launcher-0.0.11.dist-info}/METADATA +1 -1
- {python_script_launcher-0.0.10.dist-info → python_script_launcher-0.0.11.dist-info}/RECORD +8 -8
- {python_script_launcher-0.0.10.dist-info → python_script_launcher-0.0.11.dist-info}/WHEEL +0 -0
- {python_script_launcher-0.0.10.dist-info → python_script_launcher-0.0.11.dist-info}/entry_points.txt +0 -0
- {python_script_launcher-0.0.10.dist-info → python_script_launcher-0.0.11.dist-info}/top_level.txt +0 -0
python_script_launcher/app.py
CHANGED
|
@@ -42,13 +42,35 @@ def _default_root():
|
|
|
42
42
|
return p.parent if p.name == "scripts" else p
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
# The launcher package always ships a `static/` folder next to app.py
|
|
46
|
+
# (repo root during development, python_script_launcher/ after wheel
|
|
47
|
+
# install). Remember it so `run --scripts <dir>` and `build --scripts
|
|
48
|
+
# <dir>` can serve the frontend even when <dir> has no static/ of its own.
|
|
49
|
+
_LAUNCHER_DIR = Path(__file__).parent.resolve()
|
|
50
|
+
|
|
51
|
+
|
|
45
52
|
def _default_static(root: Path):
|
|
53
|
+
"""Resolve the default static directory.
|
|
54
|
+
|
|
55
|
+
Search order:
|
|
56
|
+
1. sys._MEIPASS/static (PyInstaller frozen bundle),
|
|
57
|
+
2. <launcher package>/static (source repo or wheel install),
|
|
58
|
+
3. <user scripts root>/static (lets users override the UI).
|
|
59
|
+
|
|
60
|
+
Returns the first existing candidate, falling back to the last one
|
|
61
|
+
for backward compatibility when nothing is on disk yet.
|
|
62
|
+
"""
|
|
63
|
+
candidates = []
|
|
46
64
|
if getattr(sys, "frozen", False):
|
|
47
|
-
meipass =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
meipass = getattr(sys, "_MEIPASS", "")
|
|
66
|
+
if meipass:
|
|
67
|
+
candidates.append(Path(meipass) / "static")
|
|
68
|
+
candidates.append(_LAUNCHER_DIR / "static")
|
|
69
|
+
candidates.append(root / "static")
|
|
70
|
+
for c in candidates:
|
|
71
|
+
if c.exists():
|
|
72
|
+
return c
|
|
73
|
+
return candidates[-1]
|
|
52
74
|
|
|
53
75
|
|
|
54
76
|
DEFAULT_ROOT = _default_root()
|
|
@@ -622,6 +644,7 @@ def create_app(
|
|
|
622
644
|
candidates.append(Path(sys._MEIPASS) / "static" / "index.html")
|
|
623
645
|
candidates.extend([
|
|
624
646
|
static / "index.html",
|
|
647
|
+
_LAUNCHER_DIR / "static" / "index.html",
|
|
625
648
|
project_root / "static" / "index.html",
|
|
626
649
|
])
|
|
627
650
|
for f in candidates:
|
|
@@ -21,6 +21,7 @@ import os
|
|
|
21
21
|
import shutil
|
|
22
22
|
import subprocess
|
|
23
23
|
import sys
|
|
24
|
+
import tempfile
|
|
24
25
|
from importlib.metadata import distributions
|
|
25
26
|
from pathlib import Path
|
|
26
27
|
|
|
@@ -37,6 +38,14 @@ SKIP_PACKAGES = {
|
|
|
37
38
|
"python-script-launcher",
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
# Files copied into the on-the-fly `python_script_launcher/` package so user
|
|
42
|
+
# scripts can `from python_script_launcher.tasks import task` inside the
|
|
43
|
+
# frozen exe (mirrors publish.py's wheel layout).
|
|
44
|
+
_STAGE_PACKAGE_FILES = (
|
|
45
|
+
"app.py", "client.py", "tasks.py", "__init__.py", "__main__.py",
|
|
46
|
+
)
|
|
47
|
+
_STAGE_PACKAGE_NAME = "python_script_launcher"
|
|
48
|
+
|
|
40
49
|
|
|
41
50
|
def _norm(name: str) -> str:
|
|
42
51
|
return (name or "").strip().lower().replace("_", "-")
|
|
@@ -75,6 +84,23 @@ def _clean_target(name: str, dist_dir: Path, build_dir: Path, project: Path,
|
|
|
75
84
|
shutil.rmtree(p, ignore_errors=True)
|
|
76
85
|
|
|
77
86
|
|
|
87
|
+
def _stage_launcher_package(project: Path) -> Path:
|
|
88
|
+
"""Materialize a temporary `python_script_launcher/` package next to the
|
|
89
|
+
top-level source files so PyInstaller can pick it up via `--paths`.
|
|
90
|
+
|
|
91
|
+
Returns the *root* directory to add to PyInstaller search paths; the
|
|
92
|
+
caller is responsible for removing it once the build is done.
|
|
93
|
+
"""
|
|
94
|
+
stage_root = Path(tempfile.mkdtemp(prefix="psl-exe-"))
|
|
95
|
+
pkg_dir = stage_root / _STAGE_PACKAGE_NAME
|
|
96
|
+
pkg_dir.mkdir(parents=True, exist_ok=True)
|
|
97
|
+
for name in _STAGE_PACKAGE_FILES:
|
|
98
|
+
src = project / name
|
|
99
|
+
if src.exists():
|
|
100
|
+
shutil.copy2(src, pkg_dir / name)
|
|
101
|
+
return stage_root
|
|
102
|
+
|
|
103
|
+
|
|
78
104
|
def build_exe(entry, name, *, project=None, dist=None, static=None,
|
|
79
105
|
noconsole=False, onefile=False, clean=False):
|
|
80
106
|
"""Build a single PyInstaller exe.
|
|
@@ -128,31 +154,41 @@ def build_exe(entry, name, *, project=None, dist=None, static=None,
|
|
|
128
154
|
if scripts_dir.exists():
|
|
129
155
|
cmd += ["--add-data", f"{scripts_dir}{os.pathsep}scripts"]
|
|
130
156
|
|
|
131
|
-
# Make `tasks` and
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
# Make both `tasks` (top-level) and `python_script_launcher.tasks`
|
|
158
|
+
# (wheel-style dotted import) usable from user scripts inside the exe.
|
|
159
|
+
# - `--paths <project>` lets `from tasks import task` resolve.
|
|
160
|
+
# - We also stage a temporary `python_script_launcher/` package that
|
|
161
|
+
# mirrors publish.py wheel layout and add its parent to `--paths`, so
|
|
162
|
+
# `from python_script_launcher.tasks import task` resolves the same
|
|
163
|
+
# way it does when installed from the wheel.
|
|
164
|
+
stage_root = _stage_launcher_package(project)
|
|
165
|
+
try:
|
|
166
|
+
cmd += ["--paths", str(project), "--paths", str(stage_root)]
|
|
167
|
+
if (project / "tasks.py").exists():
|
|
168
|
+
cmd += ["--hidden-import", "tasks"]
|
|
169
|
+
cmd += [
|
|
170
|
+
"--hidden-import", _STAGE_PACKAGE_NAME,
|
|
171
|
+
"--hidden-import", f"{_STAGE_PACKAGE_NAME}.tasks",
|
|
172
|
+
"--collect-submodules", _STAGE_PACKAGE_NAME,
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
for pkg in get_installed_packages():
|
|
176
|
+
cmd += ["--collect-all", pkg]
|
|
177
|
+
|
|
178
|
+
if icon_arg:
|
|
179
|
+
cmd += ["--icon", icon_arg]
|
|
180
|
+
|
|
181
|
+
entry_path = Path(entry)
|
|
182
|
+
if not entry_path.is_absolute():
|
|
183
|
+
entry_path = (project / entry_path).resolve()
|
|
184
|
+
cmd.append(str(entry_path))
|
|
185
|
+
|
|
186
|
+
print(f" entry: {entry_path}")
|
|
187
|
+
subprocess.run(cmd, check=True, cwd=str(project))
|
|
188
|
+
out = f"{dist_dir}/{name}.exe" if onefile else f"{dist_dir}/{name}/{name}.exe"
|
|
189
|
+
print(f" [OK] {name} -> {out}\n")
|
|
190
|
+
finally:
|
|
191
|
+
shutil.rmtree(stage_root, ignore_errors=True)
|
|
156
192
|
|
|
157
193
|
|
|
158
194
|
def main():
|
|
@@ -186,4 +222,4 @@ def main():
|
|
|
186
222
|
|
|
187
223
|
|
|
188
224
|
if __name__ == "__main__":
|
|
189
|
-
main()
|
|
225
|
+
main()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Multi-task demo using the wheel-style dotted import."""
|
|
2
2
|
from tasks import task
|
|
3
3
|
|
|
4
4
|
|
|
@@ -21,4 +21,4 @@ def multiline(times: int = 3):
|
|
|
21
21
|
for i in range(times):
|
|
22
22
|
print(f"line {i}", flush=True)
|
|
23
23
|
time.sleep(0.05)
|
|
24
|
-
print("done")
|
|
24
|
+
print("done")
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
python_script_launcher/__init__.py,sha256=XeqJdWwu4-26nBlxeu2urarg3chsKMYYAWKgbUQnUyo,1750
|
|
2
2
|
python_script_launcher/__main__.py,sha256=PiKbURpPYHkB8ZQ7YCSYPKh_YUi6R2FKJEjBDXdgm_c,6824
|
|
3
|
-
python_script_launcher/app.py,sha256=
|
|
3
|
+
python_script_launcher/app.py,sha256=Qr5GAhqiHaKs3eZUkJvsyMInDIPGcJb0m8pJ-D1zuUY,25211
|
|
4
4
|
python_script_launcher/client.py,sha256=pULe9xHv4OuXFAdiFAEHp4uxO8x4D_myyNX6ofqElMg,2964
|
|
5
|
-
python_script_launcher/make_exe.py,sha256=
|
|
5
|
+
python_script_launcher/make_exe.py,sha256=X-_T4c-htHA46nz465Dog1xfUeHj27wWgiwHjHcqFS0,8399
|
|
6
6
|
python_script_launcher/tasks.py,sha256=0f6P88vlUXscnIxXNL5pEQpmr6x-kavAHvDB9Z1qowE,5755
|
|
7
7
|
python_script_launcher/scripts/file_status.py,sha256=D22FwHlsNnJkHz7ICyM3L2xK3vhNaMRl-aJEERrEG8s,1063
|
|
8
8
|
python_script_launcher/scripts/hello.py,sha256=df5gR95rta5J0sDORI0wncCTBS0wGrTX07pEcR9R4_A,517
|
|
9
|
-
python_script_launcher/scripts/multi.py,sha256=
|
|
9
|
+
python_script_launcher/scripts/multi.py,sha256=2HApGhrdt8Xs1WZ9HY7yHXAlabqc1KRC6CHqTUhjWy0,590
|
|
10
10
|
python_script_launcher/scripts/test_modules.py,sha256=AFzAAC2NlYSVcPucNgChguLMEj8m1pmCIB3LIDthOf8,618
|
|
11
11
|
python_script_launcher/static/index.html,sha256=_HTLklrRge6A09l2kb5ucFI4rrm2KMIEgU9xXbPJzT8,15000
|
|
12
|
-
python_script_launcher-0.0.
|
|
13
|
-
python_script_launcher-0.0.
|
|
14
|
-
python_script_launcher-0.0.
|
|
15
|
-
python_script_launcher-0.0.
|
|
16
|
-
python_script_launcher-0.0.
|
|
12
|
+
python_script_launcher-0.0.11.dist-info/METADATA,sha256=rTy-hgyoSPVcP8HDc1jNAAQalUHBWHRweU8QkU_OcKo,6378
|
|
13
|
+
python_script_launcher-0.0.11.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
14
|
+
python_script_launcher-0.0.11.dist-info/entry_points.txt,sha256=13ENRmw4BsgWGLDCr_y1dKB91ed1j6rEyks6JDFzOns,66
|
|
15
|
+
python_script_launcher-0.0.11.dist-info/top_level.txt,sha256=Ap6WryIb1YRnr_QVCmfGjdEVLDNA3dQcTl_-gxX11qk,23
|
|
16
|
+
python_script_launcher-0.0.11.dist-info/RECORD,,
|
|
File without changes
|
{python_script_launcher-0.0.10.dist-info → python_script_launcher-0.0.11.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{python_script_launcher-0.0.10.dist-info → python_script_launcher-0.0.11.dist-info}/top_level.txt
RENAMED
|
File without changes
|