# =========================================================================== #
# File: parser.py #
# Author: Pfesesani V. van Zyl #
# Email: pfesi24@gmail.com #
# =>========================================================================= #
# Library imports
# --------------------------------------------------------------------------- #
import argparse
from pathlib import Path
from dran.config.constants import VERSION
from dran.utils.fs import resolve_existing_path_without_logger
# =========================================================================== #
def _positive_int(value: str) -> int:
"""
Parse and validate a positive integer for argparse input.
Converts the provided string to an integer and ensures the value
is strictly greater than zero.
Args:
value (str): The input string received from the command line.
Returns:
int: The validated positive integer.
Raises:
argparse.ArgumentTypeError:
- If the value cannot be converted to an integer.
- If the parsed integer is less than or equal to zero.
Notes:
Designed for use as the `type` argument in argparse.add_argument(),
enforcing positive integer constraints at parse time.
"""
try:
parsed = int(value)
except ValueError as exc:
raise argparse.ArgumentTypeError("Must be an integer.") from exc
if parsed <= 0:
raise argparse.ArgumentTypeError("Must be a positive integer.")
return parsed
[docs]
def build_parser() -> argparse.ArgumentParser:
"""
Construct and return the DRAN command-line interface parser.
This parser defines all supported CLI options for running the DRAN
(Drift-scan Reduction and Analysis) system. It configures processing
inputs, runtime behavior, operating modes, logging,
database interaction, and web serving parameters.
Supported capabilities include:
- FITS file or directory ingestion via --path
- Debug logging control
- Optional persistence of plot/lightcurve data to the database
- Multiple operating modes:
auto : Automatically process data
gui : Launch desktop GUI interface
web : Launch web interface
anal : Run analysis-only mode
docs : Generate or serve documentation
serve : Start backend service (private to author)
- Web server port configuration
- Custom working/results directory
- Version reporting
Returns
-------
argparse.ArgumentParser
A fully configured parser ready for argument parsing.
"""
parser = argparse.ArgumentParser(
prog="DRAN",
description="Begin processing HartRAO drift-scan data from the Hart 26m telescope.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"-path",
"--path",
type=Path,
required=False,
help="Path to a FITS file or directory containing FITS files.",
)
parser.add_argument(
"--debug",
action="store_true",
default=False,
help="Enable debug logging.",
)
parser.add_argument(
"--saveplotstodb",
action="store_true",
default=False,
help="Save plot data / lightcurves in database. Bloats the database.",
)
parser.add_argument(
"-mode",
"--mode",
choices=["auto", "gui", "web", "anal", "docs", "serve"],
default="auto",
help="Operating mode.",
)
parser.add_argument(
"-port",
"--port",
type=int,
default=4000,
help="Port number for web interface.",
)
parser.add_argument(
"-workdir",
"--workdir",
type=Path,
default=Path("DRAN_RESULTS"),
help="Working/results directory.",
)
parser.add_argument(
"-v",
action="version",
version="%(prog)s " + VERSION,
)
parser.add_argument(
"--remote-paths",
dest="remote_paths",
action="append",
default=[],
help=(
"Remote path to process. Repeat for multiple paths. "
"Examples: --remote-paths Continuum "
"--remote-paths data/Calibrators "
"--remote-paths /data/pks0454-234"
),
)
return parser
[docs]
def normalize_args(args: argparse.Namespace) -> argparse.Namespace:
"""Normalize parsed command-line arguments.
Resolves path and workdir to validated existing filesystem paths and
returns the updated namespace.
"""
if isinstance(args.path, Path):
args.path = resolve_existing_path_without_logger(args.path)
if isinstance(args.workdir, Path):
args.workdir = resolve_existing_path_without_logger(args.workdir)
return args
[docs]
def validate_args(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
"""
Validate command-line arguments and enforce required constraints.
Parameters
----------
args : argparse.Namespace
Parsed command-line arguments.
parser : argparse.ArgumentParser
Argument parser instance used to raise user-friendly CLI errors.
Raises
------
SystemExit
Triggered via parser.error() if:
- mode="auto" and no path is provided.
- The provided path does not exist.
Notes
-----
- Expands user home shortcuts (e.g. "~") before checking existence.
- Validation errors terminate execution with a clear CLI message.
"""
if args.mode == "auto" and args.path is None:
parser.error("Missing required argument: -path/--path (required for mode=auto)")
if isinstance(args.path, Path):
path_obj = args.path.expanduser()
if not path_obj.exists():
parser.error(f"Path does not exist: {path_obj}")
[docs]
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
"""Parse command-line arguments and return a normalized namespace.
Builds the argument parser, parses the provided argument list or
sys.argv, and applies post-processing to ensure consistent argument values.
"""
parser = build_parser()
args = parser.parse_args(argv)
args = normalize_args(args)
validate_args(args, parser)
return args