From ec4750b5e2f801fec7e1100299114483d4a04f4e Mon Sep 17 00:00:00 2001 From: Wolfgang Müller Date: Sat, 26 Oct 2024 20:59:35 +0200 Subject: Use getopt-based parser for command-line arguments Upcoming commits will want to make use of bespoke argument handling alongside subcommands and using Python's argparse proved unable to easily handle that kind of complexity. Therefore switch to the more basic getopt-based parser with which we can implement our own logic. Without argparse we're no longer bound to having an autogenerated help listing, so stop mentioning that in the manual. Instead have later(1) output a concise usage listing when given an unknown argument. Since we'll be adding more commands to later(1) in the future, also specify an explicit "list" command. --- later | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- later.1 | 20 +++++++++------ 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/later b/later index a5a25c8..3eb9ea0 100755 --- a/later +++ b/later @@ -1,13 +1,14 @@ #!/usr/bin/env python3 -import argparse import datetime +import getopt import json import os import re import sys -from dataclasses import dataclass +from dataclasses import dataclass, field from datetime import datetime as dt +from typing import Protocol class YTDLPLogger: @@ -25,6 +26,18 @@ def exit(message=""): sys.exit(f"later: {message}" if message else 0) +def usage(message=""): + lines = [] + if message: + lines.append(f"later: {message}") + lines.append("") + + lines.append("usage: later [options] [list]") + lines.append("options: [-u | --update-titles]") + + sys.exit("\n".join(lines)) + + def get_xdg(directory, fallback): env = f"XDG_{directory.upper()}_HOME" if env in os.environ: @@ -138,6 +151,54 @@ class TitleMap: exit(f"cannot write title cache: {err}") +class CommandFunction(Protocol): + def __call__(self, args: "Arguments", title_map: TitleMap) -> None: + pass + + +@dataclass +class Command: + name: str + fun: CommandFunction + implies_list: bool = True + args: bool = True + + +@dataclass +class Arguments: + command: Command + update_titles: bool = False + rest: list[str] = field(default_factory=list) + + +def parse_args(argv, commands, default): + try: + options, args = getopt.gnu_getopt(argv, "u", "update-titles") + except getopt.GetoptError as e: + usage(e) + + parsed_args = Arguments(command=commands[default]) + + for option in options: + match option: + case ("-u", _) | ("--update-titles", _): + parsed_args.update_titles = True + + if args: + if args[0] in commands: + parsed_args.command = commands[args.pop(0)] + + parsed_args.rest = args + + if parsed_args.rest and not parsed_args.command.args: + usage(f'unexpected argument for "{parsed_args.command.name}"') + + if not parsed_args.rest and parsed_args.command.args: + usage(f'"{parsed_args.command.name}" requires an argument') + + return parsed_args + + def entries(title_map): def get_mtime(entry): return entry.stat().st_mtime @@ -163,20 +224,20 @@ def entries(title_map): yield WatchLaterEntry(name=name, path=entry.path, mtime=mtime) -parser = argparse.ArgumentParser( - prog="later", description="List mpv's watch_later entries" -) -parser.add_argument( - "-u", - "--update-titles", - action="store_true", - help="update titles of videos using yt-dlp", -) -args = parser.parse_args() +def list_entries(args, title_map): + for entry in entries(title_map): + print(entry.format(title_map)) + + +commands = {"list": Command("list", list_entries, implies_list=False, args=False)} + +args = parse_args(sys.argv[1:], commands, "list") title_map = TitleMap(title_map_file, update=args.update_titles) -for entry in entries(title_map): - print(entry.format(title_map)) +args.command.fun(args=args, title_map=title_map) + +if args.command.implies_list: + list_entries(args, title_map) title_map.maybe_commit() diff --git a/later.1 b/later.1 index 4464da5..a4be6b0 100644 --- a/later.1 +++ b/later.1 @@ -1,4 +1,4 @@ -.Dd September 20, 2024 +.Dd October 26, 2024 .Dt LATER 1 .Os .Sh NAME @@ -6,12 +6,11 @@ .Nd list mpv's watch_later entries .Sh SYNOPSIS .Nm -.Op Fl u | \-update-titles -.Nm -.Fl h | \-help +.Op options +.Op Ic list .Sh DESCRIPTION .Nm -lists all watch_later entries as saved by +is a program to manage watch_later entries as created by .Xr mpv 1 . .Pp In order to display file names and URLs for an entry, the @@ -19,12 +18,17 @@ In order to display file names and URLs for an entry, the option must be set in .Xr mpv 1 . .Pp -The options are as follows: +The following options can be given for any command: .Bl -tag -width "-h, --update-titles" .It Fl u , Fl \-update-titles update titles of videos using yt-dlp -.It Fl h , Fl \-help -show a concise help message +.El +.Pp +The commands are as follows: +.Bl -tag -width Ds +.It Sy list +Lists all watch_later entries. +This command is implied if there are no remaining arguments. .El .Sh FILES .Bl -tag -width "1" -- cgit v1.2.3-2-gb3c3