diff --git a/conda_build/cli/main_build.py b/conda_build/cli/main_build.py index ae54e24ca8..3d9e43a5fb 100644 --- a/conda_build/cli/main_build.py +++ b/conda_build/cli/main_build.py @@ -24,6 +24,7 @@ get_or_merge_config, zstd_compression_level_default, ) +from ..deprecations import deprecated from ..utils import LoggingContext from .actions import KeyValueAction, PackageTypeNormalize from .main_render import get_render_parser @@ -487,14 +488,19 @@ def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]: "Do not display value of environment variables specified in build.script_env." ), ) + # TODO: Remove in 25.1 + default_pkg_format = context.conda_build.get("pkg_format") + if default_pkg_format is None: + warn_about_default_pkg_format = True + default_pkg_format = conda_pkg_format_default + else: + warn_about_default_pkg_format = False parser.add_argument( "--package-format", dest="conda_pkg_format", choices=CondaPkgFormat.acceptable(), action=PackageTypeNormalize, - default=CondaPkgFormat.normalize( - context.conda_build.get("pkg_format", conda_pkg_format_default) - ), + default=CondaPkgFormat.normalize(default_pkg_format), help=( "Choose which package type(s) are outputted. (Accepted inputs .tar.bz2 or 1, .conda or 2)" ), @@ -503,6 +509,27 @@ def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]: parsed = parser.parse_args(args) check_recipe(parsed.recipe) + + # TODO: Remove in 25.1 + if ( + all(not arg.startswith("--package-format") for arg in args) + and warn_about_default_pkg_format + and "purge" not in parsed.recipe + and "purge-all" not in parsed.recipe + ): + deprecated.topic( + "24.11", + "25.1", + topic="The default `pkg_format` of '.tar.bz2'", + addendum=( + "\n\n" + "The new default `pkg_format` value will be '.conda'. " + "If you want to keep using `.tar.bz2`, consider:\n" + "- Setting `conda_build.pkg_format: 'tar.bz2' in your condarc file.\n" + "- Using `--pkg-format=tar.bz2` in the CLI.\n" + ), + deprecation_type=FutureWarning, + ) return parser, parsed diff --git a/news/4890-package-format-cli-options b/news/4890-package-format-cli-options index 3d9989f73f..3bfff08744 100644 --- a/news/4890-package-format-cli-options +++ b/news/4890-package-format-cli-options @@ -1,6 +1,22 @@ ### Enhancements * `--package-format` introduced as command line argument. (#4890 via #5209) - * This takes precedence over default value and .condarc - * Normalization occurs so 1,tar.bz2,.tar.bz2,2,conda,.conda are all recognized and mapped appropriately - * Other options are rejected + * This takes precedence over default value and `condarc`. + * Normalization occurs so `1`, `"1"`, `tar.bz2`,`.tar.bz2`, `2`, `"2"`, `conda`, `.conda` are all recognized and mapped appropriately. + * Other options are rejected. + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +* diff --git a/news/5534-warn-default-conda-pkg-format b/news/5534-warn-default-conda-pkg-format new file mode 100644 index 0000000000..e60dbefc3d --- /dev/null +++ b/news/5534-warn-default-conda-pkg-format @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* + +### Deprecations + +* The default value for `--package-format` and `conda_pkg_format` will become `.conda` in 25.1. (#5534) + +### Docs + +* + +### Other + +* diff --git a/tests/cli/test_main_build.py b/tests/cli/test_main_build.py index d78f87b749..8388eb020a 100644 --- a/tests/cli/test_main_build.py +++ b/tests/cli/test_main_build.py @@ -29,6 +29,9 @@ from conda_build.metadata import MetaData +# FUTURE: Remove after 25.1 +DEFAULT_PACKAGE_FORMAT_FLAG = "--package-format=1" + @pytest.mark.sanity def test_build_empty_sections(conda_build_test_recipe_envvar: str): @@ -37,6 +40,7 @@ def test_build_empty_sections(conda_build_test_recipe_envvar: str): os.path.join(metadata_dir, "empty_sections"), "--no-activate", "--no-anaconda-upload", + DEFAULT_PACKAGE_FORMAT_FLAG, ] main_build.execute(args) @@ -52,6 +56,7 @@ def test_build_add_channel(): "conda_build_test", "--no-activate", "--no-anaconda-upload", + DEFAULT_PACKAGE_FORMAT_FLAG, os.path.join(metadata_dir, "_recipe_requiring_external_channel"), ] main_build.execute(args) @@ -64,6 +69,7 @@ def test_build_without_channel_fails(testing_workdir): "--no-anaconda-upload", "--no-activate", os.path.join(metadata_dir, "_recipe_requiring_external_channel"), + DEFAULT_PACKAGE_FORMAT_FLAG, ] with pytest.raises(DependencyNeedsBuildingError): main_build.execute(args) @@ -81,6 +87,7 @@ def test_no_filename_hash(testing_workdir, testing_metadata, capfd): "--no-activate", testing_workdir, "--old-build-string", + DEFAULT_PACKAGE_FORMAT_FLAG, ] main_build.execute(args) output, error = capfd.readouterr() @@ -100,7 +107,7 @@ def test_build_output_build_path( api.output_yaml(testing_metadata, "meta.yaml") testing_config.verbose = False testing_config.debug = False - args = ["--output", testing_workdir] + args = ["--output", testing_workdir, DEFAULT_PACKAGE_FORMAT_FLAG] main_build.execute(args) test_path = os.path.join( testing_config.croot, @@ -118,7 +125,7 @@ def test_build_output_build_path_multiple_recipes( api.output_yaml(testing_metadata, "meta.yaml") testing_config.verbose = False skip_recipe = os.path.join(metadata_dir, "build_skip") - args = ["--output", testing_workdir, skip_recipe] + args = ["--output", testing_workdir, skip_recipe, DEFAULT_PACKAGE_FORMAT_FLAG] main_build.execute(args) @@ -142,6 +149,7 @@ def test_slash_in_recipe_arg_keeps_build_id( "--croot", testing_config.croot, "--no-anaconda-upload", + DEFAULT_PACKAGE_FORMAT_FLAG, ] main_build.execute(args) @@ -161,7 +169,7 @@ def test_slash_in_recipe_arg_keeps_build_id( @pytest.mark.skipif(on_win, reason="prefix is always short on win.") def test_build_long_test_prefix_default_enabled(mocker, testing_workdir): recipe_path = os.path.join(metadata_dir, "_test_long_test_prefix") - args = [recipe_path, "--no-anaconda-upload"] + args = [recipe_path, "--no-anaconda-upload", DEFAULT_PACKAGE_FORMAT_FLAG] main_build.execute(args) args.append("--no-long-test-prefix") @@ -177,6 +185,7 @@ def test_build_no_build_id(testing_workdir: str, testing_config: Config): testing_config.croot, "--no-activate", "--no-anaconda-upload", + DEFAULT_PACKAGE_FORMAT_FLAG, ] main_build.execute(args) @@ -205,7 +214,7 @@ def test_build_multiple_recipes(testing_metadata, testing_workdir, testing_confi api.output_yaml(testing_metadata, "recipe2/meta.yaml") with open("recipe2/run_test.py", "w") as f: f.write("import os; assert 'package2' in os.getenv('PREFIX')") - args = ["--no-anaconda-upload", "recipe1", "recipe2"] + args = ["--no-anaconda-upload", "recipe1", "recipe2", DEFAULT_PACKAGE_FORMAT_FLAG] main_build.execute(args) @@ -224,6 +233,7 @@ def test_build_output_folder(testing_workdir: str, testing_metadata: MetaData): "--no-anaconda-upload", "--output-folder", str(out), + DEFAULT_PACKAGE_FORMAT_FLAG, ] main_build.execute(args) @@ -241,6 +251,7 @@ def test_build_source(testing_workdir: str): testing_workdir, "--no-activate", "--no-anaconda-upload", + DEFAULT_PACKAGE_FORMAT_FLAG, ] main_build.execute(args) assert Path(testing_workdir, "work", "setup.py").is_file() @@ -309,12 +320,14 @@ def test_no_force_upload( ) # check for normal upload - main_build.execute(["--no-force-upload", testing_workdir]) + main_build.execute( + ["--no-force-upload", testing_workdir, DEFAULT_PACKAGE_FORMAT_FLAG] + ) call.assert_called_once_with([anaconda, "upload", *pkg]) call.reset_mock() # check for force upload - main_build.execute([testing_workdir]) + main_build.execute([testing_workdir, DEFAULT_PACKAGE_FORMAT_FLAG]) call.assert_called_once_with([anaconda, "upload", "--force", *pkg]) @@ -335,7 +348,7 @@ def test_build_skip_existing( ): # build the recipe first empty_sections = os.path.join(metadata_dir, "empty_sections") - args = ["--no-anaconda-upload", empty_sections] + args = ["--no-anaconda-upload", empty_sections, DEFAULT_PACKAGE_FORMAT_FLAG] main_build.execute(args) args.insert(0, "--skip-existing") import conda_build.source @@ -354,7 +367,13 @@ def test_build_skip_existing_croot( ): # build the recipe first empty_sections = os.path.join(metadata_dir, "empty_sections") - args = ["--no-anaconda-upload", "--croot", testing_workdir, empty_sections] + args = [ + "--no-anaconda-upload", + "--croot", + testing_workdir, + empty_sections, + DEFAULT_PACKAGE_FORMAT_FLAG, + ] main_build.execute(args) args.insert(0, "--skip-existing") main_build.execute(args) @@ -367,13 +386,19 @@ def test_package_test(testing_workdir, testing_metadata): """Test calling conda build -t - rather than """ api.output_yaml(testing_metadata, "recipe/meta.yaml") output = api.build(testing_workdir, config=testing_metadata.config, notest=True)[0] - args = ["-t", output] + args = ["-t", output, DEFAULT_PACKAGE_FORMAT_FLAG] main_build.execute(args) def test_activate_scripts_not_included(testing_workdir): recipe = os.path.join(metadata_dir, "_activate_scripts_not_included") - args = ["--no-anaconda-upload", "--croot", testing_workdir, recipe] + args = [ + "--no-anaconda-upload", + "--croot", + testing_workdir, + recipe, + DEFAULT_PACKAGE_FORMAT_FLAG, + ] main_build.execute(args) out = api.get_output_file_paths(recipe, croot=testing_workdir)[0] for f in ( @@ -401,7 +426,12 @@ def test_relative_path_croot( empty_sections = Path(metadata_dir, "empty_with_build_script") croot = Path(".", "relative", "path") - args = ["--no-anaconda-upload", f"--croot={croot}", str(empty_sections)] + args = [ + "--no-anaconda-upload", + f"--croot={croot}", + str(empty_sections), + DEFAULT_PACKAGE_FORMAT_FLAG, + ] main_build.execute(args) assert len(list(croot.glob("**/*.tar.bz2"))) == 1 @@ -425,6 +455,7 @@ def test_relative_path_test_artifact( "--no-test", f"--croot={croot_abs}", str(empty_sections), + DEFAULT_PACKAGE_FORMAT_FLAG, ] main_build.execute(args) @@ -439,6 +470,7 @@ def test_relative_path_test_artifact( testing_config.subdir, "empty_with_build_script-0.0-0.tar.bz2", ), + DEFAULT_PACKAGE_FORMAT_FLAG, ] main_build.execute(args) @@ -449,13 +481,13 @@ def test_test_extra_dep(testing_metadata): output = api.build(testing_metadata, notest=True, anaconda_upload=False)[0] # tests version constraints. CLI would quote this - "click <6.7" - args = [output, "-t", "--extra-deps", "imagesize <1.0"] + args = [output, "-t", "--extra-deps", "imagesize <1.0", DEFAULT_PACKAGE_FORMAT_FLAG] # extra_deps will add it in main_build.execute(args) # missing click dep will fail tests with pytest.raises(CondaBuildUserError): - args = [output, "-t"] + args = [output, "-t", DEFAULT_PACKAGE_FORMAT_FLAG] # extra_deps will add it in main_build.execute(args) @@ -465,7 +497,7 @@ def test_test_extra_dep(testing_metadata): [([], True), (["--long-test-prefix"], True), (["--no-long-test-prefix"], False)], ) def test_long_test_prefix(additional_args, is_long_test_prefix): - args = ["non_existing_recipe"] + additional_args + args = ["non_existing_recipe", DEFAULT_PACKAGE_FORMAT_FLAG] + additional_args parser, args = main_build.parse_args(args) config = Config(**args.__dict__) assert config.long_test_prefix is is_long_test_prefix @@ -494,7 +526,7 @@ def test_zstd_compression_level( ) request.addfinalizer(_reset_config) _reset_config([os.path.join(testing_workdir, ".condarc")]) - args = ["non_existing_recipe"] + args = ["non_existing_recipe", DEFAULT_PACKAGE_FORMAT_FLAG] if zstd_level_cli: args.append(f"--zstd-compression-level={zstd_level_cli}") parser, args = main_build.parse_args(args) @@ -512,14 +544,14 @@ def test_user_warning(tmpdir, recwarn): recipe = dir_recipe_path.join("meta.yaml") recipe.write("") - main_build.parse_args([str(recipe)]) + main_build.parse_args([str(recipe), DEFAULT_PACKAGE_FORMAT_FLAG]) assert ( f"RECIPE_PATH received is a file ({recipe}).\n" "It should be a path to a folder.\n" "Forcing conda-build to use the recipe file." ) == str(recwarn.pop(UserWarning).message) - main_build.parse_args([str(dir_recipe_path)]) + main_build.parse_args([str(dir_recipe_path), DEFAULT_PACKAGE_FORMAT_FLAG]) assert not recwarn.list @@ -530,5 +562,6 @@ def test_build_with_empty_channel_fails(empty_channel: Path) -> None: "--override-channels", f"--channel={empty_channel}", os.path.join(metadata_dir, "_recipe_requiring_external_channel"), + DEFAULT_PACKAGE_FORMAT_FLAG, ] ) diff --git a/tests/cli/test_main_skeleton.py b/tests/cli/test_main_skeleton.py index c2dd0a65b5..51aa78a014 100644 --- a/tests/cli/test_main_skeleton.py +++ b/tests/cli/test_main_skeleton.py @@ -7,6 +7,9 @@ from conda_build import api from conda_build.cli import main_build, main_skeleton +# FUTURE: Remove after 25.1 +DEFAULT_PACKAGE_FORMAT_FLAG = "--package-format=1" + @pytest.mark.sanity def test_skeleton_pypi(testing_workdir, testing_config): @@ -15,7 +18,7 @@ def test_skeleton_pypi(testing_workdir, testing_config): assert os.path.isdir("peppercorn") # ensure that recipe generated is buildable - main_build.execute(("peppercorn",)) + main_build.execute(("peppercorn", DEFAULT_PACKAGE_FORMAT_FLAG)) @pytest.mark.sanity