Skip to content

Commit

Permalink
Add packse list command
Browse files Browse the repository at this point in the history
  • Loading branch information
zanieb committed Dec 14, 2023
1 parent 8d0d29b commit a8ab50a
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 15 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ A scenario is a JSON description of a dependency tree.

See [`scenarios/example.json`](./scenarios/example.json)

### Listing scenarios

A list of available scenarios can be printed with the `list` command:

```bash
packse list
```

### Building scenarios

A scenario can be used to generate packages and build distributions:
Expand Down
4 changes: 2 additions & 2 deletions src/packse/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
BuildError,
DestinationAlreadyExists,
InvalidScenario,
ScenarioNotFound,
FileNotFound,
)
from packse.scenario import (
Package,
Expand All @@ -35,7 +35,7 @@ def build(targets: list[Path], rm_destination: bool):

for target in targets:
if not target.exists():
raise ScenarioNotFound(target)
raise FileNotFound(target)

try:
logger.debug("Loading %s", target)
Expand Down
38 changes: 38 additions & 0 deletions src/packse/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
)
from packse.publish import publish
from packse.view import view
from packse.list import list


def entrypoint():
Expand Down Expand Up @@ -65,6 +66,15 @@ def _call_publish(args):
)


def _call_list(args):
skip_invalid = args.skip_invalid
if not args.targets:
skip_invalid = True
args.targets = Path.cwd().glob("**/*.json")

list(args.targets, args.no_versions, skip_invalid, args.no_sources)


def _root_parser():
parser = argparse.ArgumentParser(
description="Utilities for working example packaging scenarios",
Expand Down Expand Up @@ -138,6 +148,33 @@ def _add_view_parser(subparsers):
_add_shared_arguments(parser)


def _add_list_parser(subparsers):
parser = subparsers.add_parser("list", help="List scenarios")
parser.set_defaults(call=_call_list)
parser.add_argument(
"targets",
type=Path,
nargs="*",
help="The scenario files to load",
)
parser.add_argument(
"--no-versions",
action="store_true",
help="Do not include in the scenario versions in the displayed names.",
)
parser.add_argument(
"--skip-invalid",
action="store_true",
help="Skip invalid scenario files instead of failing.",
)
parser.add_argument(
"--no-sources",
action="store_true",
help="Do not show the source file for each scenario.",
)
_add_shared_arguments(parser)


def _add_shared_arguments(parser):
parser.add_argument(
"-v",
Expand All @@ -160,5 +197,6 @@ def get_parser() -> argparse.ArgumentParser:
_add_build_parser(subparsers)
_add_view_parser(subparsers)
_add_publish_parser(subparsers)
_add_list_parser(subparsers)

return parser
4 changes: 2 additions & 2 deletions src/packse/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ def __init__(self, path: Path, reason: str) -> None:
super().__init__(message)


class ScenarioNotFound(UserError):
class FileNotFound(UserError):
def __init__(self, path: Path) -> None:
message = f"Scenario '{path}' not found"
message = f"File '{path}' not found"
super().__init__(message)


Expand Down
73 changes: 73 additions & 0 deletions src/packse/list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""
List all scenarios.
"""
import logging
from pathlib import Path

from packaging.requirements import Requirement

from packse.error import InvalidScenario, FileNotFound
from packse.scenario import (
Package,
Scenario,
load_scenarios,
scenario_prefix,
)

logger = logging.getLogger(__name__)


def list(
targets: list[Path],
no_versions: bool = False,
skip_invalid: bool = False,
no_sources: bool = False,
):
scenarios: dict[Path, list[Scenario]] = {}

# Validate and collect all targets first
for target in sorted(targets):
if not target.exists():
raise FileNotFound(target)

try:
logger.debug("Loading %s", target)
scenarios[target] = load_scenarios(target)
except Exception as exc:
if not skip_invalid:
raise InvalidScenario(target, reason=str(exc)) from exc

# Then list each one
for source, scenarios in scenarios.items():
prefix = "" if no_sources else " " * 4
if not no_sources:
print(to_display_path(source))

for scenario in scenarios:
if no_versions:
name = scenario.name
else:
name = scenario_prefix(scenario)

print(prefix + name)


def to_display_path(path: Path | str, relative_to: Path | str | None = None) -> str:
"""
Convert a path to a displayable path. The absolute path or relative path to the
current (or given) directory will be returned, whichever is shorter.
"""
path, relative_to = (
Path(path).resolve(),
Path(relative_to or ".").resolve(),
)

absolute_path = str(path)

try:
relative_path = str(path.relative_to(relative_to))
except ValueError:
# Not a child path, use the other one
return absolute_path

return relative_path if len(relative_path) < len(absolute_path) else absolute_path
4 changes: 2 additions & 2 deletions src/packse/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from packaging.requirements import Requirement

from packse.error import InvalidScenario, ScenarioNotFound
from packse.error import InvalidScenario, FileNotFound
from packse.scenario import (
Package,
Scenario,
Expand All @@ -23,7 +23,7 @@ def view(targets: list[Path]):
# Validate and collect all targets first
for target in targets:
if not target.exists():
raise ScenarioNotFound(target)
raise FileNotFound(target)

try:
logger.debug("Loading %s", target)
Expand Down
2 changes: 1 addition & 1 deletion tests/__snapshots__/test_build.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@
dict({
'exit_code': 1,
'stderr': '''
Scenario 'foo' not found.
File 'foo' not found.

''',
'stdout': '',
Expand Down
113 changes: 113 additions & 0 deletions tests/__snapshots__/test_list.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# serializer version: 1
# name: test_list_example
dict({
'exit_code': 0,
'stderr': '',
'stdout': '''
scenarios/example.json
example-5803604b

''',
})
# ---
# name: test_list_invalid_target
dict({
'exit_code': 1,
'stderr': '''
File at '[PWD]/test.json' is not a valid scenario: Input data was truncated.

''',
'stdout': '',
})
# ---
# name: test_list_invalid_target_skip_invalid
dict({
'exit_code': 0,
'stderr': '',
'stdout': '''
[PROJECT_ROOT]/scenarios/example.json
example-5803604b

''',
})
# ---
# name: test_list_no_sources
dict({
'exit_code': 0,
'stderr': '',
'stdout': '''
example-5803604b
requires-package-does-not-exist-e4666012
requires-exact-version-does-not-exist-c640da4b
requires-greater-version-does-not-exist-ceb05782
requires-less-version-does-not-exist-14de847d
transitive-requires-package-does-not-exist-15763ba4
requires-direct-incompatible-versions-3a64108d
requires-transitive-incompatible-with-root-version-8af9847a
requires-transitive-incompatible-with-transitive-cb77ed7e

''',
})
# ---
# name: test_list_no_target
dict({
'exit_code': 0,
'stderr': '',
'stdout': '''
scenarios/example.json
example-5803604b
scenarios/requires-does-not-exist.json
requires-package-does-not-exist-e4666012
requires-exact-version-does-not-exist-c640da4b
requires-greater-version-does-not-exist-ceb05782
requires-less-version-does-not-exist-14de847d
transitive-requires-package-does-not-exist-15763ba4
scenarios/requires-incompatible-versions.json
requires-direct-incompatible-versions-3a64108d
requires-transitive-incompatible-with-root-version-8af9847a
requires-transitive-incompatible-with-transitive-cb77ed7e

''',
})
# ---
# name: test_list_no_version
dict({
'exit_code': 0,
'stderr': '',
'stdout': '''
scenarios/example.json
example
scenarios/requires-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/requires-incompatible-versions.json
requires-direct-incompatible-versions
requires-transitive-incompatible-with-root-version
requires-transitive-incompatible-with-transitive

''',
})
# ---
# name: test_list_one_target_does_not_exist
dict({
'exit_code': 1,
'stderr': '''
File 'foo' not found.

''',
'stdout': '',
})
# ---
# name: test_list_target_does_not_exist
dict({
'exit_code': 1,
'stderr': '''
File 'foo' not found.

''',
'stdout': '',
})
# ---
15 changes: 9 additions & 6 deletions tests/__snapshots__/test_root.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
'exit_code': 0,
'stderr': '',
'stdout': '''
usage: packse [-h] [-v] [-q] {build,view,publish} ...
usage: packse [-h] [-v] [-q] {build,view,publish,list} ...

Utilities for working example packaging scenarios

Expand All @@ -14,10 +14,11 @@
-q, --quiet Disable logging

commands:
{build,view,publish}
{build,view,publish,list}
build Build packages for a scenario
view View a scenario
publish Publish packages for a scenario
list List scenarios

''',
})
Expand All @@ -27,7 +28,7 @@
'exit_code': 0,
'stderr': '',
'stdout': '''
usage: packse [-h] [-v] [-q] {build,view,publish} ...
usage: packse [-h] [-v] [-q] {build,view,publish,list} ...

Utilities for working example packaging scenarios

Expand All @@ -37,10 +38,11 @@
-q, --quiet Disable logging

commands:
{build,view,publish}
{build,view,publish,list}
build Build packages for a scenario
view View a scenario
publish Publish packages for a scenario
list List scenarios

''',
})
Expand All @@ -50,7 +52,7 @@
'exit_code': 0,
'stderr': '',
'stdout': '''
usage: packse [-h] [-v] [-q] {build,view,publish} ...
usage: packse [-h] [-v] [-q] {build,view,publish,list} ...

Utilities for working example packaging scenarios

Expand All @@ -60,10 +62,11 @@
-q, --quiet Disable logging

commands:
{build,view,publish}
{build,view,publish,list}
build Build packages for a scenario
view View a scenario
publish Publish packages for a scenario
list List scenarios

''',
})
Expand Down
Loading

0 comments on commit a8ab50a

Please sign in to comment.