Coverage for etiketti / cli.py: 91.94%

44 statements  

« prev     ^ index     » next       coverage.py v7.13.2, created at 2026-02-03 22:57:11 +00:00

1"""Command line interface for labeling.""" 

2 

3import argparse 

4import pathlib 

5import sys 

6 

7import etiketti.implementation as impl 

8from etiketti import ( 

9 APP_ALIAS, 

10 APP_NAME, 

11 APP_VERSION, 

12 CONFIG_PATH_STRING, 

13 SOURCE_NAME_PATH_STRING, 

14 TARGET_NAME_PATH_STRING, 

15 parse_key_value_pair_csl, 

16) 

17 

18 

19def parse_request(argv: list[str]) -> int | argparse.Namespace: 

20 """DRY.""" 

21 parser = argparse.ArgumentParser( 

22 prog=APP_ALIAS, description=APP_NAME, formatter_class=argparse.RawTextHelpFormatter 

23 ) 

24 parser.add_argument( 

25 '--in-path', 

26 '-i', 

27 dest='in_pdf', 

28 default='', 

29 help='input file path to pdf file to label', 

30 required=False, 

31 ) 

32 parser.add_argument( 

33 'in_pdf_pos', 

34 nargs='?', 

35 default=SOURCE_NAME_PATH_STRING, 

36 help='input file path to pdf file to label', 

37 ) 

38 parser.add_argument( 

39 '--out-path', 

40 '-o', 

41 dest='out_pdf', 

42 default=TARGET_NAME_PATH_STRING, 

43 help='output path for resulting labeled pdf file', 

44 required=False, 

45 ) 

46 parser.add_argument( 

47 '--config', 

48 '-c', 

49 dest='cfg_path', 

50 default=CONFIG_PATH_STRING, 

51 help='configuration file for label context data', 

52 required=False, 

53 ) 

54 parser.add_argument( 

55 '--enforce', 

56 '-e', 

57 dest='enforce', 

58 default=False, 

59 action='store_true', 

60 help='enforce labels by overwriting the source file', 

61 required=False, 

62 ) 

63 parser.add_argument( 

64 '--version', 

65 '-V', 

66 dest='version_request', 

67 default=False, 

68 action='store_true', 

69 help='show version of the app and exit', 

70 required=False, 

71 ) 

72 parser.add_argument( 

73 '--key-value-pairs', 

74 '-k', 

75 dest='key_value_pair_csl', 

76 default='', 

77 help='key value pairs as comma separated list of key=value pairs (default: empty string)', 

78 ) 

79 

80 if not argv: 

81 print(f'{APP_NAME} version {APP_VERSION}') 

82 parser.print_help() 

83 return 0 

84 

85 options = parser.parse_args(argv) 

86 

87 if options.version_request: 87 ↛ 88line 87 didn't jump to line 88 because the condition on line 87 was never true

88 print(f'{APP_NAME} version {APP_VERSION}') 

89 return 0 

90 

91 if not options.in_pdf: 

92 if options.in_pdf_pos: 92 ↛ 95line 92 didn't jump to line 95 because the condition on line 92 was always true

93 options.in_pdf = options.in_pdf_pos 

94 else: 

95 options.in_pdf = SOURCE_NAME_PATH_STRING 

96 

97 options.kv_pairs = parse_key_value_pair_csl(options.key_value_pair_csl) 

98 

99 in_pdf = pathlib.Path(options.in_pdf) 

100 if in_pdf.exists(): 

101 if in_pdf.is_file(): 

102 cfg_path = pathlib.Path(options.cfg_path) 

103 if cfg_path.exists(): 

104 if cfg_path.is_file(): 

105 return options 

106 parser.error(f'configuration path ({cfg_path}) is not a file') 

107 parser.error(f'configuration path ({cfg_path}) does not exist') 

108 parser.error(f'requested pdf at ({in_pdf}) is not a file') 

109 parser.error(f'requested pdf at ({in_pdf}) does not exist') 

110 

111 

112def app(argv: list[str] | None = None) -> int: 

113 """Delegate processing to functional module.""" 

114 argv = sys.argv[1:] if argv is None else argv 

115 options = parse_request(argv) 

116 if isinstance(options, int): 

117 return 0 

118 return impl.patch(options)