Coverage for afasi/cli.py: 100.00%

29 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-21 13:35:37 +00:00

1#! /usr/bin/env python 

2# -*- coding: utf-8 -*- 

3# pylint: disable=line-too-long 

4"""Commandline API gateway for afasi.""" 

5import sys 

6 

7import typer 

8 

9import afasi 

10import afasi.afasi as af 

11 

12APP_NAME = 'Fuzz a language by mixing up only few words.' 

13APP_ALIAS = 'afasi' 

14app = typer.Typer( 

15 add_completion=False, 

16 context_settings={'help_option_names': ['-h', '--help']}, 

17 no_args_is_help=True, 

18) 

19 

20TEMPLATE_EXAMPLE_JSON = """\ 

21{ 

22 "table": { 

23 "description": "table level default constraints, row attributes do replace those if present.", 

24 "contra": [ 

25 "extracomment", 

26 "source" 

27 ], 

28 "count": 0, 

29 "flip_is_stop": true, 

30 "flip_flop": [ 

31 "<message id=\\"SOME_TRACK\\">", 

32 "</message>" 

33 ], 

34 "pro": [ 

35 "translation" 

36 ] 

37 }, 

38 "foo": "bar", 

39 "translations": [ 

40 { 

41 "repl": ">Lock", 

42 "ace": ">Launch" 

43 }, 

44 { 

45 "repl": ">Track", 

46 "ace": ">Lock" 

47 }, 

48 { 

49 "repl": ">Autotrack", 

50 "ace": ">Autolock" 

51 }, 

52 { 

53 "repl": "lock r", 

54 "ace": "launch r" 

55 }, 

56 { 

57 "repl": "track r", 

58 "ace": "lock r" 

59 } 

60 ] 

61} 

62""" 

63 

64TEMPLATE_EXAMPLE_YAML = """\ 

65--- 

66table: 

67 description: table level default constraints, row attributes do replace those if 

68 present. 

69 contra: 

70 - extracomment 

71 - source 

72 count: 0 

73 flip_is_stop: true 

74 flip_flop: 

75 - <message id="SOME_TRACK"> 

76 - </message> 

77 pro: 

78 - translation 

79foo: bar 

80translations: 

81- repl: ">Lock" 

82 ace: ">Launch" 

83- repl: ">Track" 

84 ace: ">Lock" 

85- repl: ">Autotrack" 

86 ace: ">Autolock" 

87- repl: lock r 

88 ace: launch r 

89- repl: track r 

90 ace: lock r 

91""" 

92 

93 

94@app.callback(invoke_without_command=True) 

95def callback( 

96 version: bool = typer.Option( 

97 False, 

98 '-V', 

99 '--version', 

100 help='Display the afasi version and exit', 

101 is_eager=True, 

102 ) 

103) -> None: 

104 """ 

105 Fuzz a language by mixing up only few words. 

106 

107 The translation table entries are applied in order per line of input. 

108 So, with large translation tables the performance will obviously degrade with a power of two. 

109 The latter should be taken as a hint to maintain both language files in separate entities not as a patch task. 

110 

111 The translation table is either an array of two element arrays provided as YAML or JSON and thus shall 

112 be in a shape like either: 

113 

114 \b 

115 [ 

116 ["repl", "ace"], 

117 ["als", "othis"] 

118 ] 

119 

120 or: 

121 

122 \b 

123 --- 

124 - - repl 

125 - ace 

126 - - als 

127 - othis 

128 

129 Or the table is given as an object providing more detailed instructions constraining the translation rules like: 

130 

131 \b 

132 * contra indicators - when given exempting a line from translation 

133 * pro indicators - when given marking a line for translation 

134 * flip_flop indicators - providing either stop-start (default) or start-stop state switching 

135 

136 The JSON object format is best understood when executing the template command and adapting the resulting JSON 

137 object written to standard out. 

138 

139 Default for input source is standard in and out per default is sent to standard out. 

140 """ 

141 if version: 

142 typer.echo(f'{APP_NAME} version {afasi.__version__}') 

143 raise typer.Exit() 

144 

145 

146@app.command('template') 

147def app_template( 

148 template_format: str = typer.Option( 

149 'yaml', 

150 '-f', 

151 '--format', 

152 help='Format of template', 

153 metavar='<translation-table>', 

154 ), 

155) -> int: 

156 """ 

157 Write a template of a translation table YAML or JSON structure to standard out and exit 

158 """ 

159 sys.stdout.write(TEMPLATE_EXAMPLE_JSON if template_format.lower() == 'json' else TEMPLATE_EXAMPLE_YAML) 

160 return sys.exit(0) 

161 

162 

163@app.command('translate') 

164def translate( 

165 source: str = typer.Argument(af.STDIN), 

166 target: str = typer.Argument(af.STDOUT), 

167 inp: str = typer.Option( 

168 '', 

169 '-i', 

170 '--input', 

171 help='Path to input file (default is reading from standard in)', 

172 metavar='<sourcepath>', 

173 ), 

174 out: str = typer.Option( 

175 '', 

176 '-o', 

177 '--output', 

178 help='Path to non-existing output file (default is writing to standard out)', 

179 metavar='<targetpath>', 

180 ), 

181 translation_table_path: str = typer.Option( 

182 '', 

183 '-t', 

184 '--table', 

185 help=( 

186 'Path to translation table file in YAML or JSON format.' 

187 '\nStructure of table data is [["repl", "ace"], ["als", "othis"]]' 

188 ), 

189 metavar='<translation table path>', 

190 ), 

191 dry: bool = typer.Option( 

192 False, 

193 '-n', 

194 '--dryrun', 

195 help='Flag to execute without writing the translation but a diff instead (default is False)', 

196 metavar='bool', 

197 ), 

198) -> int: 

199 """ 

200 Translate from a language to a 'langauge'. 

201 """ 

202 command = 'translate' 

203 incoming = inp if inp else (source if source != af.STDIN else '') 

204 outgoing = out if out else (target if target != af.STDOUT else '') 

205 dryrun = 'DRYRUN' if dry else '' 

206 action = [command, incoming, outgoing, translation_table_path, dryrun] 

207 return sys.exit(af.main(action)) 

208 

209 

210@app.command('version') 

211def app_version() -> None: 

212 """ 

213 Display the afasi version and exit 

214 """ 

215 callback(True)