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.
@@ -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 = Path(sys._MEIPASS)
48
- static = meipass / "static"
49
- if static.exists():
50
- return static
51
- return root / "static"
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 the compatibility `python_script_launcher` shim
132
- # importable from user scripts inside the frozen exe. `--paths` lets
133
- # PyInstaller resolve them at build time; hidden-imports pull them into
134
- # the frozen PYZ.
135
- cmd += ["--paths", str(project)]
136
- if (project / "tasks.py").exists():
137
- cmd += ["--hidden-import", "tasks"]
138
- if (project / "python_script_launcher" / "__init__.py").exists():
139
- cmd += ["--hidden-import", "python_script_launcher"]
140
-
141
- for pkg in get_installed_packages():
142
- cmd += ["--collect-all", pkg]
143
-
144
- if icon_arg:
145
- cmd += ["--icon", icon_arg]
146
-
147
- entry_path = Path(entry)
148
- if not entry_path.is_absolute():
149
- entry_path = (project / entry_path).resolve()
150
- cmd.append(str(entry_path))
151
-
152
- print(f" entry: {entry_path}")
153
- subprocess.run(cmd, check=True, cwd=str(project))
154
- out = f"{dist_dir}/{name}.exe" if onefile else f"{dist_dir}/{name}/{name}.exe"
155
- print(f" [OK] {name} -> {out}\n")
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
- """多任务示例: @task 注册多个可运行函数, 无需 __main__ guard."""
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-script-launcher
3
- Version: 0.0.10
3
+ Version: 0.0.11
4
4
  Summary: Package Python scripts into standalone exe (Web + desktop client).
5
5
  License-Expression: MIT
6
6
  Keywords: pyinstaller,packaging,exe,launcher,scripts
@@ -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=3MBCyOtO5pFAcwyrkRkzg5a-GmPOOQ-jiRDEC261YZ4,24252
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=zBLgUOOlhCzDOGVmmM0wnjpZK-a9jK0EGlVbgiNy6eI,6849
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=5TBxkaxF1xXtLhRkdQK1p4H5DD-mxwlptkQOpX8V9WE,617
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.10.dist-info/METADATA,sha256=VhJNZXLM-ohC2PCGVgCgF4516pmUOpzVOaRtC-Bgt2M,6378
13
- python_script_launcher-0.0.10.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
14
- python_script_launcher-0.0.10.dist-info/entry_points.txt,sha256=13ENRmw4BsgWGLDCr_y1dKB91ed1j6rEyks6JDFzOns,66
15
- python_script_launcher-0.0.10.dist-info/top_level.txt,sha256=Ap6WryIb1YRnr_QVCmfGjdEVLDNA3dQcTl_-gxX11qk,23
16
- python_script_launcher-0.0.10.dist-info/RECORD,,
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,,