Coverage for taksonomia/cli.py: 88.75%

54 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-04 23:02:14 +00:00

1"""CLI operations for taxonomy (Finnish: taksonomia) of a folder tree, guided by conventions.""" 

2 

3import argparse 

4import pathlib 

5import sys 

6from typing import List, Union 

7 

8import taksonomia.taksonomia as api 

9from taksonomia import APP_ALIAS, APP_NAME, APP_VERSION, KNOWN_FORMATS, KNOWN_KEY_FUNCTIONS, parse_csl 

10 

11 

12def parse_request(argv: List[str]) -> Union[int, argparse.Namespace]: 

13 """DRY.""" 

14 parser = argparse.ArgumentParser( 

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

16 ) 

17 parser.add_argument( 

18 '--tree-root', 

19 '-t', 

20 dest='tree_root', 

21 default='', 

22 help='Root of the tree to visit. Optional\n(default: positional tree root value)', 

23 required=False, 

24 ) 

25 parser.add_argument( 

26 'tree_root_pos', nargs='?', default='', help='Root of the tree to visit. Optional (default: PWD)' 

27 ) 

28 parser.add_argument( 

29 '--excludes', 

30 '-x', 

31 dest='excludes', 

32 default='', 

33 help='comma separated list of values to exclude paths\ncontaining the substring (default: empty string)', 

34 ) 

35 parser.add_argument( 

36 '--key-function', 

37 '-k', 

38 dest='key_function', 

39 default='blake2', 

40 help=( 

41 'key function (blake2, elf, md5) for branches and leaves\n' 

42 '(default: blake2) use elf only for very small taxonomies' 

43 ), 

44 ) 

45 parser.add_argument( 

46 '--out-path', 

47 '-o', 

48 dest='out_path', 

49 default=sys.stdout, 

50 help='output file path (stem) for taxonomy (default: STDOUT)', 

51 ) 

52 parser.add_argument( 

53 '--formats', 

54 '-f', 

55 dest='format_type_csl', 

56 default='json', 

57 help='formats (json, xml, yaml) as comma separated list for taxonomy (default: json)', 

58 ) 

59 parser.add_argument( 

60 '--base64-encode', 

61 '-e', 

62 dest='base64_encode', 

63 default=False, 

64 action='store_true', 

65 help='encode taxonomy in base64 (default: False)\nincompatible with option --xz-compress', 

66 ) 

67 parser.add_argument( 

68 '--xz-compress', 

69 '-c', 

70 dest='xz_compress', 

71 default=False, 

72 action='store_true', 

73 help='compress taxonomy per LZMA(xz) (default: False)\nincompatible with option --base64-encode', 

74 ) 

75 parser.add_argument( 

76 '--version', 

77 '-V', 

78 dest='version_request', 

79 default=False, 

80 action='store_true', 

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

82 required=False, 

83 ) 

84 

85 if not argv: 

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

87 parser.print_help() 

88 return 0 

89 

90 options = parser.parse_args(argv) 

91 

92 if options.version_request: 92 ↛ 93line 92 didn't jump to line 93, because the condition on line 92 was never true

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

94 return 0 

95 

96 if not options.tree_root: 

97 if options.tree_root_pos: 97 ↛ 100line 97 didn't jump to line 100, because the condition on line 97 was never false

98 options.tree_root = options.tree_root_pos 

99 else: 

100 options.tree_root = str(pathlib.Path.cwd()) 

101 

102 if options.key_function.lower() not in KNOWN_KEY_FUNCTIONS: 

103 parser.error( 

104 f'requested key function {options.key_function} for branches and leaves not in {KNOWN_KEY_FUNCTIONS}' 

105 ) 

106 

107 format_types = parse_csl(options.format_type_csl) 

108 channel_count = len(format_types) 

109 for fmt in format_types: 

110 if fmt not in KNOWN_FORMATS: 

111 parser.error(f'requested format {fmt} for taxonomy dump not in {KNOWN_FORMATS}') 

112 

113 if options.out_path is sys.stdout and channel_count > 1: 113 ↛ 114line 113 didn't jump to line 114, because the condition on line 113 was never true

114 parser.error('writing multiple formats to STDOUT is not supported.') 

115 

116 if options.base64_encode and options.xz_compress: 

117 parser.error('the options --base64-encode and --xz-compress are mutually exclusive.') 

118 

119 if options.xz_compress and options.out_path is sys.stdout: 

120 parser.error('compression for now not supported for standard output (only for files)') 

121 

122 tree_root = pathlib.Path(options.tree_root) 

123 if tree_root.exists(): 

124 if tree_root.is_dir(): 

125 return options 

126 parser.error(f'requested tree root at ({tree_root}) is not a folder') 

127 

128 parser.error(f'requested tree root at ({tree_root}) does not exist') 

129 

130 

131def main(argv: Union[List[str], None] = None) -> int: 

132 """Delegate processing to functional module.""" 

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

134 options = parse_request(argv) 

135 if isinstance(options, int): 135 ↛ 136line 135 didn't jump to line 136, because the condition on line 135 was never true

136 return 0 

137 return api.main(options)