fix: handle case where output_dir does not already exist on macos & windows (#1851)

* replace `with suppress(FileNotFoundError)` by `.unlink(missing_ok=True)` for macos

* also use `.unlink(missing_ok=True)` in pyodide and windows for consistency

* remove contextlib imports which are no longer required

* Apply suggestions from code review

* explicity resolve and create output location

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* use explicit str for move

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update comments based on review feedback

* Apply suggestions from code review

* Break out functionality to move files to util.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* raise instance of IsADirectoryError with meaningful message

* Don't need a comment and a exception message

---------

Co-authored-by: Henry Schreiner <HenrySchreinerIII@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
MusicalNinjaDad
2024-06-07 11:37:20 -04:00
committed by GitHub
co-authored by Henry Schreiner pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
parent 877d3bf649
commit 6c6e0f6ba5
4 changed files with 59 additions and 17 deletions
+8 -6
View File
@@ -1,6 +1,5 @@
from __future__ import annotations from __future__ import annotations
import contextlib
import functools import functools
import os import os
import platform import platform
@@ -37,6 +36,7 @@ from .util import (
get_build_verbosity_extra_flags, get_build_verbosity_extra_flags,
get_pip_version, get_pip_version,
install_certifi_script, install_certifi_script,
move_file,
prepare_command, prepare_command,
read_python_configs, read_python_configs,
shell, shell,
@@ -641,11 +641,13 @@ def build(options: Options, tmp_path: Path) -> None:
# we're all done here; move it to output (overwrite existing) # we're all done here; move it to output (overwrite existing)
if compatible_wheel is None: if compatible_wheel is None:
with contextlib.suppress(FileNotFoundError): output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
(build_options.output_dir / repaired_wheel.name).unlink() moved_wheel = move_file(repaired_wheel, output_wheel)
if moved_wheel != output_wheel.resolve():
shutil.move(str(repaired_wheel), build_options.output_dir) log.warning(
built_wheels.append(build_options.output_dir / repaired_wheel.name) "{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
)
built_wheels.append(output_wheel)
# clean up # clean up
shutil.rmtree(identifier_tmp_dir) shutil.rmtree(identifier_tmp_dir)
+8 -5
View File
@@ -1,6 +1,5 @@
from __future__ import annotations from __future__ import annotations
import contextlib
import os import os
import shutil import shutil
import sys import sys
@@ -27,6 +26,7 @@ from .util import (
extract_zip, extract_zip,
find_compatible_wheel, find_compatible_wheel,
get_pip_version, get_pip_version,
move_file,
prepare_command, prepare_command,
read_python_configs, read_python_configs,
shell, shell,
@@ -395,10 +395,13 @@ def build(options: Options, tmp_path: Path) -> None:
# we're all done here; move it to output (overwrite existing) # we're all done here; move it to output (overwrite existing)
if compatible_wheel is None: if compatible_wheel is None:
with contextlib.suppress(FileNotFoundError): output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
(build_options.output_dir / repaired_wheel.name).unlink() moved_wheel = move_file(repaired_wheel, output_wheel)
if moved_wheel != output_wheel.resolve():
log.warning(
"{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
)
built_wheels.append(output_wheel)
shutil.move(str(repaired_wheel), build_options.output_dir)
built_wheels.append(build_options.output_dir / repaired_wheel.name)
finally: finally:
pass pass
+35
View File
@@ -6,6 +6,7 @@ import itertools
import os import os
import re import re
import shlex import shlex
import shutil
import ssl import ssl
import subprocess import subprocess
import sys import sys
@@ -359,6 +360,40 @@ def extract_tar(tar_src: Path, dest: Path) -> None:
tar_.extractall(dest) tar_.extractall(dest)
def move_file(src_file: Path, dst_file: Path) -> Path:
"""Moves a file safely while avoiding potential semantic confusion:
1. `dst_file` must point to the target filename, not a directory
2. `dst_file` will be overwritten if it already exists
3. any missing parent directories will be created
Returns the fully resolved Path of the resulting file.
Raises:
NotADirectoryError: If any part of the intermediate path to `dst_file` is an existing file
IsADirectoryError: If `dst_file` points directly to an existing directory
"""
# Importing here as logger needs various functions from util -> circular imports
from .logger import log
src_file = src_file.resolve()
dst_file = dst_file.resolve()
if dst_file.is_dir():
msg = "dst_file must be a valid target filename, not an existing directory."
raise IsADirectoryError(msg)
dst_file.unlink(missing_ok=True)
dst_file.parent.mkdir(parents=True, exist_ok=True)
# using shutil.move() as Path.rename() is not guaranteed to work across filesystem boundaries
# explicit str() needed for Python 3.8
resulting_file = shutil.move(str(src_file), str(dst_file))
resulting_file = Path(resulting_file).resolve()
log.notice(f"Moved {src_file} to {resulting_file}")
return Path(resulting_file)
class DependencyConstraints: class DependencyConstraints:
def __init__(self, base_file_path: Path): def __init__(self, base_file_path: Path):
assert base_file_path.exists() assert base_file_path.exists()
+8 -6
View File
@@ -7,7 +7,6 @@ import subprocess
import sys import sys
import textwrap import textwrap
from collections.abc import MutableMapping, Sequence, Set from collections.abc import MutableMapping, Sequence, Set
from contextlib import suppress
from dataclasses import dataclass from dataclasses import dataclass
from functools import lru_cache from functools import lru_cache
from pathlib import Path from pathlib import Path
@@ -34,6 +33,7 @@ from .util import (
find_compatible_wheel, find_compatible_wheel,
get_build_verbosity_extra_flags, get_build_verbosity_extra_flags,
get_pip_version, get_pip_version,
move_file,
prepare_command, prepare_command,
read_python_configs, read_python_configs,
shell, shell,
@@ -543,11 +543,13 @@ def build(options: Options, tmp_path: Path) -> None:
# we're all done here; move it to output (remove if already exists) # we're all done here; move it to output (remove if already exists)
if compatible_wheel is None: if compatible_wheel is None:
with suppress(FileNotFoundError): output_wheel = build_options.output_dir.joinpath(repaired_wheel.name)
(build_options.output_dir / repaired_wheel.name).unlink() moved_wheel = move_file(repaired_wheel, output_wheel)
if moved_wheel != output_wheel.resolve():
shutil.move(str(repaired_wheel), build_options.output_dir) log.warning(
built_wheels.append(build_options.output_dir / repaired_wheel.name) "{repaired_wheel} was moved to {moved_wheel} instead of {output_wheel}"
)
built_wheels.append(output_wheel)
# clean up # clean up
# (we ignore errors because occasionally Windows fails to unlink a file and we # (we ignore errors because occasionally Windows fails to unlink a file and we