Skip to content

Commit 104556e

Browse files
committed
Misc improvements (conda#1191)
1 parent be74b27 commit 104556e

2 files changed

Lines changed: 48 additions & 46 deletions

File tree

constructor/briefcase.py

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ def remove(self, *, ignore_errors: bool = True) -> None:
360360
# delattr on a cached_property may raise on some versions / edge cases
361361
pass
362362

363-
def prepare(self) -> tuple:
363+
def prepare(self) -> None:
364364
"""Prepares the payload.
365365
366366
Directory structure created during preparation:
@@ -369,9 +369,10 @@ def prepare(self) -> tuple:
369369
└── <EXTERNAL_PACKAGE_PATH>/ (external_dir: contains the payload archive and conda exe)
370370
└── base/ (base_dir: represents the base conda environment)
371371
└── pkgs/ (pkgs_dir: staging area for conda package distributions)
372+
373+
Note: base_dir and pkgs_dir are removed after archiving.
372374
"""
373-
root = self.root
374-
external_dir = root / EXTERNAL_PACKAGE_PATH
375+
external_dir = self.root / EXTERNAL_PACKAGE_PATH
375376
external_dir.mkdir(parents=True, exist_ok=True)
376377

377378
# Note that the directory name "base" is also explicitly defined in `run_installation.bat`
@@ -380,9 +381,9 @@ def prepare(self) -> tuple:
380381

381382
pkgs_dir = base_dir / "pkgs"
382383
pkgs_dir.mkdir()
383-
# Render the template files and add them to the necessary config field
384+
384385
self.render_templates()
385-
self.write_pyproject_toml(root, external_dir)
386+
self.write_pyproject_toml(self.root, external_dir)
386387

387388
preconda.write_files(self.info, base_dir)
388389
preconda.copy_extra_files(self.info.get("extra_files", []), external_dir)
@@ -392,7 +393,6 @@ def prepare(self) -> tuple:
392393
archive_path = self.make_archive(base_dir, external_dir)
393394
if not archive_path.exists():
394395
raise RuntimeError(f"Unexpected error, failed to create archive: {archive_path}")
395-
return (root, external_dir, base_dir, pkgs_dir)
396396

397397
def make_archive(self, src: Path, dst: Path) -> Path:
398398
"""Create an archive of the directory 'src'.
@@ -522,40 +522,42 @@ def _stage_conda(self, external_dir: Path) -> None:
522522

523523
def create(info, verbose=False):
524524
if not IS_WINDOWS:
525-
raise Exception(f"Invalid platform '{sys.platform}'. MSI installers require Windows.")
525+
raise OSError(f"Invalid platform '{sys.platform}'. MSI installers require Windows.")
526526

527527
if not info.get("_conda_exe_supports_logging"):
528-
raise Exception("MSI installers require conda-standalone with logging support.")
529-
530-
# MSI installers always use conda-standalone for uninstallation.
531-
# This ensures proper cleanup of conda init, environments, and shortcuts
532-
# via the `conda constructor uninstall` command.
533-
info["uninstall_with_conda_exe"] = True
534-
535-
payload = Payload(info)
536-
payload.prepare()
528+
raise ValueError("MSI installers require conda-standalone with logging support.")
537529

530+
# Check briefcase exists before doing any work
538531
briefcase = Path(sysconfig.get_path("scripts")) / "briefcase.exe"
539532
if not briefcase.exists():
540533
raise FileNotFoundError(
541534
f"Dependency 'briefcase' does not seem to be installed.\nTried: {briefcase}"
542535
)
543536

544-
logger.info("Building MSI installer")
545-
run(
546-
[briefcase, "package"] + (["-v"] if verbose else []),
547-
cwd=payload.root,
548-
check=True,
549-
)
550-
551-
dist_dir = payload.root / "dist"
552-
msi_paths = list(dist_dir.glob("*.msi"))
553-
if len(msi_paths) != 1:
554-
raise RuntimeError(f"Found {len(msi_paths)} MSI files in {dist_dir}, expected 1.")
537+
# MSI installers always use conda-standalone for uninstallation.
538+
# This ensures proper cleanup of conda init, environments, and shortcuts
539+
# via the `conda constructor uninstall` command.
540+
info["uninstall_with_conda_exe"] = True
555541

556-
outpath = Path(info["_outpath"])
557-
outpath.unlink(missing_ok=True)
558-
shutil.move(msi_paths[0], outpath)
542+
payload = Payload(info)
543+
try:
544+
payload.prepare()
545+
546+
logger.info("Building MSI installer")
547+
run(
548+
[briefcase, "package"] + (["-v"] if verbose else []),
549+
cwd=payload.root,
550+
check=True,
551+
)
559552

560-
if not info.get("_debug"):
561-
payload.remove()
553+
dist_dir = payload.root / "dist"
554+
msi_paths = list(dist_dir.glob("*.msi"))
555+
if len(msi_paths) != 1:
556+
raise RuntimeError(f"Found {len(msi_paths)} MSI files in {dist_dir}, expected 1.")
557+
558+
outpath = Path(info["_outpath"])
559+
outpath.unlink(missing_ok=True)
560+
shutil.move(msi_paths[0], outpath)
561+
finally:
562+
if not info.get("_debug"):
563+
payload.remove()

tests/test_briefcase.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,13 @@ def test_payload_layout():
198198
"""
199199
info = mock_info.copy()
200200
payload = Payload(info)
201-
prepared_payload = payload.prepare()
201+
payload.prepare()
202202

203-
root = prepared_payload[0]
204-
external_dir = root / "external"
205-
# The second item in prepared_payload is the 'external' directory
206-
assert external_dir.is_dir() and external_dir == prepared_payload[1]
203+
external_dir = payload.root / "external"
204+
assert external_dir.is_dir()
207205

208-
base_dir = root / "external" / "base"
209-
pkgs_dir = root / "external" / "base" / "pkgs"
206+
base_dir = payload.root / "external" / "base"
207+
pkgs_dir = payload.root / "external" / "base" / "pkgs"
210208
archive_path = external_dir / payload.archive_name
211209
# Since archiving removes the directory 'base_dir' and its contents
212210
assert not base_dir.exists()
@@ -241,20 +239,21 @@ def test_payload_remove():
241239
"""Test removing the payload."""
242240
info = mock_info.copy()
243241
payload = Payload(info)
244-
prepared_payload = payload.prepare()
242+
payload.prepare()
243+
root = payload.root
245244

246-
assert prepared_payload[0].is_dir()
245+
assert root.is_dir()
247246
payload.remove()
248-
assert not prepared_payload[0].is_dir()
247+
assert not root.is_dir()
249248

250249

251250
@pytest.mark.skipif(sys.platform != "win32", reason="Windows only")
252251
def test_payload_pyproject_toml():
253252
"""Test that the pyproject.toml file is created when the payload is prepared."""
254253
info = mock_info.copy()
255254
payload = Payload(info)
256-
prepared_payload = payload.prepare()
257-
pyproject_toml = prepared_payload[0] / "pyproject.toml"
255+
payload.prepare()
256+
pyproject_toml = payload.root / "pyproject.toml"
258257
assert pyproject_toml.is_file()
259258

260259

@@ -263,8 +262,9 @@ def test_payload_conda_exe():
263262
"""Test that conda-standalone is prepared."""
264263
info = mock_info.copy()
265264
payload = Payload(info)
266-
prepared_payload = payload.prepare()
267-
conda_exe = prepared_payload[1] / "_conda.exe" # The second item is the 'external' directory
265+
payload.prepare()
266+
external_dir = payload.root / "external"
267+
conda_exe = external_dir / "_conda.exe"
268268
assert conda_exe.is_file()
269269

270270

0 commit comments

Comments
 (0)