diff --git a/conda_pack/cli.py b/conda_pack/cli.py index 4851f6b4..97c9b9dd 100644 --- a/conda_pack/cli.py +++ b/conda_pack/cli.py @@ -117,7 +117,8 @@ def build_parser(): help="Re-add excluded files matching this pattern") parser.add_argument("--force", "-f", action="store_true", - help="Overwrite any existing archive at the output path.") + help=("Overwrite any existing archive at the output path, " + "or create the output directory structure if it's missing.")) parser.add_argument("--quiet", "-q", action="store_true", help="Do not report progress") diff --git a/conda_pack/core.py b/conda_pack/core.py index da47ebb1..8e0893c6 100644 --- a/conda_pack/core.py +++ b/conda_pack/core.py @@ -285,6 +285,18 @@ def _parcel_output(self, parcel_root, parcel_name, parcel_version, parcel_distro dest_prefix = os.path.join(parcel_root, arcroot) return dest_prefix, arcroot, triple + def _check_output_location(self, output, force): + if os.path.exists(output) and not force: + raise CondaPackException("File %r already exists" % output) + + # `dirname` returns an empty string if the provided `output` is just a filename. + directory = os.path.dirname(output) or "." + if force: + os.makedirs(directory, exist_ok=True) + + if not os.path.exists(directory): + raise CondaPackException(f"The target output directory {directory} does not exist") + def pack( self, output=None, @@ -340,8 +352,8 @@ def pack( verbose : bool, optional If True, progress is reported to stdout. Default is False. force : bool, optional - Whether to overwrite any existing archive at the output path. - Default is False. + Whether to overwrite any existing archive at the output path if present, or + create the output directory structure if it's missing. Default is False. compress_level : int, optional The compression level to use, from 0 to 9. Higher numbers decrease output file size at the expense of compression time. Ignored for @@ -386,8 +398,7 @@ def pack( # Ensure the prefix is a relative path arcroot = arcroot.strip(os.path.sep) if arcroot else "" - if os.path.exists(output) and not force: - raise CondaPackException("File %r already exists" % output) + self._check_output_location(output, force) if verbose: print(f"Packing environment at {self.prefix!r} to {output!r}") @@ -518,8 +529,8 @@ def pack( verbose : bool, optional If True, progress is reported to stdout. Default is False. force : bool, optional - Whether to overwrite any existing archive at the output path. Default - is False. + Whether to overwrite any existing archive at the output path if present, or + create the output directory structure if it's missing. Default is False. compress_level : int, optional The compression level to use, from 0 to 9. Higher numbers decrease output file size at the expense of compression time. Ignored for diff --git a/conda_pack/tests/test_core.py b/conda_pack/tests/test_core.py index 785d93ce..355185f7 100644 --- a/conda_pack/tests/test_core.py +++ b/conda_pack/tests/test_core.py @@ -102,6 +102,31 @@ def test_ignore_errors_editable_packages(): CondaEnv.from_prefix(py37_editable_path, ignore_editable_packages=True) +def test_errors_when_target_directory_not_exists_and_not_force(tmpdir, py37_env): + + target_directory = os.path.join(tmpdir, "not_a_real_directory/") + assert not os.path.exists(target_directory) + + target_file = os.path.join(target_directory, "env.tar.gz") + + with pytest.raises(CondaPackException) as exc: + py37_env.pack(output=target_file, force=False) + + assert "not_a_real_directory" in str(exc.value) + + +def test_creates_directories_if_missing_and_force(tmpdir, py37_env): + + target_directory = os.path.join(tmpdir, "not_a_real_directory/") + assert not os.path.exists(target_directory) + + target_file = os.path.join(target_directory, "env.tar.gz") + + py37_env.pack(output=target_file, force=True) + + assert os.path.exists(target_directory) + + def test_errors_pip_overwrites(): with pytest.raises(CondaPackException) as exc: CondaEnv.from_prefix(py37_broken_path) diff --git a/news/295-create-output-dir-if-missing-and-force b/news/295-create-output-dir-if-missing-and-force new file mode 100644 index 00000000..95382df8 --- /dev/null +++ b/news/295-create-output-dir-if-missing-and-force @@ -0,0 +1,21 @@ +### Enhancements + +* Creates the output directory if it does not already exist and `force` is used (#295) + +* Raise error before packing if the output directory is missing and `force` is not used (#295) + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +*