pyship 0.3.2__py3-none-any.whl → 0.4.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.
pyship/__init__.py CHANGED
@@ -7,7 +7,7 @@ CLIP_EXT = "clip" # zipped clip file extension
7
7
  DEFAULT_DIST_DIR_NAME = "dist"
8
8
 
9
9
  from .pyship_path import NullPath
10
- from ._version_ import __version__, __author__, __application_name__, __title__, __description__, __url__, __author_email__, __download_url__
10
+ from .__version__ import __version__, __author__, __application_name__, __title__, __description__, __url__, __author_email__, __download_url__
11
11
  from .logging import PyshipLog, log_process_output
12
12
  from .pyship_exceptions import PyshipException, PyshipNoProductDirectory, PyshipCouldNotGetVersion, PyshipLicenseFileDoesNotExist, PyshipInsufficientAppInfo, PyshipNoAppName
13
13
  from .pyship_exceptions import PyshipNoTargetAppInfo
pyship/app_info.py CHANGED
@@ -80,6 +80,15 @@ def get_app_info_wheel(app_info: AppInfo, dist_path: Path) -> AppInfo:
80
80
  app_info.name = metadata.get("name")
81
81
  app_info.version = VersionInfo.parse(metadata.get("version"))
82
82
  app_info.author = metadata.get("author")
83
+ if app_info.author is None:
84
+ # PEP 621 authors array produces "author_email" like "Name <email>" in metadata 2.4+
85
+ author_email = metadata.get("author_email")
86
+ if author_email is not None:
87
+ # extract name from "Name <email>" format
88
+ if "<" in author_email:
89
+ app_info.author = author_email.split("<")[0].strip()
90
+ else:
91
+ app_info.author = author_email
83
92
  app_info.description = metadata.get("summary", "") # called description in setup.py
84
93
  return app_info
85
94
 
pyship/create_launcher.py CHANGED
@@ -64,7 +64,7 @@ def create_pyship_launcher(target_app_info: AppInfo, app_path_output: Path):
64
64
  )
65
65
 
66
66
  # 2. Copy the standalone launcher script alongside the stub
67
- standalone_source = Path(launcher_module_dir, "launcher_standalone.py")
67
+ standalone_source = Path(launcher_module_dir, "launcher.py")
68
68
  standalone_dest = Path(launcher_dir, f"{target_app_info.name}_launcher.py")
69
69
  shutil.copy2(str(standalone_source), str(standalone_dest))
70
70
  log.info(f"copied launcher script to {standalone_dest}")
@@ -3,7 +3,7 @@ pyship launcher
3
3
  """
4
4
 
5
5
  from .restart_monitor import RestartMonitor
6
- from .launcher_standalone import launch
6
+ from .launcher import launch
7
7
  from .hash import get_file_sha256
8
8
  from .metadata import calculate_metadata, load_metadata, store_metadata
9
9
 
@@ -241,6 +241,20 @@ def launch(app_dir=None, additional_path=None):
241
241
  std_out = target_process.stdout
242
242
  std_err = target_process.stderr
243
243
 
244
+ # When pythonw.exe fails silently (no stderr), re-run with python.exe to capture the actual error
245
+ if is_gui and return_code not in (OK_RETURN_CODE, RESTART_RETURN_CODE) and not (std_err and std_err.strip()):
246
+ log.warning(f"pythonw.exe exited with return_code={return_code} but produced no error output, re-running with python.exe for diagnostics")
247
+ diag_python = Path(versions[latest_version], "Scripts", "python.exe")
248
+ if diag_python.exists():
249
+ diag_cmd = [str(diag_python), "-X", "faulthandler"] + cmd[1:]
250
+ log.info(f"diagnostic cmd={diag_cmd}")
251
+ try:
252
+ diag_process = subprocess.run(diag_cmd, cwd=str(python_exe_path.parent), capture_output=True, text=True)
253
+ std_out = diag_process.stdout
254
+ std_err = diag_process.stderr
255
+ except Exception as diag_e:
256
+ log.error(f"diagnostic re-run failed: {diag_e}")
257
+
244
258
  if (std_err and std_err.strip()) or (return_code != OK_RETURN_CODE and return_code != RESTART_RETURN_CODE):
245
259
  if std_out and std_out.strip():
246
260
  log.warning(std_out)
pyship/launcher_stub.py CHANGED
@@ -117,7 +117,11 @@ class Program
117
117
  """
118
118
 
119
119
  CLI_ERROR_HANDLER = "Console.Error.WriteLine(msg);"
120
- GUI_ERROR_HANDLER = "System.Windows.Forms.MessageBox.Show(msg, appName, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);"
120
+ GUI_ERROR_HANDLER = (
121
+ 'string logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), appName, "log", appName + "_launcher.log");'
122
+ ' msg += "\\n\\nLog file: " + logPath;'
123
+ " System.Windows.Forms.MessageBox.Show(msg, appName, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);"
124
+ )
121
125
 
122
126
 
123
127
  @typechecked
pyship/main.py CHANGED
@@ -4,7 +4,7 @@ from pyship import PyShip, __application_name__, __author__, PyshipLog, get_argu
4
4
  def main():
5
5
  args = get_arguments()
6
6
 
7
- pyship_log = PyshipLog(__application_name__, __author__)
7
+ pyship_log = PyshipLog(__application_name__, __author__) # type: ignore[unknown-argument]
8
8
  pyship_log.init_logger_from_args(args)
9
9
 
10
10
  pyship_print(f"log_path={pyship_log.log_path}")
pyship/nsis.py CHANGED
@@ -225,7 +225,7 @@ def run_nsis(target_app_info: AppInfo, target_app_version: VersionInfo, app_dir:
225
225
  raise FileNotFoundError(make_nsis_path)
226
226
 
227
227
  else:
228
- log.error(f"{license_file_name} file does not exist at {target_app_info.project_dir}")
228
+ log.error(f'{license_file_name} file does not exist at "{target_app_info.project_dir}" ("{target_app_info.project_dir.resolve()}")')
229
229
  raise PyshipLicenseFileDoesNotExist(target_app_info.project_dir)
230
230
 
231
231
  # return the actual path to the installer (we cd'd to target_app_project_dir when we ran nsis)
pyship/pyship.py CHANGED
@@ -33,6 +33,7 @@ class PyShip:
33
33
  cloud_id: Union[str, None] = None # e.g. AWS Access Key ID
34
34
  cloud_secret: Union[str, None] = None # e.g. AWS Secret Access Key
35
35
  cloud_access: Union[PyShipCloud, None] = None # making this accessible outside this class aids in testing, especially when mocking
36
+ name: Union[str, None] = None # optional target application name (overrides pyproject.toml)
36
37
  upload: bool = True # set to False in order to tell pyship to not attempt to perform file upload to the cloud (e.g. installer, clip files to AWS S3)
37
38
 
38
39
  @typechecked
pyship/uv_util.py CHANGED
@@ -36,7 +36,7 @@ def find_or_bootstrap_uv(cache_dir: Path) -> Path:
36
36
  return uv_exe
37
37
 
38
38
  # download uv standalone binary
39
- pyship_print("downloading uv...")
39
+ pyship_print("downloading uv")
40
40
  uv_cache_dir.mkdir(parents=True, exist_ok=True)
41
41
 
42
42
  machine = platform.machine().lower()
@@ -82,7 +82,7 @@ def uv_python_install(uv_path: Path, python_version: str) -> Path:
82
82
  :param python_version: Python version string (e.g. "3.12.4")
83
83
  :return: path to the installed Python interpreter
84
84
  """
85
- pyship_print(f"installing Python {python_version} via uv...")
85
+ pyship_print(f"installing Python {python_version} via uv")
86
86
  subprocess.run([str(uv_path), "python", "install", python_version], check=True, capture_output=True, text=True)
87
87
 
88
88
  result = subprocess.run([str(uv_path), "python", "find", python_version], check=True, capture_output=True, text=True)
@@ -99,7 +99,7 @@ def uv_venv_create(uv_path: Path, venv_dir: Path, python_version: str) -> None:
99
99
  :param venv_dir: destination directory for the venv
100
100
  :param python_version: Python version string
101
101
  """
102
- pyship_print(f"creating relocatable venv at {venv_dir}...")
102
+ pyship_print(f'creating relocatable venv at "{venv_dir}"')
103
103
  cmd = [str(uv_path), "venv", "--relocatable", "--python", python_version, str(venv_dir)]
104
104
  log.info(f"uv venv cmd: {cmd}")
105
105
  subprocess.run(cmd, check=True, capture_output=True, text=True)
@@ -142,7 +142,7 @@ def uv_build(uv_path: Path, project_dir: Path, output_dir: Path) -> Path:
142
142
  :param output_dir: directory for the built wheel
143
143
  :return: path to the built wheel
144
144
  """
145
- pyship_print(f"building wheel via uv in {project_dir}...")
145
+ pyship_print(f'building wheel via uv in "{project_dir}"')
146
146
  output_dir.mkdir(parents=True, exist_ok=True)
147
147
  cmd = [str(uv_path), "build", "--wheel", "--out-dir", str(output_dir)]
148
148
  log.info(f"uv build cmd: {cmd}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyship
3
- Version: 0.3.2
3
+ Version: 0.4.1
4
4
  Summary: freezer, installer and updater for Python applications
5
5
  Author-email: abel <j@abel.co>
6
6
  License: MIT
@@ -1,34 +1,34 @@
1
- pyship/__init__.py,sha256=5lL2td91Xm31z8x4XcTZW591rO3hvFzXpAtTiKdt064,1438
1
+ pyship/__init__.py,sha256=2E6cOA1EpFk_bWkNb_6W-4PfG7KcypmvpLB1i4syfzU,1440
2
2
  pyship/__main__.py,sha256=KtAdk7BCWhXbhywlFB8P4HbvypVkqJXbxgMW3XVD-To,84
3
- pyship/_version_.py,sha256=y-oqerPvrMS7jU7JSdHdUyKkSxJWA0anyoCQCfQ3INQ,502
4
- pyship/app_info.py,sha256=kxWMOhksgPijODj76LUkHuxKzPeHNWMN2Dv4D9PfAp4,6383
3
+ pyship/__version__.py,sha256=y-oqerPvrMS7jU7JSdHdUyKkSxJWA0anyoCQCfQ3INQ,502
4
+ pyship/app_info.py,sha256=UTWD4ol226sNAAYsiByMJswIIL1lN8nx96PpD43YQG4,6866
5
5
  pyship/arguments.py,sha256=p4cA8FwbNSZqg4XI6LsYit1QN7TpAWWZH2xrgLf-f-g,1882
6
6
  pyship/clip.py,sha256=N6es31zy0l3kakf1Enxt2fqkcMQoaUSXMlcCqhHJdWI,3700
7
7
  pyship/cloud.py,sha256=k8m-254Akpp3NALeP0pJzFs21_2qZmmK0GHfSC-Z0sw,1282
8
8
  pyship/constants.py,sha256=Y-yA_MMwL572UbZqmhILrJL6NH9xK3q5jELJR6atw7I,243
9
- pyship/create_launcher.py,sha256=HxfOFC9zJf5yhTm9XtCaR49Jn9-SNkpzWgHSLyhnu6k,3794
9
+ pyship/create_launcher.py,sha256=2wyEmB7Xe-CwpTi8qfpSE6ki0iO6hw1HkVq0GrM0CYs,3783
10
10
  pyship/download.py,sha256=QHbdVwxLNBToMEC0tOF2ZquDpoydRu0-cj7LOERr5Q4,2460
11
- pyship/launcher_stub.py,sha256=iezFuelAps8_tmjMZdb7G6MkccWp9a2wFqtIxgLRICk,6677
11
+ pyship/launcher_stub.py,sha256=3yC1EKk45m4GOJ_uat1OWKUFqgAWdcpfQZKh7qhflnk,6889
12
12
  pyship/logging.py,sha256=cIB7487tN8TsKeDHwwzjA30rHRW5Ci5GA5uPbaEXaY0,966
13
- pyship/main.py,sha256=pALpy8ooWsKkUpWUtGWO567I9u1fbIFWdBU0dnWm2K4,866
14
- pyship/nsis.py,sha256=xPW1f10CEKfDgvu4jd7e-tJqUfgm3AbAPC1E5rmLzO4,13066
13
+ pyship/main.py,sha256=kRy4DZ1Z4jXztKuvV8589SqxMNqTH_Sn57qdLCQYzqI,900
14
+ pyship/nsis.py,sha256=QTsAWDcdxxLNG0f3gZ5lli4QlWJMLBFhy9GBJD5OkVA,13112
15
15
  pyship/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  pyship/pyship.ico,sha256=9oiMtmA7NiBmxM0NmMJQU5jXmnbEWCdqTaO3XvJ7ozY,103500
17
- pyship/pyship.py,sha256=3kmYP_UNF4oexNlvLqTrVqE3s0-5rAgHsETaQp69LV0,5682
17
+ pyship/pyship.py,sha256=eYmEBmdPO-IXzLUnVBcOviTZOp3etYvZdVUxlJqLZAg,5780
18
18
  pyship/pyship_custom_print.py,sha256=FrVy3XCZg8sex4a-aKtghqz1BlfO2DGgcpsdviQcssw,628
19
19
  pyship/pyship_exceptions.py,sha256=teUOh3FuRUU9hyv7L4owACQZPWNdwpYTEz6l-Ao35nw,959
20
20
  pyship/pyship_get_icon.py,sha256=BQp_xYl8CXt2e7nDB0llSxr0hbP7to2fkQqPeu8WpIg,2050
21
21
  pyship/pyship_path.py,sha256=RpU2n7pWXTr0501S7U8KxWCpBc8jOqKPuaX0X6IWPI8,262
22
22
  pyship/pyship_subprocess.py,sha256=hH4MW_j2zLJLDmLDkkl2JWupXrvSemAeCPAEtPdSGyk,3212
23
- pyship/uv_util.py,sha256=dcPtE0XYeQ3KFLMGA0qr_FhuTi1hprlWddFBa5hssNM,5662
24
- pyship/launcher/__init__.py,sha256=OujoBeud36d3e8V6QzCWe5A-ilkaMeC5AmnoE72u62U,256
23
+ pyship/uv_util.py,sha256=vGvaR9Uj7MGAhc5mS1q8LpoM9ctetQwkYQCZS7rwyWM,5654
24
+ pyship/launcher/__init__.py,sha256=25vVghziYIHDBA0mCaGqhj6hWxxjQqlm4LrY0Hem0YY,245
25
25
  pyship/launcher/__main__.py,sha256=KhFSf1RU5Cj15y0c4FlzvIvDpWWi9NKYy0gzJ-w7KQE,115
26
26
  pyship/launcher/hash.py,sha256=THEh8G-StEJeh5tH24RnAQpYB0Zv1nnBwqOoStN6_NY,1153
27
- pyship/launcher/launcher_standalone.py,sha256=1HIXieq17IQFuldBAkmo7tjpsjADQVw7R8zXXQpEMXs,10654
27
+ pyship/launcher/launcher.py,sha256=5OgNVqhJdDAcd6zS6MXg4GgU4LiVR9xrqWfB8NvZbfk,11811
28
28
  pyship/launcher/metadata.py,sha256=PnOkYLQf6NYvPiZWhf0LAUjmFWzURKWriRQJSy3T-x4,1969
29
29
  pyship/launcher/restart_monitor.py,sha256=jfzchLxGuC_xUNdPAr849FhAdozJ6BX9M-gqLd-gGIA,952
30
- pyship-0.3.2.dist-info/licenses/LICENSE,sha256=wJpPh6rC_YhbTZ7ZIy1VjrqeLdJ8Pz0ppx2MUi7s8Dg,1088
31
- pyship-0.3.2.dist-info/METADATA,sha256=-nX9TalZF05DmVKUebIA3daad4gc34cwogbNySW2E5Q,2513
32
- pyship-0.3.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
33
- pyship-0.3.2.dist-info/top_level.txt,sha256=mlozeft-UInVAceSajzNvtkn2vPa8El9j0RulsTVtlU,7
34
- pyship-0.3.2.dist-info/RECORD,,
30
+ pyship-0.4.1.dist-info/licenses/LICENSE,sha256=vfTkV97IHvr2g_I1pGQg2bRYT05ktYYgaPwBDKNRv98,1093
31
+ pyship-0.4.1.dist-info/METADATA,sha256=jAhE0Aw9zAnEYcoiBvmeaLqrk7ivl36N9xOhO3lMIr0,2513
32
+ pyship-0.4.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
33
+ pyship-0.4.1.dist-info/top_level.txt,sha256=mlozeft-UInVAceSajzNvtkn2vPa8El9j0RulsTVtlU,7
34
+ pyship-0.4.1.dist-info/RECORD,,
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020 James Abel
3
+ Copyright (c) 2020-2026 James Abel
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
File without changes
File without changes