Coverage for tallipoika/cli.py: 86.67%
51 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 23:32:02 +00:00
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 23:32:02 +00:00
1"""JSON Canonicalization Scheme (JCS) serializer - command line interface."""
3import argparse
4import _io # type: ignore
5import json
6import pathlib
7import sys
8from typing import Union, no_type_check
10import tallipoika.api as api
11from tallipoika import (
12 APP_ALIAS,
13 APP_NAME,
14 ENCODING,
15 VERSION,
16)
19@no_type_check
20def parse_request(argv: list[str]) -> Union[int, argparse.Namespace]:
21 """Implementation of command line API returning parsed request."""
22 parser = argparse.ArgumentParser(
23 prog=APP_ALIAS, description=APP_NAME, formatter_class=argparse.RawTextHelpFormatter
24 )
25 parser.add_argument(
26 '--in-path',
27 '-i',
28 dest='in_path',
29 default='',
30 help='Path to the file to transform. Optional\n(default: positional path value)',
31 required=False,
32 )
33 parser.add_argument(
34 'in_path_pos', nargs='?', default=sys.stdin, help='Path to the file to transform. Optional (default: STDIN)'
35 )
36 parser.add_argument(
37 '--out-path',
38 '-o',
39 dest='out_path',
40 default=sys.stdout,
41 help='output file path for transformed file (default: STDOUT)',
42 )
43 parser.add_argument(
44 '--serialize-only',
45 '-s',
46 dest='serialize_only',
47 default=False,
48 action='store_true',
49 help='serialize only i.e. do not sort keys (default: False)',
50 )
51 parser.add_argument(
52 '--version',
53 '-V',
54 dest='version_request',
55 default=False,
56 action='store_true',
57 help='show version of the app and exit',
58 required=False,
59 )
61 options = parser.parse_args(argv)
63 if options.version_request:
64 print(f'{APP_NAME} version {VERSION}')
65 return 0
67 if not options.in_path:
68 if options.in_path_pos: 68 ↛ 71line 68 didn't jump to line 71, because the condition on line 68 was never false
69 options.in_path = options.in_path_pos
70 else:
71 options.in_path = sys.stdin
73 if options.in_path is not sys.stdin:
74 in_path = pathlib.Path(options.in_path)
75 if in_path.exists():
76 if in_path.is_file():
77 return options
78 parser.error(f'requested source ({in_path}) is not a file')
79 parser.error(f'requested source ({in_path}) does not exist')
81 return options
84def process(options: argparse.Namespace) -> int:
85 """Visit the source and yield the requested transformed target."""
86 if isinstance(options.in_path, _io.TextIOWrapper): 86 ↛ 87line 86 didn't jump to line 87, because the condition on line 86 was never true
87 loaded = options.in_path.read()
88 else:
89 in_path = pathlib.Path(options.in_path)
90 with open(in_path, 'r', encoding=ENCODING) as source:
91 loaded = source.read()
93 transformed = api.canonicalize(json.loads(loaded)) if options.serialize_only else api.serialize(json.loads(loaded))
95 if isinstance(options.out_path, _io.TextIOWrapper): 95 ↛ 98line 95 didn't jump to line 98, because the condition on line 95 was never false
96 options.out_path.write(transformed.decode())
97 else:
98 out_path = pathlib.Path(options.out_path)
99 with open(out_path, 'wb') as target:
100 target.write(transformed)
102 return 0
105def app(argv: Union[list[str], None] = None) -> int:
106 """Delegate processing to functional module."""
107 argv = sys.argv[1:] if argv is None else argv
108 options = parse_request(argv)
109 if isinstance(options, int):
110 return 0
111 return process(options)