Skip to content

Commit 4a2fbaa

Browse files
committed
feat!: Support adaptive chunking, general CLI improvement
* Add an --extensions option that can select which Open Job Description extensions to enable. By default all extensions that are implemented are enabled. * Validate that task parameters provided from the CLI are within the parameter space specified by the job template. * Modify LocalSession from a "deferred" style to "immediate." Previously it queued up all the session actions, then ran them. To be adaptive, it needs to run some session actions and use their timings to change the chunk size, and that's not compatible with pre-determining them all. * Add the ability to run multiple steps in the same session when they include step environments. It exits any step environments for a step before proceeding to the next one. * Change the default timestamps to be relative to session start, to make local debugging easier. Add an option to control the formatting between relative, local, and utc. * Modify __main__.py to be more idiomatic by removing the 'if __name__ == "__main__"' and moving the main() function out. * Create an openjd.cli.main function to call with the CLI arguments. * Modify a number of tests to use a more end-to-end CLI main function approach. * Bump openjd-model and openjd-sessions dependency versions to include the required changes. BREAKING CHANGE: The logging output has changed to use relative timestamps by default, and print more messages about the job and steps that are running. Signed-off-by: Mark Wiebe <[email protected]>
1 parent e497bd7 commit 4a2fbaa

36 files changed

+2141
-1653
lines changed

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ This library requires:
2222

2323
## Versioning
2424

25-
This package's version follows [Semantic Versioning 2.0](https://semver.org/), but is still considered to be in its
25+
This package's version follows [Semantic Versioning 2.0](https://semver.org/), but is still considered to be in its
2626
initial development, thus backwards incompatible versions are denoted by minor version bumps. To help illustrate how
2727
versions will increment during this initial development stage, they are described below:
2828

29-
1. The MAJOR version is currently 0, indicating initial development.
30-
2. The MINOR version is currently incremented when backwards incompatible changes are introduced to the public API.
31-
3. The PATCH version is currently incremented when bug fixes or backwards compatible changes are introduced to the public API.
29+
1. The MAJOR version is currently 0, indicating initial development.
30+
2. The MINOR version is currently incremented when backwards incompatible changes are introduced to the public API.
31+
3. The PATCH version is currently incremented when bug fixes or backwards compatible changes are introduced to the public API.
3232

3333
## Contributing
3434

@@ -64,7 +64,7 @@ Template at '/path/to/job.template.json' passes validation checks!
6464

6565
### `summary`
6666

67-
Displays summary information about a sample Job or Step, and the Steps and Tasks therein. The user may provide parameters to
67+
Displays summary information about a sample Job or Step, and the Steps and Tasks therein. The user may provide parameters to
6868
customize the Job, as parameters can have an impact on the amount of Steps and Tasks that a job consists of.
6969

7070
#### Arguments
@@ -145,7 +145,7 @@ Session ended successfully
145145
Job: MyJob
146146
Step: Step1
147147
Duration: 1.0 seconds
148-
Tasks run: 1
148+
Chunks run: 1
149149

150150
```
151151

@@ -181,11 +181,11 @@ See [VERIFYING_PGP_SIGNATURE](VERIFYING_PGP_SIGNATURE.md) for more information.
181181

182182
## Security
183183

184-
We take all security reports seriously. When we receive such reports, we will
185-
investigate and subsequently address any potential vulnerabilities as quickly
186-
as possible. If you discover a potential security issue in this project, please
184+
We take all security reports seriously. When we receive such reports, we will
185+
investigate and subsequently address any potential vulnerabilities as quickly
186+
as possible. If you discover a potential security issue in this project, please
187187
notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/)
188-
or directly via email to [AWS Security]([email protected]). Please do not
188+
or directly via email to [AWS Security]([email protected]). Please do not
189189
create a public GitHub issue in this project.
190190

191191
## License

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ classifiers = [
2929
"Intended Audience :: End Users/Desktop"
3030
]
3131
dependencies = [
32-
"openjd-sessions == 0.10.*",
33-
"openjd-model == 0.6.*"
32+
"openjd-sessions >= 0.10.1,< 0.11",
33+
"openjd-model == 0.7.*"
3434
]
3535

3636
[project.urls]

src/openjd/__main__.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,6 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22

3-
import sys
4-
import traceback
3+
from .cli._create_argparser import main
54

6-
from .cli._create_argparser import create_argparser
75

8-
__all__ = ("main",)
9-
10-
11-
def main() -> None:
12-
parser = create_argparser()
13-
14-
args = parser.parse_args(sys.argv[1:])
15-
try:
16-
# Raises:
17-
# SystemExit - on failure
18-
args.func(args)
19-
except Exception as exc:
20-
print(f"ERROR: {str(exc)}", file=sys.stderr)
21-
traceback.print_exc()
22-
sys.exit(1)
23-
24-
25-
if __name__ == "__main__":
26-
main()
6+
main()

src/openjd/cli/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
3+
from ._create_argparser import main
4+
5+
__all__ = ["main"]

src/openjd/cli/_check/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22

33
from .._common import add_common_arguments, CommonArgument, SubparserGroup
4-
from ._check_command import do_check
4+
from ._check_command import add_check_arguments, do_check
55

66

77
def populate_argparser(subcommands: SubparserGroup) -> None:
@@ -12,7 +12,7 @@ def populate_argparser(subcommands: SubparserGroup) -> None:
1212
description="Given an Open Job Description template file, parse the file and run validation checks against it to ensure that it is correctly formed.",
1313
)
1414

15-
# `check` has no unique arguments;
1615
# add all arguments through `add_common_arguments`
1716
add_common_arguments(check_parser, {CommonArgument.PATH})
17+
add_check_arguments(check_parser)
1818
check_parser.set_defaults(func=do_check)

src/openjd/cli/_check/_check_command.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22

3-
from argparse import Namespace
3+
from argparse import ArgumentParser, Namespace
44
from openjd.model import (
55
DecodeValidationError,
66
TemplateSpecificationVersion,
77
decode_job_template,
88
decode_environment_template,
99
)
1010

11-
from .._common import read_template, OpenJDCliResult, print_cli_result
11+
from .._common import read_template, OpenJDCliResult, print_cli_result, process_extensions_argument
12+
13+
14+
def add_check_arguments(run_parser: ArgumentParser):
15+
run_parser.add_argument(
16+
"--extensions",
17+
help="A comma-separated list of Open Job Description extension names to enable. Defaults to all that are implemented.",
18+
)
1219

1320

1421
@print_cli_result
1522
def do_check(args: Namespace) -> OpenJDCliResult:
1623
"""Open a provided template file and check its schema for errors."""
1724

25+
extensions = process_extensions_argument(args.extensions)
26+
1827
try:
1928
# Raises: RuntimeError
2029
template_object = read_template(args.path)
@@ -27,7 +36,7 @@ def do_check(args: Namespace) -> OpenJDCliResult:
2736

2837
# Raises: DecodeValidationError
2938
if TemplateSpecificationVersion.is_job_template(template_version):
30-
decode_job_template(template=template_object, supported_extensions=["TASK_CHUNKING"])
39+
decode_job_template(template=template_object, supported_extensions=extensions)
3140
elif TemplateSpecificationVersion.is_environment_template(template_version):
3241
decode_environment_template(template=template_object)
3342
else:

src/openjd/cli/_common/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import yaml
1010
import os
1111

12+
from ._extensions import process_extensions_argument
1213
from ._job_from_template import (
1314
job_from_template,
1415
get_job_params,
@@ -26,6 +27,7 @@
2627
"get_doc_type",
2728
"get_job_params",
2829
"get_params_from_file",
30+
"process_extensions_argument",
2931
"read_template",
3032
"read_job_template",
3133
"read_environment_template",
@@ -109,10 +111,10 @@ def add(self, name: str, description: str, **kwargs) -> ArgumentParser:
109111
return self.group.add_parser(name, **kwargs)
110112

111113

112-
def generate_job(args: Namespace) -> Job:
114+
def generate_job(args: Namespace, *, supported_extensions: list[str]) -> Job:
113115
try:
114116
# Raises: RuntimeError, DecodeValidationError
115-
template = read_job_template(args.path)
117+
template = read_job_template(args.path, supported_extensions=supported_extensions)
116118
# Raises: RuntimeError
117119
return job_from_template(
118120
template,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
3+
from typing import Optional
4+
5+
# This is the list of Open Job Description extensions with implemented support
6+
SUPPORTED_EXTENSIONS = ["TASK_CHUNKING"]
7+
8+
9+
def process_extensions_argument(extensions: Optional[str]) -> list[str]:
10+
"""Process the comma-separated extensions argument and return a list of supported extensions."""
11+
12+
# If the option is not provided, default to all the supported extensions.
13+
if extensions is None:
14+
return SUPPORTED_EXTENSIONS
15+
16+
extensions_list = [
17+
extension.strip().upper() for extension in extensions.split(",") if extension.strip() != ""
18+
]
19+
20+
unsupported_extensions = set(extensions_list) - set(SUPPORTED_EXTENSIONS)
21+
if unsupported_extensions:
22+
raise ValueError(
23+
f"Unsupported Open Job Description extension(s): {', '.join(sorted(unsupported_extensions))}"
24+
)
25+
26+
return extensions_list

src/openjd/cli/_common/_validation_utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def read_template(template_file: Path) -> dict[str, Any]:
5353
return template_object
5454

5555

56-
def read_job_template(template_file: Path) -> JobTemplate:
56+
def read_job_template(template_file: Path, *, supported_extensions: list[str]) -> JobTemplate:
5757
"""Open a JSON or YAML-formatted file and attempt to parse it into a JobTemplate object.
5858
Raises a RuntimeError if the file doesn't exist or can't be opened, and raises a
5959
DecodeValidationError if its contents can't be parsed into a valid JobTemplate.
@@ -62,7 +62,9 @@ def read_job_template(template_file: Path) -> JobTemplate:
6262
template_object = read_template(template_file)
6363

6464
# Raises: DecodeValidationError
65-
template = decode_job_template(template=template_object, supported_extensions=["TASK_CHUNKING"])
65+
template = decode_job_template(
66+
template=template_object, supported_extensions=supported_extensions
67+
)
6668

6769
return template
6870

src/openjd/cli/_create_argparser.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22

33
from argparse import ArgumentParser
4+
import sys
5+
import traceback
6+
from typing import Optional
47

58
from ._common import SubparserGroup
69

@@ -28,3 +31,21 @@ def create_argparser() -> ArgumentParser:
2831
populate_run_subparser(subcommands)
2932
populate_schema_subparser(subcommands)
3033
return parser
34+
35+
36+
def main(arg_list: Optional[list[str]] = None) -> None:
37+
"""Main function for invoking the CLI"""
38+
parser = create_argparser()
39+
40+
if arg_list is None:
41+
arg_list = sys.argv[1:]
42+
43+
args = parser.parse_args(arg_list)
44+
try:
45+
# Raises:
46+
# SystemExit - on failure
47+
args.func(args)
48+
except Exception as exc:
49+
print(f"ERROR: {str(exc)}", file=sys.stderr)
50+
traceback.print_exc()
51+
sys.exit(1)

0 commit comments

Comments
 (0)