|
1 | 1 | """Adapter for argparse CLI framework.""" |
2 | 2 |
|
3 | 3 | from argparse import _VersionAction, ArgumentParser, Namespace |
| 4 | +from collections.abc import Callable |
4 | 5 | from dataclasses import dataclass, field |
5 | | -from datetime import datetime |
| 6 | +from datetime import UTC, datetime |
| 7 | +from functools import wraps |
6 | 8 | from pathlib import Path |
7 | 9 | from typing import Any |
8 | 10 | import logging |
@@ -235,7 +237,7 @@ def collect_record_info_from_argparse( |
235 | 237 | return program, ioargs |
236 | 238 |
|
237 | 239 |
|
238 | | -def record_with_argparse( |
| 240 | +def record_argparse( |
239 | 241 | parser: ArgumentParser, |
240 | 242 | ns: Namespace, |
241 | 243 | ios: IOArgumentNames, |
@@ -288,3 +290,70 @@ def record_with_argparse( |
288 | 290 | current_user=current_user, |
289 | 291 | dataset_license=dataset_license, |
290 | 292 | ) |
| 293 | + |
| 294 | + |
| 295 | +def recorded_argparse[T]( |
| 296 | + parser: ArgumentParser, |
| 297 | + input_dirs: list[str] | None = None, |
| 298 | + output_dirs: list[str] | None = None, |
| 299 | + input_files: list[str] | None = None, |
| 300 | + output_files: list[str] | None = None, |
| 301 | + dataset_license: str | None = None, |
| 302 | + enabled_argument: str | None = None, |
| 303 | +) -> Callable[[Callable[[Namespace], T]], Callable[[Namespace], T]]: |
| 304 | + """Decorator to record a CLI invocation in an RO-Crate using argparse. |
| 305 | +
|
| 306 | + Args: |
| 307 | + parser: The argument parser used to parse the command-line arguments. |
| 308 | + This is needed to extract program information and help texts for the arguments. |
| 309 | + input_dirs: List of argument names representing input directories |
| 310 | + output_dirs: List of argument names representing output directories |
| 311 | + input_files: List of argument names representing input files |
| 312 | + output_files: List of argument names representing output files |
| 313 | + dataset_license: License string for the dataset (e.g., "CC BY 4.0"). |
| 314 | + If None, no license is recorded. |
| 315 | + enabled_argument: Name of the attribute in args that indicates whether |
| 316 | + to record the invocation. Records if None. |
| 317 | + If provided, the invocation is only recorded if getattr(args, enabled_argument) is truthy. |
| 318 | +
|
| 319 | + Returns: |
| 320 | + Decorator function |
| 321 | +
|
| 322 | + Raises: |
| 323 | + ValueError: |
| 324 | + If the current user cannot be determined. |
| 325 | + If the specified paths are outside the crate root. |
| 326 | + If the software version cannot be determined based on the program name. |
| 327 | + MissingDestArgparseSubparserError: |
| 328 | + If parser has subparsers but dest is not set. |
| 329 | + """ |
| 330 | + |
| 331 | + def decorator(func: Callable[[Namespace], T]) -> Callable[[Namespace], T]: |
| 332 | + @wraps(func) |
| 333 | + def wrapper(args: Namespace) -> T: |
| 334 | + start_datetime = datetime.now(tz=UTC) |
| 335 | + |
| 336 | + result = func(args) |
| 337 | + |
| 338 | + if enabled_argument is None or getattr(args, enabled_argument, False): |
| 339 | + end_time = datetime.now(tz=UTC) |
| 340 | + ios = IOArgumentNames( |
| 341 | + input_dirs=input_dirs or [], |
| 342 | + output_dirs=output_dirs or [], |
| 343 | + input_files=input_files or [], |
| 344 | + output_files=output_files or [], |
| 345 | + ) |
| 346 | + record_argparse( |
| 347 | + parser=parser, |
| 348 | + ns=args, |
| 349 | + ios=ios, |
| 350 | + start_time=start_datetime, |
| 351 | + end_time=end_time, |
| 352 | + dataset_license=dataset_license, |
| 353 | + ) |
| 354 | + |
| 355 | + return result |
| 356 | + |
| 357 | + return wrapper |
| 358 | + |
| 359 | + return decorator |
0 commit comments