diff --git a/src/packse/cli.py b/src/packse/cli.py index 15784846..0783882e 100644 --- a/src/packse/cli.py +++ b/src/packse/cli.py @@ -13,6 +13,7 @@ ServeError, UserError, ) +from packse.fetch import fetch from packse.index import index_down, index_up from packse.inspect import inspect from packse.list import list @@ -41,7 +42,7 @@ def entrypoint(): try: args.call(args) except DestinationAlreadyExists as exc: - print(f"{exc}. Pass `--rm` to allow removal.", file=sys.stderr) + print(f"{exc}. Pass `--force` to allow removal.", file=sys.stderr) exit(1) except UserError as exc: print(f"{exc}.", file=sys.stderr) @@ -73,7 +74,7 @@ def entrypoint(): def _call_build(args): build( args.targets, - rm_destination=args.rm, + rm_destination=args.force, short_names=args.short_names, no_hash=args.no_hash, skip_root=args.skip_root, @@ -88,7 +89,7 @@ def _call_build_package(args): args.no_sdist, args.requires_python, args.wheel_tag, - args.rm, + args.force, ) @@ -137,6 +138,14 @@ def _call_publish(args): ) +def _call_fetch(args): + fetch( + args.dest, + ref=args.ref, + force=args.force, + ) + + def _call_list(args): skip_invalid = args.skip_invalid if not args.targets: @@ -195,9 +204,9 @@ def _add_build_parser(subparsers): help="The scenario to build", ) parser.add_argument( - "--rm", + "--force", action="store_true", - help="Allow removal of existing build directory", + help="Overwrite existing builds", ) parser.add_argument( "--short-names", @@ -254,9 +263,9 @@ def _add_build_package_parser(subparsers): help="Disable building source distributions", ) parser.add_argument( - "--rm", + "--force", action="store_true", - help="Allow removal of existing build directory", + help="Replace existing builds", ) _add_shared_arguments(parser) @@ -498,6 +507,31 @@ def _add_shared_arguments(parser): ) +def _add_fetch_parser(subparsers): + parser = subparsers.add_parser( + "fetch", help="Fetch built-in scenarios from the packse repository" + ) + parser.set_defaults(call=_call_fetch) + parser.add_argument( + "--dest", + type=Path, + help="Where to place the fetched scenarios", + ) + parser.add_argument( + "--ref", + type=str, + default=None, + help="The reference to fetch", + ) + parser.add_argument( + "-f", + "--force", + action="store_true", + help="Allow replacement of existing destination directory", + ) + _add_shared_arguments(parser) + + def get_parser() -> argparse.ArgumentParser: parser = _root_parser() subparsers = parser.add_subparsers(title="commands") @@ -508,6 +542,7 @@ def get_parser() -> argparse.ArgumentParser: _add_list_parser(subparsers) _add_inspect_parser(subparsers) _add_serve_parser(subparsers) + _add_fetch_parser(subparsers) _add_index_parser(subparsers) return parser diff --git a/src/packse/fetch.py b/src/packse/fetch.py new file mode 100644 index 00000000..033e54f0 --- /dev/null +++ b/src/packse/fetch.py @@ -0,0 +1,78 @@ +import logging +import shutil +import subprocess +import tempfile +import time +from pathlib import Path + +import pkg_resources + +from packse.error import DestinationAlreadyExists + +logger = logging.getLogger(__name__) + + +def fetch( + dest: Path | None = None, + ref: str | None = None, + repo_url: str = "https://github.com/zanieb/packse", + repo_subdir: str = "scenarios", + force: bool = False, +): + start_time = time.time() + dest = dest or (Path.cwd() / "scenarios") + + if dest.exists() and not force: + raise DestinationAlreadyExists(dest) + + if not ref: + ref = pkg_resources.get_distribution("packse").version + if ref == "0.0.0": + ref = "HEAD" + + with tempfile.TemporaryDirectory() as tmpdir: + # Perform a sparse checkout where we only grab the `scenarios` folder + logger.info("Cloning repository %s", repo_url) + subprocess.check_call( + [ + "git", + "clone", + "-n", + "--depth=1", + "--filter=tree:0", + repo_url, + "repo", + ], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT, + cwd=tmpdir, + ) + + logger.info("Checking out directory '%s' at ref %s", repo_subdir, ref) + subprocess.check_call( + ["git", "sparse-checkout", "set", "--no-cone", repo_subdir], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT, + cwd=Path(tmpdir) / "repo", + ) + subprocess.check_call( + ["git", "checkout", ref], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT, + cwd=Path(tmpdir) / "repo", + ) + + src = Path(tmpdir) / "repo" / repo_subdir + file_count = 0 + for file in sorted(src.iterdir()): + file_count += 1 + logger.info("Found %s", file.name) + + logger.info("Copying files into '%s'", dest) + shutil.copytree(src, dest, dirs_exist_ok=True) + + logger.info( + "Fetched %s files in %.2fs", + file_count, + time.time() - start_time, + ) diff --git a/tests/__snapshots__/test_build.ambr b/tests/__snapshots__/test_build.ambr index 4b512a53..522d7a66 100644 --- a/tests/__snapshots__/test_build.ambr +++ b/tests/__snapshots__/test_build.ambr @@ -210,7 +210,7 @@ }), 'stderr': ''' Building 'example-8597e52a' in directory '[PWD]/build/example-8597e52a' - Destination directory '[PWD]/build/example-8597e52a' already exists. Pass `--rm` to allow removal. + Destination directory '[PWD]/build/example-8597e52a' already exists. Pass `--force` to allow removal. ''', 'stdout': '', @@ -218,12 +218,9 @@ # --- # name: test_build_example_already_exists_with_rm_flag dict({ - 'exit_code': 0, + 'exit_code': 2, 'stderr': '', - 'stdout': ''' - example-8597e52a - - ''', + 'stdout': '', }) # --- # name: test_build_example_short_names @@ -632,8 +629,8 @@ dict({ 'exit_code': 2, 'stderr': ''' - usage: packse build [-h] [--rm] [--short-names] [--no-hash] [--skip-root] [-v] - [-q] + usage: packse build [-h] [--force] [--short-names] [--no-hash] [--skip-root] + [-v] [-q] targets [targets ...] packse build: error: the following arguments are required: targets diff --git a/tests/__snapshots__/test_build_pkg.ambr b/tests/__snapshots__/test_build_pkg.ambr index 2a6412a3..e5a6b9d3 100644 --- a/tests/__snapshots__/test_build_pkg.ambr +++ b/tests/__snapshots__/test_build_pkg.ambr @@ -75,7 +75,7 @@ 'stderr': ''' usage: packse build-pkg [-h] [-t WHEEL_TAG] [--no-wheel] [--requires-python REQUIRES_PYTHON] [--no-sdist] - [--rm] [-v] [-q] + [--force] [-v] [-q] name version packse build-pkg: error: the following arguments are required: name, version @@ -146,7 +146,7 @@ 'stderr': ''' usage: packse build-pkg [-h] [-t WHEEL_TAG] [--no-wheel] [--requires-python REQUIRES_PYTHON] [--no-sdist] - [--rm] [-v] [-q] + [--force] [-v] [-q] name version packse build-pkg: error: the following arguments are required: version diff --git a/tests/__snapshots__/test_fetch.ambr b/tests/__snapshots__/test_fetch.ambr new file mode 100644 index 00000000..8dd5e3b2 --- /dev/null +++ b/tests/__snapshots__/test_fetch.ambr @@ -0,0 +1,216 @@ +# serializer version: 1 +# name: test_fetch + dict({ + 'exit_code': 0, + 'filesystem': dict({ + 'scenarios/does-not-exist.json': 'md5:9fae2b375228989d804897b6074d280f', + 'scenarios/example.json': 'md5:62d99e0a2370199b8d826738e579fbbf', + 'scenarios/excluded.json': 'md5:2b3d094be8433b84c0c54458f402a132', + 'scenarios/extras.json': 'md5:c06482eae069dedbf9c657e119b2f8a8', + 'scenarios/incompatible-versions.json': 'md5:b385322b218df131ed398eeba6c00c42', + 'scenarios/prereleases.json': 'md5:438f0472a289a51165dcbefdb26a3a69', + 'scenarios/requires-python.json': 'md5:2e849d71f835c731194141cce344bb13', + 'scenarios/wheels.json': 'md5:e5d512e65fdcb1e3c325e6986081208a', + 'scenarios/yanked.json': 'md5:ec3778cc2c784054c6b13bdb9b475f6e', + 'tree': ''' + test_fetch0 + └── scenarios + ├── does-not-exist.json + ├── example.json + ├── excluded.json + ├── extras.json + ├── incompatible-versions.json + ├── prereleases.json + ├── requires-python.json + ├── wheels.json + └── yanked.json + + 1 directories, 9 files + + ''', + }), + 'stderr': ''' + Cloning repository https://github.com/zanieb/packse + Checking out directory 'scenarios' at ref df20b898fdf1fd242cc19acc2a3148d72aa4d89f + Found does-not-exist.json + Found example.json + Found excluded.json + Found extras.json + Found incompatible-versions.json + Found prereleases.json + Found requires-python.json + Found wheels.json + Found yanked.json + Copying files into '[PWD]/scenarios' + Fetched 9 files in [TIME] + + ''', + 'stdout': '', + }) +# --- +# name: test_fetch_dest + dict({ + 'exit_code': 0, + 'filesystem': dict({ + 'foo/does-not-exist.json': 'md5:9fae2b375228989d804897b6074d280f', + 'foo/example.json': 'md5:62d99e0a2370199b8d826738e579fbbf', + 'foo/excluded.json': 'md5:2b3d094be8433b84c0c54458f402a132', + 'foo/extras.json': 'md5:c06482eae069dedbf9c657e119b2f8a8', + 'foo/incompatible-versions.json': 'md5:b385322b218df131ed398eeba6c00c42', + 'foo/prereleases.json': 'md5:438f0472a289a51165dcbefdb26a3a69', + 'foo/requires-python.json': 'md5:2e849d71f835c731194141cce344bb13', + 'foo/wheels.json': 'md5:e5d512e65fdcb1e3c325e6986081208a', + 'foo/yanked.json': 'md5:ec3778cc2c784054c6b13bdb9b475f6e', + 'tree': ''' + test_fetch_dest0 + └── foo + ├── does-not-exist.json + ├── example.json + ├── excluded.json + ├── extras.json + ├── incompatible-versions.json + ├── prereleases.json + ├── requires-python.json + ├── wheels.json + └── yanked.json + + 1 directories, 9 files + + ''', + }), + 'stderr': ''' + Cloning repository https://github.com/zanieb/packse + Checking out directory 'scenarios' at ref df20b898fdf1fd242cc19acc2a3148d72aa4d89f + Found does-not-exist.json + Found example.json + Found excluded.json + Found extras.json + Found incompatible-versions.json + Found prereleases.json + Found requires-python.json + Found wheels.json + Found yanked.json + Copying files into 'foo' + Fetched 9 files in [TIME] + + ''', + 'stdout': '', + }) +# --- +# name: test_fetch_dest_exists + dict({ + 'exit_code': 1, + 'filesystem': dict({ + 'tree': ''' + test_fetch_dest_exists0 + └── foo + + 1 directories + + ''', + }), + 'stderr': ''' + Destination directory 'foo' already exists. Pass `--force` to allow removal. + + ''', + 'stdout': '', + }) +# --- +# name: test_fetch_dest_exists_force + dict({ + 'exit_code': 0, + 'filesystem': dict({ + 'foo/does-not-exist.json': 'md5:9fae2b375228989d804897b6074d280f', + 'foo/example.json': 'md5:62d99e0a2370199b8d826738e579fbbf', + 'foo/excluded.json': 'md5:2b3d094be8433b84c0c54458f402a132', + 'foo/extras.json': 'md5:c06482eae069dedbf9c657e119b2f8a8', + 'foo/incompatible-versions.json': 'md5:b385322b218df131ed398eeba6c00c42', + 'foo/prereleases.json': 'md5:438f0472a289a51165dcbefdb26a3a69', + 'foo/requires-python.json': 'md5:2e849d71f835c731194141cce344bb13', + 'foo/wheels.json': 'md5:e5d512e65fdcb1e3c325e6986081208a', + 'foo/yanked.json': 'md5:ec3778cc2c784054c6b13bdb9b475f6e', + 'tree': ''' + test_fetch_dest_exists_force0 + └── foo + ├── does-not-exist.json + ├── example.json + ├── excluded.json + ├── extras.json + ├── incompatible-versions.json + ├── prereleases.json + ├── requires-python.json + ├── wheels.json + └── yanked.json + + 1 directories, 9 files + + ''', + }), + 'stderr': ''' + Cloning repository https://github.com/zanieb/packse + Checking out directory 'scenarios' at ref df20b898fdf1fd242cc19acc2a3148d72aa4d89f + Found does-not-exist.json + Found example.json + Found excluded.json + Found extras.json + Found incompatible-versions.json + Found prereleases.json + Found requires-python.json + Found wheels.json + Found yanked.json + Copying files into 'foo' + Fetched 9 files in [TIME] + + ''', + 'stdout': '', + }) +# --- +# name: test_fetch_tag + dict({ + 'exit_code': 0, + 'filesystem': dict({ + 'scenarios/does-not-exist.json': 'md5:9fae2b375228989d804897b6074d280f', + 'scenarios/example.json': 'md5:62d99e0a2370199b8d826738e579fbbf', + 'scenarios/excluded.json': 'md5:2b3d094be8433b84c0c54458f402a132', + 'scenarios/extras.json': 'md5:c06482eae069dedbf9c657e119b2f8a8', + 'scenarios/incompatible-versions.json': 'md5:b385322b218df131ed398eeba6c00c42', + 'scenarios/prereleases.json': 'md5:438f0472a289a51165dcbefdb26a3a69', + 'scenarios/requires-python.json': 'md5:2e849d71f835c731194141cce344bb13', + 'scenarios/wheels.json': 'md5:e5d512e65fdcb1e3c325e6986081208a', + 'scenarios/yanked.json': 'md5:ec3778cc2c784054c6b13bdb9b475f6e', + 'tree': ''' + test_fetch_tag0 + └── scenarios + ├── does-not-exist.json + ├── example.json + ├── excluded.json + ├── extras.json + ├── incompatible-versions.json + ├── prereleases.json + ├── requires-python.json + ├── wheels.json + └── yanked.json + + 1 directories, 9 files + + ''', + }), + 'stderr': ''' + Cloning repository https://github.com/zanieb/packse + Checking out directory 'scenarios' at ref 0.1.0 + Found does-not-exist.json + Found example.json + Found excluded.json + Found extras.json + Found incompatible-versions.json + Found prereleases.json + Found requires-python.json + Found wheels.json + Found yanked.json + Copying files into '[PWD]/scenarios' + Fetched 9 files in [TIME] + + ''', + 'stdout': '', + }) +# --- diff --git a/tests/__snapshots__/test_list.ambr b/tests/__snapshots__/test_list.ambr index aba9750f..a9b422bf 100644 --- a/tests/__snapshots__/test_list.ambr +++ b/tests/__snapshots__/test_list.ambr @@ -1,15 +1,4 @@ # serializer version: 1 -# name: test_list_example - dict({ - 'exit_code': 0, - 'stderr': '', - 'stdout': ''' - scenarios/example.json - example-8597e52a - - ''', - }) -# --- # name: test_list_invalid_target dict({ 'exit_code': 1, @@ -36,82 +25,7 @@ 'exit_code': 0, 'stderr': '', 'stdout': ''' - requires-package-does-not-exist-5a1a4a35 - requires-exact-version-does-not-exist-7cff23d9 - requires-greater-version-does-not-exist-63569c9e - requires-less-version-does-not-exist-2af6fa02 - transitive-requires-package-does-not-exist-64b04b2b example-8597e52a - excluded-only-version-72f0d052 - excluded-only-compatible-version-d6ce69da - dependency-excludes-range-of-compatible-versions-5824fb81 - dependency-excludes-non-contiguous-range-of-compatible-versions-119f929b - extra-required-c1e0ed38 - missing-extra-de25a6db - multiple-extras-required-502cbb59 - all-extras-required-4cf56e90 - extra-incompatible-with-extra-a5547b80 - extra-incompatible-with-extra-not-requested-8bb31c23 - extra-incompatible-with-root-aca6971b - extra-does-not-exist-backtrack-c4307e58 - direct-incompatible-versions-c0e7adfa - transitive-incompatible-with-root-version-a13da883 - transitive-incompatible-with-transitive-ec82e315 - package-only-prereleases-472fcc7e - package-only-prereleases-in-range-1017748b - requires-package-only-prereleases-in-range-global-opt-in-95140069 - requires-package-prerelease-and-final-any-909975d8 - package-prerelease-specified-only-final-available-6f8bea9f - package-prerelease-specified-only-prerelease-available-48d4bba0 - package-prerelease-specified-mixed-available-2b1193a7 - package-multiple-prereleases-kinds-72919cf7 - package-multiple-prereleases-numbers-cecdb92d - transitive-package-only-prereleases-e3c94488 - transitive-package-only-prereleases-in-range-20238f1b - transitive-package-only-prereleases-in-range-opt-in-d65d5fdf - transitive-prerelease-and-stable-dependency-d62255d0 - transitive-prerelease-and-stable-dependency-opt-in-0778b0eb - transitive-prerelease-and-stable-dependency-many-versions-cc6a6eac - transitive-prerelease-and-stable-dependency-many-versions-holes-041e36bc - package-only-prereleases-boundary-edcef999 - package-prereleases-boundary-6d600873 - package-prereleases-global-boundary-cf1b8081 - package-prereleases-specifier-boundary-357b9636 - requires-python-version-does-not-exist-1601081f - requires-python-version-less-than-current-d4ea58de - requires-python-version-greater-than-current-741c8854 - requires-python-version-greater-than-current-patch-0044ac94 - requires-python-version-greater-than-current-many-da5bd150 - requires-python-version-greater-than-current-backtrack-3204bc0a - requires-python-version-greater-than-current-excluded-874cae6d - requires-incompatible-python-version-compatible-override-3f4ac9b2 - requires-compatible-python-version-incompatible-override-fd6db412 - requires-incompatible-python-version-compatible-override-no-wheels-3521037f - requires-incompatible-python-version-compatible-override-no-wheels-available-system-c68bcf5c - requires-incompatible-python-version-compatible-override-no-compatible-wheels-d7b25a2d - requires-incompatible-python-version-compatible-override-other-wheel-a9179f0c - requires-python-patch-version-override-no-patch-e1884826 - requires-python-patch-version-override-patch-compatible-91b4bcfc - specific-tag-and-default-8f7a81f1 - only-wheels-a874f41e - no-wheels-0278f343 - no-wheels-with-matching-platform-f1a1f15c - no-sdist-no-wheels-with-matching-platform-94e293e5 - no-sdist-no-wheels-with-matching-python-40fe677d - no-sdist-no-wheels-with-matching-abi-8727a9b9 - no-wheels-no-build-662cbd94 - only-wheels-no-binary-dd137625 - no-build-9ff1e173 - no-binary-10e961b8 - package-only-yanked-e3de7eb4 - package-only-yanked-in-range-84b3720e - requires-package-yanked-and-unyanked-any-93eac6d7 - package-yanked-specified-mixed-available-3325916e - transitive-package-only-yanked-9ec30fe2 - transitive-package-only-yanked-in-range-872d714e - transitive-package-only-yanked-in-range-opt-in-1bbd5d1b - transitive-yanked-and-unyanked-dependency-eb1ba5f5 - transitive-yanked-and-unyanked-dependency-opt-in-f0760ee9 ''', }) @@ -119,95 +33,8 @@ # name: test_list_no_target_finds_all_valid_scenarios dict({ 'exit_code': 0, - 'stderr': '', - 'stdout': ''' - scenarios/does-not-exist.json - requires-package-does-not-exist-5a1a4a35 - requires-exact-version-does-not-exist-7cff23d9 - requires-greater-version-does-not-exist-63569c9e - requires-less-version-does-not-exist-2af6fa02 - transitive-requires-package-does-not-exist-64b04b2b - scenarios/example.json - example-8597e52a - scenarios/excluded.json - excluded-only-version-72f0d052 - excluded-only-compatible-version-d6ce69da - dependency-excludes-range-of-compatible-versions-5824fb81 - dependency-excludes-non-contiguous-range-of-compatible-versions-119f929b - scenarios/extras.json - extra-required-c1e0ed38 - missing-extra-de25a6db - multiple-extras-required-502cbb59 - all-extras-required-4cf56e90 - extra-incompatible-with-extra-a5547b80 - extra-incompatible-with-extra-not-requested-8bb31c23 - extra-incompatible-with-root-aca6971b - extra-does-not-exist-backtrack-c4307e58 - scenarios/incompatible-versions.json - direct-incompatible-versions-c0e7adfa - transitive-incompatible-with-root-version-a13da883 - transitive-incompatible-with-transitive-ec82e315 - scenarios/prereleases.json - package-only-prereleases-472fcc7e - package-only-prereleases-in-range-1017748b - requires-package-only-prereleases-in-range-global-opt-in-95140069 - requires-package-prerelease-and-final-any-909975d8 - package-prerelease-specified-only-final-available-6f8bea9f - package-prerelease-specified-only-prerelease-available-48d4bba0 - package-prerelease-specified-mixed-available-2b1193a7 - package-multiple-prereleases-kinds-72919cf7 - package-multiple-prereleases-numbers-cecdb92d - transitive-package-only-prereleases-e3c94488 - transitive-package-only-prereleases-in-range-20238f1b - transitive-package-only-prereleases-in-range-opt-in-d65d5fdf - transitive-prerelease-and-stable-dependency-d62255d0 - transitive-prerelease-and-stable-dependency-opt-in-0778b0eb - transitive-prerelease-and-stable-dependency-many-versions-cc6a6eac - transitive-prerelease-and-stable-dependency-many-versions-holes-041e36bc - package-only-prereleases-boundary-edcef999 - package-prereleases-boundary-6d600873 - package-prereleases-global-boundary-cf1b8081 - package-prereleases-specifier-boundary-357b9636 - scenarios/requires-python.json - requires-python-version-does-not-exist-1601081f - requires-python-version-less-than-current-d4ea58de - requires-python-version-greater-than-current-741c8854 - requires-python-version-greater-than-current-patch-0044ac94 - requires-python-version-greater-than-current-many-da5bd150 - requires-python-version-greater-than-current-backtrack-3204bc0a - requires-python-version-greater-than-current-excluded-874cae6d - requires-incompatible-python-version-compatible-override-3f4ac9b2 - requires-compatible-python-version-incompatible-override-fd6db412 - requires-incompatible-python-version-compatible-override-no-wheels-3521037f - requires-incompatible-python-version-compatible-override-no-wheels-available-system-c68bcf5c - requires-incompatible-python-version-compatible-override-no-compatible-wheels-d7b25a2d - requires-incompatible-python-version-compatible-override-other-wheel-a9179f0c - requires-python-patch-version-override-no-patch-e1884826 - requires-python-patch-version-override-patch-compatible-91b4bcfc - scenarios/wheels.json - specific-tag-and-default-8f7a81f1 - only-wheels-a874f41e - no-wheels-0278f343 - no-wheels-with-matching-platform-f1a1f15c - no-sdist-no-wheels-with-matching-platform-94e293e5 - no-sdist-no-wheels-with-matching-python-40fe677d - no-sdist-no-wheels-with-matching-abi-8727a9b9 - no-wheels-no-build-662cbd94 - only-wheels-no-binary-dd137625 - no-build-9ff1e173 - no-binary-10e961b8 - scenarios/yanked.json - package-only-yanked-e3de7eb4 - package-only-yanked-in-range-84b3720e - requires-package-yanked-and-unyanked-any-93eac6d7 - package-yanked-specified-mixed-available-3325916e - transitive-package-only-yanked-9ec30fe2 - transitive-package-only-yanked-in-range-872d714e - transitive-package-only-yanked-in-range-opt-in-1bbd5d1b - transitive-yanked-and-unyanked-dependency-eb1ba5f5 - transitive-yanked-and-unyanked-dependency-opt-in-f0760ee9 - - ''', + 'stderr': '', + 'stdout': '', }) # --- # name: test_list_no_versions @@ -215,91 +42,8 @@ 'exit_code': 0, 'stderr': '', 'stdout': ''' - scenarios/does-not-exist.json - requires-package-does-not-exist - requires-exact-version-does-not-exist - requires-greater-version-does-not-exist - requires-less-version-does-not-exist - transitive-requires-package-does-not-exist scenarios/example.json example - scenarios/excluded.json - excluded-only-version - excluded-only-compatible-version - dependency-excludes-range-of-compatible-versions - dependency-excludes-non-contiguous-range-of-compatible-versions - scenarios/extras.json - extra-required - missing-extra - multiple-extras-required - all-extras-required - extra-incompatible-with-extra - extra-incompatible-with-extra-not-requested - extra-incompatible-with-root - extra-does-not-exist-backtrack - scenarios/incompatible-versions.json - direct-incompatible-versions - transitive-incompatible-with-root-version - transitive-incompatible-with-transitive - scenarios/prereleases.json - package-only-prereleases - package-only-prereleases-in-range - requires-package-only-prereleases-in-range-global-opt-in - requires-package-prerelease-and-final-any - package-prerelease-specified-only-final-available - package-prerelease-specified-only-prerelease-available - package-prerelease-specified-mixed-available - package-multiple-prereleases-kinds - package-multiple-prereleases-numbers - transitive-package-only-prereleases - transitive-package-only-prereleases-in-range - transitive-package-only-prereleases-in-range-opt-in - transitive-prerelease-and-stable-dependency - transitive-prerelease-and-stable-dependency-opt-in - transitive-prerelease-and-stable-dependency-many-versions - transitive-prerelease-and-stable-dependency-many-versions-holes - package-only-prereleases-boundary - package-prereleases-boundary - package-prereleases-global-boundary - package-prereleases-specifier-boundary - scenarios/requires-python.json - requires-python-version-does-not-exist - requires-python-version-less-than-current - requires-python-version-greater-than-current - requires-python-version-greater-than-current-patch - requires-python-version-greater-than-current-many - requires-python-version-greater-than-current-backtrack - requires-python-version-greater-than-current-excluded - requires-incompatible-python-version-compatible-override - requires-compatible-python-version-incompatible-override - requires-incompatible-python-version-compatible-override-no-wheels - requires-incompatible-python-version-compatible-override-no-wheels-available-system - requires-incompatible-python-version-compatible-override-no-compatible-wheels - requires-incompatible-python-version-compatible-override-other-wheel - requires-python-patch-version-override-no-patch - requires-python-patch-version-override-patch-compatible - scenarios/wheels.json - specific-tag-and-default - only-wheels - no-wheels - no-wheels-with-matching-platform - no-sdist-no-wheels-with-matching-platform - no-sdist-no-wheels-with-matching-python - no-sdist-no-wheels-with-matching-abi - no-wheels-no-build - only-wheels-no-binary - no-build - no-binary - scenarios/yanked.json - package-only-yanked - package-only-yanked-in-range - requires-package-yanked-and-unyanked-any - package-yanked-specified-mixed-available - transitive-package-only-yanked - transitive-package-only-yanked-in-range - transitive-package-only-yanked-in-range-opt-in - transitive-yanked-and-unyanked-dependency - transitive-yanked-and-unyanked-dependency-opt-in ''', }) @@ -319,91 +63,8 @@ 'exit_code': 0, 'stderr': '', 'stdout': ''' - scenarios/does-not-exist.json - 5a1a4a35 - 7cff23d9 - 63569c9e - 2af6fa02 - 64b04b2b scenarios/example.json 8597e52a - scenarios/excluded.json - 72f0d052 - d6ce69da - 5824fb81 - 119f929b - scenarios/extras.json - c1e0ed38 - de25a6db - 502cbb59 - 4cf56e90 - a5547b80 - 8bb31c23 - aca6971b - c4307e58 - scenarios/incompatible-versions.json - c0e7adfa - a13da883 - ec82e315 - scenarios/prereleases.json - 472fcc7e - 1017748b - 95140069 - 909975d8 - 6f8bea9f - 48d4bba0 - 2b1193a7 - 72919cf7 - cecdb92d - e3c94488 - 20238f1b - d65d5fdf - d62255d0 - 0778b0eb - cc6a6eac - 041e36bc - edcef999 - 6d600873 - cf1b8081 - 357b9636 - scenarios/requires-python.json - 1601081f - d4ea58de - 741c8854 - 0044ac94 - da5bd150 - 3204bc0a - 874cae6d - 3f4ac9b2 - fd6db412 - 3521037f - c68bcf5c - d7b25a2d - a9179f0c - e1884826 - 91b4bcfc - scenarios/wheels.json - 8f7a81f1 - a874f41e - 0278f343 - f1a1f15c - 94e293e5 - 40fe677d - 8727a9b9 - 662cbd94 - dd137625 - 9ff1e173 - 10e961b8 - scenarios/yanked.json - e3de7eb4 - 84b3720e - 93eac6d7 - 3325916e - 9ec30fe2 - 872d714e - 1bbd5d1b - eb1ba5f5 - f0760ee9 ''', }) diff --git a/tests/__snapshots__/test_root.ambr b/tests/__snapshots__/test_root.ambr index 0c4d3427..4217bd4a 100644 --- a/tests/__snapshots__/test_root.ambr +++ b/tests/__snapshots__/test_root.ambr @@ -5,7 +5,8 @@ 'stderr': '', 'stdout': ''' usage: packse [-h] [-v] [-q] - {build,build-pkg,view,publish,list,inspect,serve,index} ... + {build,build-pkg,view,publish,list,inspect,serve,fetch,index} + ... Utilities for working example packaging scenarios @@ -15,7 +16,7 @@ -q, --quiet Disable logging commands: - {build,build-pkg,view,publish,list,inspect,serve,index} + {build,build-pkg,view,publish,list,inspect,serve,fetch,index} build Build packages for a scenario build-pkg Build a single package view View a scenario @@ -23,6 +24,7 @@ list List scenarios at the given paths inspect Inspect scenarios at the given paths serve Serve scenarios on a temporary local package index + fetch Fetch built-in scenarios from the packse repository index Run a local package index ''', @@ -34,7 +36,8 @@ 'stderr': '', 'stdout': ''' usage: packse [-h] [-v] [-q] - {build,build-pkg,view,publish,list,inspect,serve,index} ... + {build,build-pkg,view,publish,list,inspect,serve,fetch,index} + ... Utilities for working example packaging scenarios @@ -44,7 +47,7 @@ -q, --quiet Disable logging commands: - {build,build-pkg,view,publish,list,inspect,serve,index} + {build,build-pkg,view,publish,list,inspect,serve,fetch,index} build Build packages for a scenario build-pkg Build a single package view View a scenario @@ -52,6 +55,7 @@ list List scenarios at the given paths inspect Inspect scenarios at the given paths serve Serve scenarios on a temporary local package index + fetch Fetch built-in scenarios from the packse repository index Run a local package index ''', @@ -63,7 +67,8 @@ 'stderr': '', 'stdout': ''' usage: packse [-h] [-v] [-q] - {build,build-pkg,view,publish,list,inspect,serve,index} ... + {build,build-pkg,view,publish,list,inspect,serve,fetch,index} + ... Utilities for working example packaging scenarios @@ -73,7 +78,7 @@ -q, --quiet Disable logging commands: - {build,build-pkg,view,publish,list,inspect,serve,index} + {build,build-pkg,view,publish,list,inspect,serve,fetch,index} build Build packages for a scenario build-pkg Build a single package view View a scenario @@ -81,6 +86,7 @@ list List scenarios at the given paths inspect Inspect scenarios at the given paths serve Serve scenarios on a temporary local package index + fetch Fetch built-in scenarios from the packse repository index Run a local package index ''', diff --git a/tests/__snapshots__/test_view.ambr b/tests/__snapshots__/test_view.ambr index 118d4d5d..5db600e5 100644 --- a/tests/__snapshots__/test_view.ambr +++ b/tests/__snapshots__/test_view.ambr @@ -97,13 +97,8 @@ # name: test_view_no_target dict({ 'exit_code': 2, - 'stderr': ''' - usage: packse view [-h] [--name NAME] [--short-names] [-v] [-q] - targets [targets ...] - packse view: error: the following arguments are required: targets - - ''', - 'stdout': '', + 'stderr': '', + 'stdout': '', }) # --- # name: test_view_one_target_does_not_exist diff --git a/tests/test_fetch.py b/tests/test_fetch.py new file mode 100644 index 00000000..1d22292d --- /dev/null +++ b/tests/test_fetch.py @@ -0,0 +1,78 @@ +from .common import snapshot_command + + +def test_fetch(snapshot, tmpcwd): + assert ( + # Include a ref for reproducibility + snapshot_command( + ["fetch", "--ref", "df20b898fdf1fd242cc19acc2a3148d72aa4d89f"], + snapshot_filesystem=True, + working_directory=tmpcwd, + ) + == snapshot + ) + + +def test_fetch_dest(snapshot, tmpcwd): + assert ( + snapshot_command( + [ + "fetch", + "--dest", + "foo", + "--ref", + "df20b898fdf1fd242cc19acc2a3148d72aa4d89f", + ], + snapshot_filesystem=True, + working_directory=tmpcwd, + ) + == snapshot + ) + + +def test_fetch_dest_exists(snapshot, tmpcwd): + (tmpcwd / "foo").mkdir() + assert ( + snapshot_command( + [ + "fetch", + "--dest", + "foo", + "--ref", + "df20b898fdf1fd242cc19acc2a3148d72aa4d89f", + ], + snapshot_filesystem=True, + working_directory=tmpcwd, + ) + == snapshot + ) + + +def test_fetch_dest_exists_force(snapshot, tmpcwd): + (tmpcwd / "foo").mkdir() + assert ( + snapshot_command( + [ + "fetch", + "--dest", + "foo", + "--force", + "--ref", + "df20b898fdf1fd242cc19acc2a3148d72aa4d89f", + ], + snapshot_filesystem=True, + working_directory=tmpcwd, + ) + == snapshot + ) + + +def test_fetch_tag(snapshot, tmpcwd): + assert ( + snapshot_command( + ["fetch", "--ref", "0.1.0"], + snapshot_filesystem=True, + working_directory=tmpcwd, + ) + == snapshot + ) diff --git a/tests/test_list.py b/tests/test_list.py index 4c157a4e..efd51d38 100644 --- a/tests/test_list.py +++ b/tests/test_list.py @@ -5,19 +5,25 @@ def test_list_no_target_finds_all_valid_scenarios(snapshot): - assert snapshot_command(["list"]) == snapshot + assert ( + snapshot_command(["list"], snapshot_stdout=False, snapshot_stderr=False) + == snapshot + ) def test_list_no_versions(snapshot): - assert snapshot_command(["list", "--no-versions"]) == snapshot + target = __development_base_path__ / "scenarios" / "example.json" + assert snapshot_command(["list", str(target), "--no-versions"]) == snapshot def test_list_short_names(snapshot): - assert snapshot_command(["list", "--short-names"]) == snapshot + target = __development_base_path__ / "scenarios" / "example.json" + assert snapshot_command(["list", str(target), "--short-names"]) == snapshot def test_list_no_sources(snapshot): - assert snapshot_command(["list", "--no-sources"]) == snapshot + target = __development_base_path__ / "scenarios" / "example.json" + assert snapshot_command(["list", str(target), "--no-sources"]) == snapshot def test_list_target_does_not_exist(snapshot): @@ -45,8 +51,3 @@ def test_list_invalid_target_skip_invalid(snapshot, tmpcwd): snapshot_command(["list", str(bad_target), str(good_target), "--skip-invalid"]) == snapshot ) - - -def test_list_example(snapshot): - target = __development_base_path__ / "scenarios" / "example.json" - assert snapshot_command(["list", str(target)]) == snapshot diff --git a/tests/test_view.py b/tests/test_view.py index 1a79a1f0..9d67d1c4 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -5,7 +5,10 @@ def test_view_no_target(snapshot): - assert snapshot_command(["view"]) == snapshot + assert ( + snapshot_command(["view"], snapshot_stderr=False, snapshot_stdout=False) + == snapshot + ) def test_view_target_does_not_exist(snapshot):