cmdpackage 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,772 @@
1
+ #!/usr/bin/python
2
+ from string import Template
3
+ from textwrap import dedent
4
+
5
+ initFile = dedent("""import sys, os
6
+ from .classes.argParse import ArgParse
7
+ from .commands.cmdSwitchbord import cmdSwitchbord
8
+
9
+ def main():
10
+ #packName = os.path.basename(sys.argv[0])
11
+ argParse = ArgParse()
12
+ cmdSwitchbord(argParse)
13
+
14
+ if __name__ == '__main__':
15
+ main()
16
+ """)
17
+ cmdSwitchbordFileStr = dedent("""import sys, traceback
18
+ from argparse import Namespace
19
+ from ..defs.logIt import printIt, lable
20
+ from .commands import Commands
21
+ from .cmdOptSwitchbord import cmdOptSwitchbord
22
+ from ..classes.argParse import ArgParse
23
+
24
+ cmdObj = Commands()
25
+ commands = cmdObj.commands
26
+ switchFlags = cmdObj.switchFlags["switcheFlags"]
27
+
28
+ def cmdSwitchbord(argParse: ArgParse):
29
+ global commands
30
+ theCmd = 'notSet'
31
+ try:
32
+ if len(sys.argv) > 1:
33
+ if len(sys.argv) > 2:
34
+ switchFlagChk = sys.argv[2]
35
+ if len(sys.argv) == 3 and switchFlagChk[0] in '-+?':
36
+ if switchFlagChk[1:] in switchFlags.keys():
37
+ print(f'00001: {switchFlagChk}')
38
+ cmdOptSwitchbord(switchFlagChk, switchFlags)
39
+ else:
40
+ if switchFlagChk not in ["-h", "--help"]:
41
+ printIt(f'{switchFlagChk} not defined',lable.WARN)
42
+ else:
43
+ argParse.parser.print_help()
44
+ exit()
45
+ args: Namespace = argParse.args
46
+ theCmd = args.commands[0]
47
+ if theCmd in commands.keys():
48
+ exec(f'from ..commands.{theCmd} import {theCmd}')
49
+ exec(f'{theCmd}(argParse)')
50
+ else:
51
+ print(args)
52
+ printIt(f'Command "{theCmd}" not present.\\n',lable.ERROR)
53
+ argParse.parser.print_help()
54
+ else:
55
+ argParse.parser.print_help()
56
+ except Exception as e:
57
+ tb_str = ''.join(traceback.format_exception(None, e, e.__traceback__))
58
+ printIt(f'{theCmd}\\n{tb_str}', lable.ERROR)
59
+ exit()
60
+ """)
61
+ cmdOptSwitchbordFileStr = dedent("""from ..classes.optSwitches import OptSwitches
62
+
63
+ def cmdOptSwitchbord(switchFlag: str, switchFlags: str):
64
+ optSwitches = OptSwitches(switchFlags)
65
+ optSwitches.toggleSwitchFlag(switchFlag)
66
+ """)
67
+ newCmdTemplateStr = """from ..defs.logIt import printIt, lable, cStr, color
68
+ from .commands import Commands
69
+
70
+ cmdObj = Commands()
71
+ commands = cmdObj.commands
72
+
73
+ def ${defName}(argParse):
74
+ global commands
75
+ args = argParse.args
76
+ theCmd = args.commands[0]
77
+ theArgNames = list(commands[theCmd].keys())
78
+ theArgs = args.arguments
79
+ argIndex = 0
80
+ intOnly = True
81
+ printIt("Modify default behavour in ${packName}/commands/${defName}.py", lable.DEBUG)
82
+ # delete place holder code bellow that loops though arguments provided
83
+ # when this command is called when not needed.
84
+ # Note: that function having a name that is entered as an argument part
85
+ # of this code and is called using the built in exec function. while argIndex < len(theArgs):
86
+ while argIndex < len(theArgs):
87
+ anArg = theArgs[argIndex]
88
+ if anArg in commands[theCmd]:
89
+ intOnly = False
90
+ exec(f"{anArg}(argParse)")
91
+ elif intOnly: # starts out with intergr
92
+ printIt(f"{theArgNames[argIndex+1]}: {anArg}",lable.INFO)
93
+ argIndex += 1
94
+ if len(theArgs) == 0:
95
+ printIt("no argument(s) entered", lable.INFO)
96
+
97
+ """
98
+ argDefTemplateStr = dedent("""def ${argName}(argParse):
99
+ args = argParse.args
100
+ printIt(args, lable.INFO)
101
+
102
+ """)
103
+ newCmdStr = dedent("""import os, sys, copy
104
+ from ..defs.logIt import printIt, lable
105
+ from ..classes.argParse import ArgParse
106
+ from .commands import Commands, cmdDescriptionTagStr
107
+ from .templates.newCmd import cmdDefTemplate, argDefTemplate
108
+ import readline
109
+ readline.parse_and_bind('tab: compleat')
110
+ readline.parse_and_bind('set editing-mode vi')
111
+
112
+ def newCmd(argParse: ArgParse):
113
+ args = argParse.args
114
+ #cmd = args.commands
115
+ cmdObj = Commands()
116
+ argsDict = args.arguments
117
+ newCmdName = args.arguments[0]
118
+ if newCmdName not in cmdObj.commands.keys():
119
+ theArgs = verifyArgsWithDiscriptions(cmdObj, argsDict)
120
+ updateCMDJson(cmdObj, theArgs)
121
+ writeCodeFile(theArgs)
122
+ printIt(f'"{newCmdName}" added.',lable.NewCmd)
123
+ else:
124
+ printIt(f'"{newCmdName}" exists. use modCmd or rmCmd to modiify or remove this command.',lable.INFO)
125
+
126
+ def verifyArgsWithDiscriptions(cmdObj: Commands, theArgs) -> dict:
127
+ rtnDict = {}
128
+ argIndex = 0
129
+ cmdName = theArgs[argIndex]
130
+ while argIndex < len(theArgs):
131
+ argName = theArgs[argIndex]
132
+ if argName[0] == '-':
133
+ if len(argName) >= 2:
134
+ if argName[2] == '-':
135
+ printIt("Only single hyphen options allowed.",lable.WARN)
136
+ exit(0)
137
+ else:
138
+ theDisc = input(f'Enter help description for {argName}:\\n')
139
+ if theDisc == '': theDisc = f'no help for {argName}'
140
+ else:
141
+ printIt("Missing ascii letters after hyphen.",lable.WARN)
142
+ exit(0)
143
+ else:
144
+ theDisc = input(f'Enter help description for {argName}:\\n')
145
+ if theDisc == '': theDisc = f'no help for {argName}'
146
+ rtnDict[argName] = theDisc
147
+ argIndex += 1
148
+ return rtnDict
149
+
150
+ def writeCodeFile(theArgs: dict) -> str:
151
+ fileDir = os.path.dirname(__file__)
152
+ fileName = os.path.join(fileDir, f'{list(theArgs.keys())[0]}.py')
153
+ if os.path.isfile(fileName):
154
+ rtnStr = lable.EXISTS
155
+ else:
156
+ ourStr = cmdCodeBlock(theArgs)
157
+ with open(fileName, 'w') as fw:
158
+ fw.write(ourStr)
159
+ rtnStr = lable.SAVED
160
+ return rtnStr
161
+
162
+ def cmdCodeBlock(theArgs: dict) -> str:
163
+ packName = os.path.basename(sys.argv[0])
164
+ argNames = list(theArgs.keys())
165
+ cmdName = argNames[0]
166
+ defTemp = cmdDefTemplate
167
+ argTemp = argDefTemplate
168
+ rtnStr = defTemp.substitute(
169
+ packName=packName, defName=cmdName,
170
+ )
171
+ argIndex = 1
172
+ while argIndex < len(argNames): # add subarg functions
173
+ argName = argNames[argIndex]
174
+ rtnStr += argTemp.substitute(argName=argName)
175
+ argIndex += 1
176
+ return rtnStr
177
+
178
+ def updateCMDJson(cmdObj: Commands, theArgs: dict) -> None:
179
+ commands = copy.deepcopy(cmdObj.commands)
180
+ argNames = list(theArgs.keys())
181
+ defName = argNames[0]
182
+ defDiscription = theArgs[argNames[0]]
183
+ commands[defName] = {}
184
+ commands[defName][f'{defName}{cmdDescriptionTagStr}'] = defDiscription
185
+ argIndex = 1
186
+ while argIndex < len(theArgs): # add subarg functions
187
+ argName = argNames[argIndex]
188
+ commands[defName][argName] = theArgs[argName]
189
+ argIndex += 1
190
+ cmdObj.commands = commands
191
+ """)
192
+ modCmdStr = dedent("""import os, copy
193
+ from ..defs.logIt import printIt, lable
194
+ from ..classes.argParse import ArgParse
195
+ from .commands import Commands, cmdDescriptionTagStr
196
+ from .templates.newCmd import cmdDefTemplate, argDefTemplate
197
+ import readline
198
+ readline.parse_and_bind('tab: compleat')
199
+ readline.parse_and_bind('set editing-mode vi')
200
+
201
+ cmdObj = Commands()
202
+ commands = cmdObj.commands
203
+
204
+ def modCmd(argParse: ArgParse):
205
+ args = argParse.args
206
+ #cmd = args.commands
207
+ cmdObj = Commands()
208
+ modCmdName = args.arguments[0]
209
+ if modCmdName in cmdObj.commands.keys():
210
+ theArgs = verifyArgsWithDiscriptions(cmdObj, args.arguments)
211
+ if len(theArgs.keys()) > 0:
212
+ updateCMDJson(cmdObj, modCmdName, theArgs)
213
+ printIt(f'"{modCmdName}" modified.',lable.ModCmd)
214
+ else:
215
+ printIt(f'"{modCmdName}" unchanged.',lable.INFO)
216
+ else:
217
+ printIt(f'"{modCmdName}" does not exists. use newCmd or add it.',lable.INFO)
218
+
219
+ def verifyArgsWithDiscriptions(cmdObj: Commands, theArgs) -> dict:
220
+ rtnDict = {}
221
+ argIndex = 0
222
+ cmdName = theArgs[argIndex]
223
+ while argIndex < len(theArgs):
224
+ argName = theArgs[argIndex]
225
+ if argName[0] == '-':
226
+ if len(argName) >= 2:
227
+ if argName[2] == '-':
228
+ printIt("Only single hyphen options allowed.",lable.WARN)
229
+ exit(0)
230
+ else:
231
+ theDisc = input(f'Enter help description for {argName}:\\n')
232
+ if theDisc == '': theDisc = f'no help for {argName}'
233
+ else:
234
+ printIt("Missing ascii letters after hyphen.",lable.WARN)
235
+ exit(0)
236
+ else:
237
+ theDisc = ''
238
+ saveDisc = False
239
+ if argIndex == 0 and len(theArgs) == 1:
240
+ chgDisc = input(f'Replace description for {argName} (y/N): ')
241
+ if chgDisc.lower() == 'y':
242
+ saveDisc = True
243
+ elif argIndex > 0:
244
+ if argName in cmdObj.commands[cmdName].keys():
245
+ chgDisc = input(f'Replace description for {argName} (y/N): ')
246
+ if chgDisc.lower() == 'y':
247
+ saveDisc = True
248
+ else: # add new arg
249
+ saveDisc = True
250
+ newArg = True
251
+ if saveDisc:
252
+ theDisc = input(f'Enter help description for {argName}:\\n')
253
+ if theDisc == '': theDisc = f'no help for {argName}'
254
+ if saveDisc:
255
+ # only poulate rtnDict with modified discriptions
256
+ rtnDict[argName] = theDisc
257
+ argIndex += 1
258
+ return rtnDict
259
+
260
+ def writeCodeFile(theArgs: dict) -> str:
261
+ fileDir = os.path.dirname(__file__)
262
+ fileName = os.path.join(fileDir, f'{list(theArgs.keys())[0]}.py')
263
+ if os.path.isfile(fileName):
264
+ rtnStr = lable.EXISTS
265
+ else:
266
+ ourStr = cmdCodeBlock(theArgs)
267
+ with open(fileName, 'w') as fw:
268
+ fw.write(ourStr)
269
+ rtnStr = lable.SAVED
270
+ return rtnStr
271
+
272
+ def cmdCodeBlock(theArgs: dict) -> str:
273
+ argNames = list(theArgs.keys())
274
+ defName = argNames[0]
275
+ defTemp = cmdDefTemplate
276
+ argTemp = argDefTemplate
277
+ rtnStr = defTemp.substitute(
278
+ defName=defName
279
+ )
280
+ argIndex = 1
281
+ while argIndex < len(argNames): # add subarg functions
282
+ argName = argNames[argIndex]
283
+ rtnStr += argTemp.substitute(argName=theArgs[argName])
284
+ argIndex += 1
285
+ return rtnStr
286
+
287
+ def updateCMDJson(cmdObj: Commands, modCmdName: str, theArgs: dict) -> None:
288
+ commands = copy.deepcopy(cmdObj.commands)
289
+ argNames = list(theArgs.keys())
290
+ if modCmdName in argNames:
291
+ commands[modCmdName][f'{modCmdName}{cmdDescriptionTagStr}'] = theArgs[modCmdName]
292
+ argIndex = 1
293
+ else: argIndex = 0
294
+ while argIndex < len(theArgs): # add subarg functions
295
+ argName = argNames[argIndex]
296
+ commands[modCmdName][argName] = theArgs[argName]
297
+ argIndex += 1
298
+ cmdObj.commands = commands
299
+ """)
300
+ rmCmdStr = dedent("""import os, json
301
+ from ..defs.logIt import printIt, lable, cStr, color
302
+ from .commands import Commands
303
+
304
+ cmdObj = Commands()
305
+ commands = cmdObj.commands
306
+ theDir = os.path.dirname(os.path.realpath(__file__))
307
+ jsonFileName = os.path.join(theDir,'commands.json')
308
+
309
+ def rmCmd(argParse):
310
+ global commands
311
+ args = argParse.args
312
+ theArgs = args.arguments
313
+ argIndex = 0
314
+ cmdName = theArgs[argIndex]
315
+ while argIndex < len(theArgs):
316
+ anArg = theArgs[argIndex]
317
+ if anArg in commands and len(theArgs) == 1:
318
+ if anArg == cmdName:
319
+ if anArg in ["newCmd", "modCmd", "rmCmd"]:
320
+ printIt(f'Permission denied for "{anArg}".',lable.WARN)
321
+ exit(0)
322
+ chkRm: str = input(f"Perminantly delete {anArg} (y/N): ")
323
+ if chkRm == '': chkRm = 'N'
324
+ if chkRm.lower() == 'y':
325
+ removeCmd(anArg)
326
+ else:
327
+ printIt(f'Command "{anArg}" must be removed seperataly from "{cmdName}".',lable.WARN)
328
+ elif cmdName in commands:
329
+ if anArg in commands[cmdName]:
330
+ chkRm: str = input(f"Perminantly delete {anArg} (y/N): ")
331
+ if chkRm == '': chkRm = 'N'
332
+ if chkRm.lower() == 'y':
333
+ removeCmdArg(cmdName, anArg)
334
+ else:
335
+ printIt(f'"{cmdName}" is not currently a Command.',lable.WARN)
336
+ argIndex = len(theArgs)
337
+ argIndex += 1
338
+
339
+ def removeCmdArg(cmdName, argName):
340
+ global jsonFileName
341
+ with open(jsonFileName, 'r') as rf:
342
+ theJson = json.load(rf)
343
+ del theJson[cmdName][argName]
344
+ with open(jsonFileName, 'w') as wf:
345
+ json.dump(theJson, wf, indent=2)
346
+ printIt(argName,lable.RmArg)
347
+
348
+ def removeCmd(cmdName):
349
+ global jsonFileName
350
+ with open(jsonFileName, 'r') as rf:
351
+ theJson = json.load(rf)
352
+ del theJson[cmdName]
353
+ with open(jsonFileName, 'w') as wf:
354
+ json.dump(theJson, wf, indent=2)
355
+ pyFileName = f'{cmdName}.py'
356
+ pyFileName = os.path.join(theDir, pyFileName)
357
+ if os.path.isfile(pyFileName):
358
+ os.remove(pyFileName)
359
+ printIt(cmdName,lable.RmCmd)
360
+ """)
361
+ commandsJsonDict = {
362
+ "switcheFlags": {},
363
+ "newCmd": {
364
+ "newCmd_description": "Add new command <cmdName> with [argNames...]. Also creates a file cmdName.py.",
365
+ "cmdName": "Name of new command",
366
+ "argName": "(argName...), Optional names of argument to associate with the new command."
367
+ },
368
+ "modCmd": {
369
+ "modCmd_description": "Modify a command or argument discriptions, or add another argument for command. The cmdName.py file will not be modified.",
370
+ "cmdName": "Name of command being modified",
371
+ "argName": "(argName...) Optional names of argument(s) to modify."
372
+ },
373
+ "rmCmd": {
374
+ "rmCmd_description": "Remove <cmdName> and delete file cmdName.py, or remove an argument for a command.",
375
+ "cmdName": "Name of command to remove, cmdName.py and other commands listed as argument(s) will be delated.",
376
+ "argName": "Optional names of argument to remove.v It is and I am both pi."
377
+ }
378
+ }
379
+ commandsFileStr = dedent("""import json, os
380
+ from copy import copy
381
+ import inspect
382
+
383
+ cmdDescriptionTagStr = "_description"
384
+
385
+ class Commands(object):
386
+ def __init__(self) -> None:
387
+ self.cmdFileDir = os.path.dirname(inspect.getfile(self.__class__))
388
+ self.cmdFileName = os.path.join(self.cmdFileDir, "commands.json" )
389
+ with open(self.cmdFileName, "r") as fr:
390
+ rawJson = json.load(fr)
391
+ self._switchFlags = {}
392
+ try:
393
+ self._switchFlags["switcheFlags"] = copy(rawJson["switcheFlags"])
394
+ del rawJson["switcheFlags"]
395
+ except: self._switchFlags["switcheFlags"] = {}
396
+ self._commands = rawJson
397
+ self.checkForUpdates()
398
+
399
+ @property
400
+ def commands(self):
401
+ return self._commands
402
+
403
+ @commands.setter
404
+ def commands(self, aDict: dict):
405
+ self._commands = aDict
406
+ self._writeCmdJsonFile()
407
+
408
+ @property
409
+ def switchFlags(self):
410
+ return self._switchFlags
411
+
412
+ @switchFlags.setter
413
+ def switchFlags(self, aDict: dict):
414
+ self._switchFlags = aDict
415
+ self._writeCmdJsonFile()
416
+
417
+ def _writeCmdJsonFile(self):
418
+ # outJson = copy(self._switchFlags)
419
+ # outJson.update(self._commands)
420
+ outJson = self._switchFlags | self._commands
421
+ with open(self.cmdFileName, "w") as fw:
422
+ json.dump(outJson, fw, indent=2)
423
+
424
+ def checkForUpdates(self):
425
+ dirList = os.listdir(self.cmdFileDir)
426
+ for aFile in dirList:
427
+ if aFile[:-2] == "py":
428
+ chkName = aFile[:-3]
429
+ if chkName not in self.commands and chkName != "commands":
430
+ self.commands[chkName] = [" - No argument"]
431
+ """)
432
+ optSwitchesTemplate = Template(dedent("""import json
433
+ from pathlib import Path
434
+ from ..defs.logIt import printIt, lable
435
+
436
+
437
+ rcFileDir = Path(__file__).resolve().parents[2]
438
+ rcFileName = rcFileDir.joinpath(f'.${name}rc')
439
+
440
+ class OptSwitches():
441
+ def __init__(self, switchFlags: dict) -> None:
442
+ self.switchFlags = switchFlags
443
+ self.optSwitches = readOptSwitches()
444
+
445
+ def toggleSwitchFlag(self, switchFlag: str):
446
+ optSwitches = {}
447
+ optSwitches["switcheFlags"] = {}
448
+ currSwitchFlag = switchFlag[1:]
449
+ if switchFlag[0] in '+':
450
+ currSwitchValue = True # not (self.optSwitches["switcheFlags"][currSwitchFlag] == True)
451
+ else:
452
+ currSwitchValue = False
453
+ try:
454
+ self.optSwitches["switcheFlags"][currSwitchFlag] = currSwitchValue
455
+ except:
456
+ print('here')
457
+ self.optSwitches["switcheFlags"][currSwitchFlag] = True
458
+ writeOptJson(self.optSwitches, self.switchFlags)
459
+
460
+ def readOptSwitches() -> dict:
461
+ global rcFileName
462
+ optSwitches = {}
463
+ if rcFileName.is_file():
464
+ with open(rcFileName, 'r') as rf:
465
+ rawRcJson = json.load(rf)
466
+ optSwitches["switcheFlags"] = rawRcJson["switcheFlags"]
467
+ else:
468
+ optSwitches["switcheFlags"] = {}
469
+ return optSwitches
470
+
471
+ def writeOptJson(optSwitches: dict, switchFlags: dict) -> dict:
472
+ global rcFileName
473
+ rawRC = {}
474
+ if rcFileName.is_file():
475
+ with open(rcFileName, 'r') as rf:
476
+ rawRC = json.load(rf)
477
+ rawRC = rawRC | optSwitches
478
+ for switchFlag in switchFlags.keys(): # fill in missing items'
479
+ try: _ = rawRC["switcheFlags"][switchFlag]
480
+ except: rawRC["switcheFlags"][switchFlag] = False
481
+ printIt(formatOptStr(rawRC["switcheFlags"]), lable.INFO)
482
+ with open(rcFileName, 'w') as wf:
483
+ json.dump(rawRC, wf, indent=2)
484
+
485
+ def formatOptStr(optSwitches: dict) -> str:
486
+ rtnStr = "Current option values: "
487
+ for cmdOpt in optSwitches:
488
+ rtnStr += f'-{cmdOpt}={optSwitches[cmdOpt]}, '
489
+ rtnStr = rtnStr[:-2]
490
+ return rtnStr
491
+ """))
492
+ argParseTemplate = Template(dedent("""import os, sys, argparse, shlex
493
+ from ..defs.logIt import color, cStr
494
+ from ..commands.commands import Commands, cmdDescriptionTagStr
495
+
496
+ class PiHelpFormatter(argparse.RawTextHelpFormatter):
497
+ # Corrected _max_action_length for the indenting of subactions
498
+ def add_argument(self, action):
499
+ if action.help is not argparse.SUPPRESS:
500
+ # find all invocations
501
+ get_invocation = self._format_action_invocation
502
+ invocations = [get_invocation(action)]
503
+ current_indent = self._current_indent
504
+ for subaction in self._iter_indented_subactions(action):
505
+ # compensate for the indent that will be added
506
+ indent_chg = self._current_indent - current_indent
507
+ added_indent = 'x'*indent_chg
508
+ print('added_indent', added_indent)
509
+ invocations.append(added_indent+get_invocation(subaction))
510
+ #print('inv', invocations)
511
+
512
+ # update the maximum item length
513
+ invocation_length = max([len(s) for s in invocations])
514
+ action_length = invocation_length + self._current_indent
515
+ self._action_max_length = max(self._action_max_length,
516
+ action_length)
517
+
518
+ # add the item to the list
519
+ self._add_item(self._format_action, [action])
520
+
521
+ def str_or_int(arg):
522
+ try:
523
+ return int(arg) # try convert to int
524
+ except ValueError:
525
+ pass
526
+ if type(arg) == str:
527
+ return arg
528
+ raise argparse.ArgumentTypeError("arguments must be an integer or string")
529
+
530
+ class ArgParse():
531
+
532
+ def __init__(self):
533
+ if not sys.stdin.isatty():
534
+ self.parser = argparse.ArgumentParser(add_help=False)
535
+ self.parser.add_argument('commands', nargs=1)
536
+ self.parser.add_argument('arguments', nargs='*')
537
+ self.args = self.parser.parse_args(sys.argv[1:])
538
+ else:
539
+ _, tCols = os.popen('stty size', 'r').read().split()
540
+ tCols = int(tCols)
541
+ indentPad = 8
542
+ formatter_class=lambda prog: PiHelpFormatter(prog, max_help_position=8,width=tCols)
543
+ commandsHelp = ""
544
+ argumentsHelp = ""
545
+ theCmds = Commands()
546
+ commands = theCmds.commands
547
+ switchFlag = theCmds.switchFlags["switcheFlags"]
548
+ for cmdName in commands:
549
+ needCmdDescription = True
550
+ needArgDescription = True
551
+ arguments = commands[cmdName]
552
+ argumentsHelp += cStr(cmdName, color.YELLOW) + ': \\n'
553
+ for argName in arguments:
554
+ if argName[-len(cmdDescriptionTagStr):] == cmdDescriptionTagStr:
555
+ cmdHelp = cStr(cmdName, color.YELLOW) + ': ' + f'{arguments[argName]}'
556
+ if len(cmdHelp) > tCols:
557
+ indentPad = len(cmdName) + 2
558
+ cmdHelp = formatHelpWidth(cmdHelp, tCols, indentPad)
559
+ else:
560
+ cmdHelp += '\\n'
561
+ commandsHelp += cmdHelp
562
+ needCmdDescription = False
563
+ else:
564
+ argHelp = cStr(f' <{argName}> ', color.CYAN) + f'{arguments[argName]}'
565
+ if len(argHelp) > tCols:
566
+ indentPad = len(argName) + 5
567
+ argHelp = ' ' + formatHelpWidth(argHelp, tCols, indentPad)
568
+ else:
569
+ argHelp += '\\n'
570
+ argumentsHelp += argHelp
571
+ needArgDescription = False
572
+ if needArgDescription:
573
+ argumentsHelp = argumentsHelp[:-1]
574
+ argumentsHelp += "no arguments\\n"
575
+ if needCmdDescription:
576
+ commandsHelp += cStr(cmdName, color.WHITE) + '\\n'
577
+ # commandsHelp = commandsHelp[:-1]
578
+
579
+ self.parser = argparse.ArgumentParser(
580
+ description = "pi pip package pip package pip package",
581
+ epilog="Have Fun!", formatter_class=formatter_class)
582
+
583
+ self.parser.add_argument("commands",
584
+ type=str,
585
+ nargs=1,
586
+ metavar= f'{cStr(cStr("Commands", color.YELLOW), color.UNDERLINE)}:',
587
+ help=commandsHelp)
588
+
589
+ self.parser.add_argument("arguments",
590
+ type=str_or_int,
591
+ nargs="*",
592
+ metavar= f'{cStr(cStr("Arguments", color.CYAN), color.UNDERLINE)}:',
593
+ #metavar="arguments:",
594
+ help=argumentsHelp)
595
+
596
+ for optFlag in switchFlag:
597
+ flagHelp = switchFlag[optFlag]
598
+ self.parser.add_argument(f'-{optFlag}', action='store_true', help=flagHelp)
599
+ self.args = self.parser.parse_args()
600
+
601
+ def formatHelpWidth(theText, tCols, indentPad=1) -> str:
602
+ # this uses the screen with to estabhish tCols
603
+
604
+ #tCols = int(tCols) - 20
605
+ #print(tCols)
606
+ # tCols = 60
607
+ spPaddingStr = ' '*indentPad
608
+ rtnStr = ''
609
+ outLine = ''
610
+ tokens = shlex.split(theText)
611
+ # print(tokens)
612
+ # exit()
613
+ for token in tokens: # loop though tokens
614
+ chkStr = outLine + token + ' '
615
+ if len(chkStr) <= tCols: # check line length after concatinating each word
616
+ outLine = chkStr # less the the colums of copy over to outline
617
+ else:
618
+ if len(token) > tCols:
619
+ # when the match word is longer then the terminal character width (tCols),
620
+ # DEBUG how it should be handeled here.
621
+ print(f'here with long match.group():\\n{token}')
622
+ exit()
623
+ chkStr = token
624
+ while len(chkStr) > tCols: # a single word may be larger the tCols
625
+ outLine += chkStr[:tCols]
626
+ chkStr = f'\\n{chkStr[tCols:]}'
627
+ outLine += chkStr
628
+ else:
629
+ rtnStr += outLine
630
+ outLine = f'\\n{spPaddingStr}{token} '
631
+ rtnStr += f'{outLine}\\n'
632
+ #rtnStr = rtnStr[:-1]
633
+ return rtnStr
634
+ """))
635
+ logPrintTemplate = Template(dedent("""import os, time
636
+ from inspect import currentframe, getframeinfo
637
+
638
+ # Class of different termianl styles
639
+ class color():
640
+
641
+ BLACK = "\\033[30m"
642
+ RED = "\\033[31m"
643
+ GREEN = "\\033[32m"
644
+ YELLOW = "\\033[33m"
645
+ BLUE = "\\033[34m"
646
+ MAGENTA = "\\033[35m"
647
+ CYAN = "\\033[36m"
648
+ WHITE = "\\033[37m"
649
+ UNDERLINE = "\\033[4m"
650
+ RESET = "\\033[0m"
651
+
652
+ # message: color
653
+ l2cDict: dict = {
654
+ "BK": RESET,
655
+ "ERROR: ": RED,
656
+ "PASS: ": GREEN,
657
+ "WARN: ": YELLOW,
658
+ "SAVED: ": BLUE,
659
+ "DEBUG: ": MAGENTA,
660
+ "REPLACED: ": CYAN,
661
+ "INFO: ": WHITE,
662
+ "IMPORT: ": UNDERLINE,
663
+ "RESET": RESET,
664
+ "File Not Found: ": YELLOW,
665
+ "FAIL: ": RED,
666
+ "Useage: ": WHITE,
667
+ "DELETE: ": YELLOW,
668
+ "EXISTS: ": GREEN,
669
+ "READ: ": GREEN,
670
+ "TOUCHED: ": GREEN,
671
+ "MKDIR: ": GREEN,
672
+ "NEW CMD ADDED: ": GREEN,
673
+ "CMD MODIFIED: ": GREEN,
674
+ "CMD REMOVED: ": GREEN,
675
+ "ARG REMOVED: ": GREEN,
676
+ "IndexError: ": RED,
677
+ "Testing: ": CYAN,
678
+ "Update: ": CYAN
679
+ }
680
+
681
+
682
+ class lable():
683
+ SAVED = "SAVED: "
684
+ REPLACED = "REPLACED: "
685
+ BLANK = "BK"
686
+ ERROR = "ERROR: "
687
+ PASS = "PASS: "
688
+ WARN = "WARN: "
689
+ DEBUG = "DEBUG: "
690
+ INFO = "INFO: "
691
+ IMPORT = "IMPORT: "
692
+ RESET = "RESET"
693
+ FileNotFound = "File Not Found: "
694
+ FAIL = "FAIL: "
695
+ Useage = "Useage: "
696
+ MKDIR = "MKDIR: "
697
+ DELETE = "DELETE: "
698
+ EXISTS = "EXISTS: "
699
+ READ = "READ: "
700
+ TOUCHED = "TOUCHED: "
701
+ NewCmd = "NEW CMD ADDED: "
702
+ ModCmd = "CMD MODIFIED: "
703
+ RmCmd = "CMD REMOVED: "
704
+ RmArg = "ARG REMOVED: "
705
+ IndexError = "IndexError: "
706
+ TESTING = "Testing: "
707
+ UPDATE = "Update: "
708
+
709
+
710
+ # log function
711
+ def logIt(*message, logFileName="${name}.log"):
712
+ # write log
713
+ now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
714
+
715
+ prtStr = ""
716
+ needClip = False
717
+ if len(message) > 0:
718
+ for mess in message:
719
+ if mess == lable.BLANK:
720
+ pass
721
+ elif mess in color.l2cDict:
722
+ prtStr = mess + prtStr
723
+ else:
724
+ needClip = True
725
+ prtStr += str(mess) + " "
726
+ if needClip:
727
+ prtStr = prtStr[:-1]
728
+
729
+ prtStr = "["+now+"] "+prtStr+"\\n"
730
+
731
+ with open(logFileName, "a") as f:
732
+ f.write(prtStr)
733
+
734
+ def printIt(*message):
735
+ prtStr = ""
736
+ rtnStr = ""
737
+ needClip = False
738
+ if len(message) > 0:
739
+ for mess in message:
740
+ if mess == lable.BLANK:
741
+ prtStr += message
742
+ rtnStr += message
743
+ elif mess in color.l2cDict:
744
+ prtStr = color.l2cDict[mess] + mess + color.RESET + prtStr
745
+ rtnStr = mess + rtnStr
746
+ else:
747
+ needClip = True
748
+ prtStr += str(mess) + " "
749
+ rtnStr += str(mess) + " "
750
+ if needClip:
751
+ prtStr = prtStr[:-1]
752
+ rtnStr = rtnStr[:-1]
753
+ print(prtStr)
754
+ return rtnStr
755
+
756
+ def cStr(inStr:str, cVal:str):
757
+ return cVal + inStr + color.RESET
758
+
759
+ def deleteLog(logFileName="${name}.log"):
760
+ if os.path.isfile(logFileName): os.remove(logFileName)
761
+
762
+ def getCodeFile():
763
+ cf = currentframe()
764
+ codeObj =cf.f_back.f_code
765
+ codeObjStr = str(codeObj).split(",")[1].split(\'"\')[1]
766
+ codeObjStr = os.path.basename(codeObjStr)
767
+ return codeObjStr
768
+
769
+ def getCodeLine():
770
+ cf = currentframe()
771
+ return cf.f_back.f_lineno
772
+ """))