Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ All notable changes to microbench are documented here.

### Enhancements

- **`--mixin defaults` keyword** (CLI): `defaults` can be used as a mixin
name to expand to the standard default set (`python-info`, `host-info`,
`slurm-info`, `loaded-modules`, `working-dir`). This makes it easy to add
one or more extra mixins without listing all five defaults explicitly:
`microbench --mixin defaults file-hash -- ./job.sh`.

- **`file-hash` mixin — automatic argument file scanning** (CLI): the
default hash list now includes not only the command executable (`cmd[0]`)
but also any command-line arguments (`cmd[1:]`) that resolve to existing
Expand Down
16 changes: 14 additions & 2 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,20 @@ mixins with descriptions:
microbench --show-mixins
```

Specifying `--mixin` replaces the defaults entirely. Use `--no-mixin` to
disable all mixins and record only timing and command fields:
Specifying `--mixin` replaces the defaults entirely. Use the special name
`defaults` in a `--mixin` list to include the standard default set, so you
can extend it without listing all of them explicitly:

```bash
# Defaults plus file-hash
microbench --mixin defaults file-hash -- ./job.sh

# Defaults plus git-info and cgroup-limits
microbench --mixin defaults git-info cgroup-limits -- ./job.sh
```

Use `--no-mixin` to disable all mixins and record only timing and command
fields:

```bash
# Only Python info — no host info or SLURM
Expand Down
8 changes: 7 additions & 1 deletion microbench/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,13 @@ def main(argv=None):
elif args.no_mixins:
mixin_names = []
elif args.mixins is not None:
mixin_names = list(dict.fromkeys(args.mixins))
expanded = []
for name in args.mixins:
if name == 'defaults':
expanded.extend(_DEFAULT_MIXINS)
else:
expanded.append(name)
mixin_names = list(dict.fromkeys(expanded))
else:
mixin_names = list(_DEFAULT_MIXINS)

Expand Down
6 changes: 5 additions & 1 deletion microbench/cli/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,11 @@ def _make_mixin_type(mixin_map):
"""Return an argparse type function that normalises and validates mixin names."""

def _parse(value):
if value == 'defaults':
return 'defaults'
canonical = _mb_name_to_cli(value) if value.startswith('MB') else value
if canonical not in mixin_map:
valid = ', '.join(sorted(mixin_map))
valid = 'defaults, ' + ', '.join(sorted(mixin_map))
raise argparse.ArgumentTypeError(
f'unknown mixin {value!r}. Available: {valid}'
)
Expand Down Expand Up @@ -163,6 +165,8 @@ def _build_parser(mixin_map):
type=_make_mixin_type(mixin_map),
help=(
'One or more mixins to include. Replaces defaults when specified. '
'Use the special name "defaults" to include the default set '
'(e.g. --mixin defaults file-hash). '
'Use --show-mixins to list available options. '
'MB-prefixed names (e.g. MBHostInfo) are also accepted.'
),
Expand Down
48 changes: 48 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,54 @@ def test_cli_explicit_mixin_replaces_defaults():
assert 'slurm' not in record


def test_cli_mixin_defaults_keyword_alone():
"""--mixin defaults alone is equivalent to omitting --mixin."""
_, record_explicit, _ = _run_main(['--mixin', 'defaults', '--', 'true'])
_, record_implicit, _ = _run_main(['--', 'true'])

for key in ('python', 'host', 'slurm', 'loaded_modules'):
assert key in record_explicit
assert record_explicit['python']['version'] == record_implicit['python']['version']


def test_cli_mixin_defaults_keyword_extends_defaults():
"""--mixin defaults plus a default mixin on top works."""
_, record, _ = _run_main(['--mixin', 'defaults', 'working-dir', '--', 'true'])

# All defaults present
assert 'python' in record
assert 'host' in record
assert 'slurm' in record
assert 'loaded_modules' in record
# working-dir is already in defaults, so no duplicate effect needed — just check
assert 'working_dir' in record['call']


def test_cli_mixin_defaults_keyword_with_extra_mixin():
"""--mixin defaults file-hash produces defaults plus file-hash."""
_, record, _ = _run_main(['--mixin', 'defaults', 'peak-memory', '--', 'true'])

assert 'python' in record
assert 'host' in record
# peak-memory records to call.peak_memory_bytes
assert 'peak_memory_bytes' in record['call']


def test_cli_mixin_defaults_keyword_deduplicates():
"""Repeating defaults in --mixin does not produce duplicate mixins."""
_, record, _ = _run_main(['--mixin', 'defaults', 'defaults', '--', 'true'])

assert 'python' in record
assert 'host' in record


def test_cli_mixin_defaults_keyword_invalid_extra():
"""An unknown mixin alongside defaults exits non-zero."""
with pytest.raises(SystemExit) as exc:
main(['--mixin', 'defaults', 'no-such-mixin', '--', 'true'])
assert exc.value.code != 0


def test_cli_outfile(tmp_path):
"""--outfile writes JSONL to the specified file."""
outfile = tmp_path / 'results.jsonl'
Expand Down
Loading