handofcatsでchoicesをサポートした

github.com

handofcatsでchoicesをサポートした。NewTypeで作った型にchoicesというattributeを追加してあげる感じで使う(experimental)。

詳細

以下の様なコードが

dump.py

import sys
import typing as t
from handofcats import as_command


def csv_dump(rows: t.Sequence[dict]) -> None:
    import csv
    w = csv.DictWriter(sys.stdout, ["name", "age"])
    w.writeheader()
    w.writerows(rows)


def json_dump(rows: t.Sequence[dict]) -> None:
    import json
    json.dump(rows, sys.stdout, indent=2, ensure_ascii=False)
    sys.stdout.write("\n")


DumpFormat = t.NewType("DumpFormat", str)
DumpFormat.choices = ["json", "csv"]  # これが重要

@as_command
def run(*, format: DumpFormat = "json"):
    rows = [
        {
            "name": "foo",
            "age": 20,
        },
        {
            "name": "bar",
            "age": 21,
        },
    ]
    dump = globals()["{}_dump".format(format)]
    dump(rows)

NewTypeのchoicesがコマンドライン引数のchoicesとして扱われる。

$ python dump.py -h
usage: dump.py [-h] [--expose] [--format {json,csv}]

optional arguments:
  -h, --help           show this help message and exit
  --expose
  --format {json,csv}  (default: 'json')

--exposeでその部分のコードが生える。

$ dump.py --expose > dump-exposed.py

差分。

--- dump.py  2018-08-30 09:00:06.055747417 +0900
+++ dump-exposed.py   2018-08-30 08:55:15.765228110 +0900
@@ -1,6 +1,5 @@
 import sys
 import typing as t
-from handofcats import as_command
 
 
 def csv_dump(rows: t.Sequence[dict]) -> None:
@@ -19,7 +18,7 @@
 DumpFormat = t.NewType("DumpFormat", str)
 DumpFormat.choices = ["json", "csv"]
 
-@as_command
+
 def run(*, format: DumpFormat = "json"):
     rows = [
         {
@@ -33,3 +32,15 @@
     ]
     dump = globals()["{}_dump".format(format)]
     dump(rows)
+
+def main(argv=None):
+    import argparse
+    parser = argparse.ArgumentParser(description=None)
+    parser.print_usage = parser.print_help
+    parser.add_argument('--format', choices=['json', 'csv'], default='json', help="(default: 'json')", required=False)
+    args = parser.parse_args(argv)
+    run(**vars(args))
+
+
+if __name__ == '__main__':
+    main()