107
107
-----
108
108
109
109
- https://github.com/mjg59/ssh_pki
110
-
111
- - Should we use this? No. Are we going to? Yes.
112
110
"""
113
111
import os
114
112
import sys
115
113
import pathlib
114
+ import argparse
116
115
import importlib
117
116
import contextlib
118
117
import dataclasses
@@ -159,89 +158,41 @@ def parse(contents: str):
159
158
except Exception as yaml_parse_error :
160
159
raise yaml_parse_error from json_parse_error
161
160
162
- from pprint import pprint
163
-
164
- # Known parser mapping
165
- parse = {
166
- (
167
- "tps.manifest" ,
168
- "0.0.0" ,
169
- "dataflow" ,
170
- ): self .parse_my_document_format_0_0_0_dataflow
171
- }
172
- # Grab mapped parser
173
- document_format_version_output_mode = (
174
- doc .get ("$document_format" , None ),
175
- doc .get ("$document_version" , None ),
176
- doc .get ("$document_version" , None ),
177
- )
178
- parser = parse .get (document_format_version , None )
179
-
180
- if parser is None :
181
- raise Exception (
182
- f"Unknown document format/version pair: { document_format_version } "
183
- )
184
-
185
- print ()
186
- pprint (doc )
187
- print ()
188
- parser (doc )
189
-
190
- def parse_my_document_format_0_0_0_dataflow (self , doc ):
191
- pass
192
-
193
-
194
- @dataclasses .dataclass
195
- class ManifestFormatParser :
196
- """
197
- Read in configuration to determine what the next phase of parsing is.
198
-
199
- args holds arguments passed to target.
200
- """
201
-
202
- format_name : str
203
- version : str
204
- output : str
205
- action : str
206
- target : str
207
- args : str = ""
208
-
209
161
210
- ENV_PREFIX = "TPS_MANIFEST_"
211
-
212
-
213
- def environ_discover_dataclass (
162
+ def discover_dataclass_environ (
214
163
dataclass ,
164
+ prefix : str ,
215
165
environ : Dict [str , str ] = None ,
216
166
* ,
217
- prefix : str = ENV_PREFIX ,
218
167
dataclass_key : str = None ,
219
168
):
220
169
r"""
221
170
>>> import dataclasses
222
- >>> from dffml.util.testing.manifest.shim import environ_discover_dataclass
171
+ >>> from dffml.util.testing.manifest.shim import discover_dataclass_environ
223
172
>>>
224
173
>>> @dataclasses.dataclass
225
174
... class MyDataclass:
226
175
... name: str
227
176
... version: str
177
+ ...
178
+ ... PREFIX = "MYPREFIX_"
228
179
>>>
229
- >>> environ_discover_dataclass (
180
+ >>> discover_dataclass_environ (
230
181
... MyDataclass,
182
+ ... MyDataclass.PREFIX,
231
183
... {
232
184
... "MYPREFIX_NAME_EXAMPLE_FORMAT": "Example Format",
233
185
... "MYPREFIX_VERSION_EXAMPLE_FORMAT": "0.0.1",
234
186
... },
235
- ... prefix="MYPREFIX_",
236
187
... )
237
188
{'example_format': MyDataclass(name='Example Format', version='0.0.1')}
238
189
>>>
239
- >>> environ_discover_dataclass (
190
+ >>> discover_dataclass_environ (
240
191
... MyDataclass,
192
+ ... MyDataclass.PREFIX,
241
193
... {
242
194
... "MYPREFIX_VERSION_EXAMPLE_FORMAT": "0.0.1",
243
195
... },
244
- ... prefix="MYPREFIX_",
245
196
... dataclass_key="name",
246
197
... )
247
198
{'example_format': MyDataclass(name='example_format', version='0.0.1')}
@@ -265,11 +216,144 @@ def environ_discover_dataclass(
265
216
return discovered_parsers
266
217
267
218
268
- def shim (manifest : str , lockdown : bool , strict : bool ):
269
- parsers = environ_discover_dataclass (
270
- ManifestFormatParser , dataclass_key = "format_name" , environ = os .environ
219
+ @dataclasses .dataclass
220
+ class ManifestFormatParser :
221
+ """
222
+ Read in configuration to determine what the next phase of parsing is.
223
+
224
+ args holds arguments passed to target.
225
+ """
226
+
227
+ format_name : str
228
+ version : str
229
+ output : str
230
+ action : str
231
+ target : str
232
+ args : str = ""
233
+
234
+ PREFIX : str = "TPS_MANIFEST_PARSER_"
235
+ DATACLASS_KEY : str = "format_name"
236
+
237
+
238
+ def input_action_print (
239
+ args : argparse .Namespace , parser : ManifestFormatParser , manifest : Dict
240
+ ):
241
+ """
242
+ Print the formated version of the action.
243
+ """
244
+ return print ()
245
+
246
+
247
+ def input_action_exec (
248
+ args : argparse .Namespace , parser : ManifestFormatParser , manifest : Dict
249
+ ):
250
+ """
251
+ Execute the action via :py:func:`os.execvp`.
252
+ """
253
+ raise NotImplementedError ()
254
+
255
+
256
+ FORMAT_PARSER_ACTIONS = {
257
+ "print" : input_action_print ,
258
+ "exec" : input_action_exec ,
259
+ }
260
+
261
+
262
+ def input_action_stdin (args : argparse .Namespace ):
263
+ """
264
+ Read manifest from stdin
265
+ """
266
+ return sys .stdin .read ()
267
+
268
+
269
+ def input_action_read_text (args : argparse .Namespace ):
270
+ """
271
+ Read manifest from target filepath as text
272
+ """
273
+ if args .input_target is None :
274
+ raise ValueError ("input target must be filepath but was None" )
275
+ input_target_path = pathlib .Path (args .input_target )
276
+ if not input_target_path .exists ():
277
+ raise ValueError (
278
+ f"input target must be filepath but was { args .input_target !r} "
279
+ )
280
+ return input_target_path .read_text ()
281
+
282
+
283
+ INPUT_ACTIONS = {
284
+ "stdin" : input_action_stdin ,
285
+ "read_text" : input_action_read_text ,
286
+ }
287
+
288
+
289
+ def shim (
290
+ args : argparse .Namespace , environ : Dict [str , str ] = None ,
291
+ ):
292
+ r"""
293
+
294
+ **TODO** Find code that sends all rest of args to target (QEMU?)
295
+
296
+ >>> import dataclasses
297
+ >>> import sys
298
+ >>> import types
299
+ >>> import pprint
300
+ >>> from dffml.util.testing.manifest.shim import shim
301
+ >>>
302
+ >>> @dataclasses.dataclass
303
+ ... class MyDataclass:
304
+ ... name: str
305
+ ... version: str
306
+ ...
307
+ ... PREFIX = "MYPREFIX_"
308
+ >>>
309
+ >>> shim(
310
+ ... types.SimpleNamespace(
311
+ ... action,
312
+ ... ),
313
+ ... {
314
+ ... "TPS_MANIFSET_ACTION_ONE": "print",
315
+ ... "TPS_MANIFSET_ACTION_TWO": "print",
316
+ ... }
317
+ ... )
318
+ """
319
+ # Discover options for format parsers for next phase
320
+ parsers = {
321
+ (parser .format_name , parser .version , parser .action )
322
+ for parser in discover_dataclass_environ (
323
+ ManifestFormatParser ,
324
+ ManifestFormatParser .PREFIX ,
325
+ dataclass_key = ManifestFormatParser .DATACLASS_KEY ,
326
+ environ = os .environ ,
327
+ )
328
+ }
329
+ # Determine how to get the manifest
330
+ input_action = INPUT_ACTIONS [args .input_action ]
331
+ # Get the manifest
332
+ contents = input_action (args )
333
+ # Validate the manfiest
334
+ if not args .insecure :
335
+ # TODO Implement validation
336
+ pass
337
+ if args .only_validate :
338
+ # Bail if we are only validating the manfest and not parsing it
339
+ return
340
+ # Parse the manifest
341
+ manifest = parse (contents )
342
+ # Grab mapped parser
343
+ format_version_action = (
344
+ manifest .get ("$document_format" , None ),
345
+ manifest .get ("$document_version" , None ),
346
+ args .action ,
271
347
)
272
- print (parsers )
348
+ if format_version_action not in parsers :
349
+ raise Exception (
350
+ f"Unknown document format/version/action combination: { format_version_action } : { parsers !r} "
351
+ )
352
+ parser = parsers [format_version_action ]
353
+ # Determine how to get the manifest
354
+ action = FORMAT_PARSER_ACTIONS [parser .action ]
355
+ # Send manifest to next phase
356
+ action (args , parser , manifest )
273
357
274
358
275
359
def make_parser ():
@@ -280,24 +364,46 @@ def make_parser():
280
364
)
281
365
282
366
parser .add_argument (
283
- "-l" , "--lockdown" , type = bool , action = "store_true" , default = False ,
367
+ "-l" , "--lockdown" , action = "store_true" , default = False ,
368
+ )
369
+ parser .add_argument (
370
+ "-s" , "--strict" , action = "store_true" , default = False ,
284
371
)
285
372
parser .add_argument (
286
- "-s" , "--strict" , type = argparse .FileType ("r" ), default = sys .stdin
373
+ "--insecure" ,
374
+ action = "store_true" ,
375
+ default = False ,
376
+ help = f"Skip manifest validation (validation currently unsupported)" ,
287
377
)
288
378
parser .add_argument (
289
- "-i" , "--input" , type = argparse .FileType ("r" ), default = sys .stdin
379
+ "--only-validate" ,
380
+ action = "store_true" ,
381
+ default = False ,
382
+ help = f"Exit after validating the manifest (validation currently unsupported)" ,
290
383
)
291
384
parser .add_argument (
292
- "-o" , "--output" , type = argparse .FileType ("w" ), default = sys .stdout
385
+ "--input-action" ,
386
+ choices = INPUT_ACTIONS .keys (),
387
+ default = "read_text" ,
388
+ help = f"Method for aquiring manifest" ,
389
+ )
390
+ parser .add_argument (
391
+ "--input-target" ,
392
+ help = "External callable to run which provides manifest" ,
393
+ )
394
+ parser .add_argument (
395
+ "--input-args" ,
396
+ help = "Arguments for input action if externally callable" ,
293
397
)
294
- parser .add_argument ("-n" , "--name" , help = "Name of function to replace" )
295
398
return parser
296
399
297
400
298
401
def main ():
299
402
parser = make_parser ()
300
403
args = parser .parse_args ()
301
- args .output .write (
302
- replace_function (args .input .read (), args .name , args .func .read ()) + "\n "
303
- )
404
+
405
+ shim (args )
406
+
407
+
408
+ if __name__ == "__main__" :
409
+ main ()
0 commit comments