p3lib 1.1.108__py2.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.
- p3lib/__init__.py +0 -0
- p3lib/ate.py +108 -0
- p3lib/bokeh_auth.py +363 -0
- p3lib/bokeh_gui.py +845 -0
- p3lib/boot_manager.py +420 -0
- p3lib/conduit.py +145 -0
- p3lib/database_if.py +289 -0
- p3lib/file_io.py +154 -0
- p3lib/gnome_desktop_app.py +146 -0
- p3lib/helper.py +420 -0
- p3lib/json_networking.py +239 -0
- p3lib/login.html +98 -0
- p3lib/mqtt_rpc.py +240 -0
- p3lib/netif.py +226 -0
- p3lib/netplotly.py +223 -0
- p3lib/ngt.py +841 -0
- p3lib/pconfig.py +874 -0
- p3lib/ssh.py +935 -0
- p3lib/table_plot.py +675 -0
- p3lib/uio.py +574 -0
- p3lib-1.1.108.dist-info/LICENSE +21 -0
- p3lib-1.1.108.dist-info/METADATA +34 -0
- p3lib-1.1.108.dist-info/RECORD +24 -0
- p3lib-1.1.108.dist-info/WHEEL +4 -0
p3lib/uio.py
ADDED
@@ -0,0 +1,574 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import sys
|
4
|
+
import os
|
5
|
+
import re
|
6
|
+
import traceback
|
7
|
+
import platform
|
8
|
+
from threading import Lock
|
9
|
+
from socket import socket, AF_INET, SOCK_DGRAM
|
10
|
+
from getpass import getpass, getuser
|
11
|
+
from time import strftime, localtime
|
12
|
+
from datetime import datetime
|
13
|
+
|
14
|
+
from p3lib.netif import NetIF
|
15
|
+
|
16
|
+
class UIO(object):
|
17
|
+
"""@brief responsible for user output and input via stdout/stdin"""
|
18
|
+
|
19
|
+
DISPLAY_ATTR_RESET = 0
|
20
|
+
DISPLAY_ATTR_BRIGHT = 1
|
21
|
+
DISPLAY_ATTR_DIM = 2
|
22
|
+
DISPLAY_ATTR_UNDERSCORE = 4
|
23
|
+
DISPLAY_ATTR_BLINK = 5
|
24
|
+
DISPLAY_ATTR_REVERSE = 7
|
25
|
+
DISPLAY_ATTR_HIDDEN = 8
|
26
|
+
|
27
|
+
DISPLAY_ATTR_FG_BLACK = 30
|
28
|
+
DISPLAY_ATTR_FG_RED = 31
|
29
|
+
DISPLAY_ATTR_FG_GREEN = 32
|
30
|
+
DISPLAY_ATTR_FG_YELLOW = 33
|
31
|
+
DISPLAY_ATTR_FG_BLUE = 34
|
32
|
+
DISPLAY_ATTR_FG_MAGNETA = 35
|
33
|
+
DISPLAY_ATTR_FG_CYAN = 36
|
34
|
+
DISPLAY_ATTR_FG_WHITE = 37
|
35
|
+
|
36
|
+
DISPLAY_ATTR_BG_BLACK = 40
|
37
|
+
DISPLAY_ATTR_BG_RED = 41
|
38
|
+
DISPLAY_ATTR_BG_GREEN = 42
|
39
|
+
DISPLAY_ATTR_BG_YELLOW = 43
|
40
|
+
DISPLAY_ATTR_BG_BLUE = 44
|
41
|
+
DISPLAY_ATTR_BG_MAGNETA = 45
|
42
|
+
DISPLAY_ATTR_BG_CYAN = 46
|
43
|
+
DISPLAY_ATTR_BG_WHITE = 47
|
44
|
+
|
45
|
+
DISPLAY_RESET_ESCAPE_SEQ = "\x1b[0m"
|
46
|
+
|
47
|
+
PROG_BAR_LENGTH = 40
|
48
|
+
|
49
|
+
USER_LOG_SYM_LINK = "log.txt"
|
50
|
+
DEBUG_LOG_SYM_LINK = "debug_log.txt"
|
51
|
+
|
52
|
+
@staticmethod
|
53
|
+
def GetInfoEscapeSeq():
|
54
|
+
"""@return the info level ANSI escape sequence."""
|
55
|
+
return "\x1b[{:01d};{:02d}m".format(UIO.DISPLAY_ATTR_FG_GREEN, UIO.DISPLAY_ATTR_BRIGHT)
|
56
|
+
|
57
|
+
@staticmethod
|
58
|
+
def GetDebugEscapeSeq():
|
59
|
+
"""@return the debug level ANSI escape sequence."""
|
60
|
+
return "\x1b[{:01d};{:02d};{:02d}m".format(UIO.DISPLAY_ATTR_FG_BLACK, UIO.DISPLAY_ATTR_BG_WHITE, UIO.DISPLAY_ATTR_BRIGHT)
|
61
|
+
|
62
|
+
@staticmethod
|
63
|
+
def GetWarnEscapeSeq():
|
64
|
+
"""@return the warning level ANSI escape sequence."""
|
65
|
+
return "\x1b[{:01d};{:02d}m".format(UIO.DISPLAY_ATTR_FG_RED, UIO.DISPLAY_ATTR_BRIGHT)
|
66
|
+
|
67
|
+
@staticmethod
|
68
|
+
def GetErrorEscapeSeq():
|
69
|
+
"""@return the warning level ANSI escape sequence."""
|
70
|
+
return "\x1b[{:01d};{:02d}m".format(UIO.DISPLAY_ATTR_FG_RED, UIO.DISPLAY_ATTR_BLINK)
|
71
|
+
|
72
|
+
@staticmethod
|
73
|
+
def RemoveEscapeSeq(text):
|
74
|
+
"""@brief Remove ANSI escape sequences that maybe present in text.
|
75
|
+
@param text A string that may contain ANSI escape sequences.
|
76
|
+
@return The text with any ANSI escape sequences removed."""
|
77
|
+
escapeSeq =re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
|
78
|
+
return escapeSeq.sub('', text)
|
79
|
+
|
80
|
+
def __init__(self, debug=False, colour=True):
|
81
|
+
self._debug = debug
|
82
|
+
self._colour = colour
|
83
|
+
self._logFile = None
|
84
|
+
self._progBarSize = 0
|
85
|
+
self._progBarGrow = True
|
86
|
+
self._debugLogEnabled = False
|
87
|
+
self._debugLogFile = None
|
88
|
+
self._symLinkDir = None
|
89
|
+
self._sysLogEnabled = False
|
90
|
+
self._sysLogHost = None
|
91
|
+
self._syslogProgramName = None
|
92
|
+
|
93
|
+
def logAll(self, enabled):
|
94
|
+
"""@brief Turn on/off the logging of all output including debug output even if debugging is off."""
|
95
|
+
self._debugLogEnabled = enabled
|
96
|
+
|
97
|
+
def enableDebug(self, enabled):
|
98
|
+
"""@brief Enable/Disable debugging
|
99
|
+
@param enabled If True then debugging is enabled"""
|
100
|
+
self._debug = enabled
|
101
|
+
|
102
|
+
def isDebugEnabled(self):
|
103
|
+
"""@return True if debuggin is eenabled."""
|
104
|
+
return self._debug
|
105
|
+
|
106
|
+
def info(self, text, highlight=False):
|
107
|
+
"""@brief Present an info level message to the user.
|
108
|
+
@param text The line of text to be presented to the user."""
|
109
|
+
if self._colour:
|
110
|
+
if highlight:
|
111
|
+
self._print('{}INFO: {}{}'.format(UIO.GetInfoEscapeSeq(), text, UIO.DISPLAY_RESET_ESCAPE_SEQ))
|
112
|
+
else:
|
113
|
+
self._print('{}INFO{}: {}'.format(UIO.GetInfoEscapeSeq(), UIO.DISPLAY_RESET_ESCAPE_SEQ, text))
|
114
|
+
else:
|
115
|
+
self._print('INFO: {}'.format(text))
|
116
|
+
self._update_syslog(PRIORITY.INFO, "INFO: "+text)
|
117
|
+
|
118
|
+
def debug(self, text):
|
119
|
+
"""@brief Present a debug level message to the user if debuging is enabled.
|
120
|
+
@param text The line of text to be presented to the user."""
|
121
|
+
if self._debug:
|
122
|
+
if self._colour:
|
123
|
+
self._print('{}DEBUG{}: {}'.format(UIO.GetDebugEscapeSeq(), UIO.DISPLAY_RESET_ESCAPE_SEQ, text))
|
124
|
+
else:
|
125
|
+
self._print('DEBUG: {}'.format(text))
|
126
|
+
elif self._debugLogEnabled and self._debugLogFile:
|
127
|
+
if self._colour:
|
128
|
+
self.storeToDebugLog('{}DEBUG{}: {}'.format(UIO.GetDebugEscapeSeq(), UIO.DISPLAY_RESET_ESCAPE_SEQ, text))
|
129
|
+
else:
|
130
|
+
self.storeToDebugLog('DEBUG: {}'.format(text))
|
131
|
+
self._update_syslog(PRIORITY.DEBUG, "DEBUG: "+text)
|
132
|
+
|
133
|
+
def warn(self, text):
|
134
|
+
"""@brief Present a warning level message to the user.
|
135
|
+
@param text The line of text to be presented to the user."""
|
136
|
+
if self._colour:
|
137
|
+
self._print('{}WARN{}: {}'.format(UIO.GetWarnEscapeSeq(), UIO.DISPLAY_RESET_ESCAPE_SEQ, text))
|
138
|
+
else:
|
139
|
+
self._print('WARN: {}'.format(text))
|
140
|
+
self._update_syslog(PRIORITY.WARNING, "WARN: "+text)
|
141
|
+
|
142
|
+
def error(self, text):
|
143
|
+
"""@brief Present an error level message to the user.
|
144
|
+
@param text The line of text to be presented to the user."""
|
145
|
+
if self._colour:
|
146
|
+
self._print('{}ERROR{}: {}'.format(UIO.GetErrorEscapeSeq(), UIO.DISPLAY_RESET_ESCAPE_SEQ, text))
|
147
|
+
else:
|
148
|
+
self._print('ERROR: {}'.format(text))
|
149
|
+
self._update_syslog(PRIORITY.ERROR, "ERROR: "+text)
|
150
|
+
|
151
|
+
def _print(self, text):
|
152
|
+
"""@brief Print text to stdout"""
|
153
|
+
self.storeToLog(text)
|
154
|
+
if self._debugLogEnabled and self._debugLogFile:
|
155
|
+
self.storeToDebugLog(text)
|
156
|
+
print(text)
|
157
|
+
|
158
|
+
def getInput(self, prompt, noEcho=False, stripEOL=True):
|
159
|
+
"""@brief Get a line of text from the user.
|
160
|
+
@param noEcho If True then * are printed when each character is pressed.
|
161
|
+
@param stripEOL If True then all end of line (\r, \n) characters are stripped.
|
162
|
+
@return The line of text entered by the user."""
|
163
|
+
if self._colour:
|
164
|
+
if noEcho:
|
165
|
+
prompt = "{}INPUT{}: ".format(UIO.GetInfoEscapeSeq(), UIO.DISPLAY_RESET_ESCAPE_SEQ) + prompt + ": "
|
166
|
+
self.storeToLog(prompt, False)
|
167
|
+
response = getpass(prompt, sys.stdout)
|
168
|
+
|
169
|
+
else:
|
170
|
+
prompt = "{}INPUT{}: ".format(UIO.GetInfoEscapeSeq(), UIO.DISPLAY_RESET_ESCAPE_SEQ) + prompt + ": "
|
171
|
+
self.storeToLog(prompt, False)
|
172
|
+
response = input(prompt)
|
173
|
+
|
174
|
+
else:
|
175
|
+
if noEcho:
|
176
|
+
prompt = "INPUT: " + prompt + ": "
|
177
|
+
self.storeToLog(prompt, False)
|
178
|
+
response = getpass(prompt, sys.stdout)
|
179
|
+
|
180
|
+
else:
|
181
|
+
prompt = "INPUT: " + prompt + ": "
|
182
|
+
self.storeToLog(prompt, False)
|
183
|
+
response = input(prompt)
|
184
|
+
|
185
|
+
if stripEOL:
|
186
|
+
response = response.rstrip('\n')
|
187
|
+
response = response.rstrip('\r')
|
188
|
+
|
189
|
+
self.storeToLog(response)
|
190
|
+
return response
|
191
|
+
|
192
|
+
def getBoolInput(self, prompt, allowQuit=True):
|
193
|
+
"""@brief Get boolean repsonse from user (y or n response).
|
194
|
+
@param allowQuit If True and the user enters q then the program will exit.
|
195
|
+
@return True or False"""
|
196
|
+
while True:
|
197
|
+
response = self.getInput(prompt=prompt)
|
198
|
+
if response.lower() == 'y':
|
199
|
+
return True
|
200
|
+
elif response.lower() == 'n':
|
201
|
+
return False
|
202
|
+
elif allowQuit and response.lower() == 'q':
|
203
|
+
sys.exit(0)
|
204
|
+
|
205
|
+
def _getNumericInput(self, floatValue, prompt, allowQuit=True, radix=10, minValue=None, maxValue=None):
|
206
|
+
"""@brief Get a decimal int number from the user.
|
207
|
+
@param floatValue If True a float value is returned. If False an int value is returned.
|
208
|
+
@param allowQuit If True and the user enters q then the program will exit.
|
209
|
+
@param radix The radix of the number entered (default=10). Only used if inputting an int value.
|
210
|
+
@param minValue The minimum acceptable value. If left at None then any value is accepted.
|
211
|
+
@param maxValue The maximum acceptable value. If left at None then any value is accepted.
|
212
|
+
@return True or False"""
|
213
|
+
while True:
|
214
|
+
response = self.getInput(prompt=prompt)
|
215
|
+
try:
|
216
|
+
if floatValue:
|
217
|
+
value = float(response)
|
218
|
+
else:
|
219
|
+
value = int(response, radix)
|
220
|
+
|
221
|
+
if minValue is not None and value < minValue:
|
222
|
+
self.warn(f"The minimum acceptable value is {minValue}")
|
223
|
+
|
224
|
+
if maxValue is not None and value > maxValue:
|
225
|
+
self.warn(f"The mximum acceptable value is {maxValue}")
|
226
|
+
|
227
|
+
return value
|
228
|
+
|
229
|
+
except ValueError:
|
230
|
+
if floatValue:
|
231
|
+
self.warn("%s is not a valid float value." % (response))
|
232
|
+
|
233
|
+
else:
|
234
|
+
self.warn("%s is not a valid integer value." % (response))
|
235
|
+
|
236
|
+
if allowQuit and response.lower() == 'q':
|
237
|
+
return None
|
238
|
+
|
239
|
+
def getIntInput(self, prompt, allowQuit=True, radix=10, minValue=None, maxValue=None):
|
240
|
+
"""@brief Get a decimal int number from the user.
|
241
|
+
@param allowQuit If True and the user enters q then the program will exit.
|
242
|
+
@param radix The radix of the number entered (default=10).
|
243
|
+
@param minValue The minimum acceptable value. If left at None then any value is accepted.
|
244
|
+
@param maxValue The maximum acceptable value. If left at None then any value is accepted.
|
245
|
+
@return True or False"""
|
246
|
+
return self._getNumericInput(False, prompt, allowQuit=allowQuit, radix=radix, minValue=minValue, maxValue=maxValue)
|
247
|
+
|
248
|
+
def getFloatInput(self, prompt, allowQuit=True, radix=10, minValue=None, maxValue=None):
|
249
|
+
"""@brief Get a float number from the user.
|
250
|
+
@param allowQuit If True and the user enters q then the program will exit.
|
251
|
+
@param radix The radix of the number entered (default=10).
|
252
|
+
@param minValue The minimum acceptable value. If left at None then any value is accepted.
|
253
|
+
@param maxValue The maximum acceptable value. If left at None then any value is accepted.
|
254
|
+
@return True or False"""
|
255
|
+
return self._getNumericInput(True, prompt, allowQuit=allowQuit, radix=radix, minValue=minValue, maxValue=maxValue)
|
256
|
+
|
257
|
+
def errorException(self):
|
258
|
+
"""@brief Show an exception traceback if debugging is enabled"""
|
259
|
+
if self._debug:
|
260
|
+
lines = traceback.format_exc().split('\n')
|
261
|
+
for l in lines:
|
262
|
+
self.error(l)
|
263
|
+
|
264
|
+
def getPassword(self, prompt):
|
265
|
+
"""@brief Get a password from a user.
|
266
|
+
@param prompt The user prompt.
|
267
|
+
@return The password entered."""
|
268
|
+
return self.getInput(prompt, noEcho=True)
|
269
|
+
|
270
|
+
def setSymLinkDir(self, symLinkDir):
|
271
|
+
"""@brief Set a shortcut location for symLink.
|
272
|
+
@param symLinkDir The directory to create the simlink.
|
273
|
+
@return None"""
|
274
|
+
self._symLinkDir=symLinkDir
|
275
|
+
|
276
|
+
def setLogFile(self, logFile):
|
277
|
+
"""@brief Set a logfile for all output.
|
278
|
+
@param logFile The file to send all output to.
|
279
|
+
@return None"""
|
280
|
+
self._logFile=logFile
|
281
|
+
self._debugLogFile = "{}.debug.txt".format(self._logFile)
|
282
|
+
|
283
|
+
def storeToLog(self, text, addLF=True, addDateTime=True):
|
284
|
+
"""@brief Save the text to the main log file if one is defined.
|
285
|
+
@param text The text to be saved.
|
286
|
+
@param addLF If True then a line feed is added to the output in the log file.
|
287
|
+
@return None"""
|
288
|
+
self._storeToLog(text, self._logFile, addLF=addLF, addDateTime=addDateTime)
|
289
|
+
|
290
|
+
def storeToDebugLog(self, text, addLF=True, addDateTime=True):
|
291
|
+
"""@brief Save the text to the debug log file if one is defined. This file holds all the
|
292
|
+
data from the main log file plus debug data even if debugging is not enabled.
|
293
|
+
@param text The text to be saved.
|
294
|
+
@param addLF If True then a line feed is added to the output in the log file.
|
295
|
+
@return None"""
|
296
|
+
self._storeToLog(text, self._debugLogFile, addLF=addLF, addDateTime=addDateTime, symLinkFile=UIO.DEBUG_LOG_SYM_LINK)
|
297
|
+
|
298
|
+
def _storeToLog(self, text, logFile, addLF=True, addDateTime=True, symLinkFile=USER_LOG_SYM_LINK):
|
299
|
+
"""@brief Save the text to the log file if one is defined.
|
300
|
+
@param text The text to be saved.
|
301
|
+
@param logFile The logFile to save data to.
|
302
|
+
@param addLF If True then a line feed is added to the output in the log file.
|
303
|
+
@param symLinkFile The name of the fixed symlink file to point to the latest log file.
|
304
|
+
@return None"""
|
305
|
+
createSymLink = False
|
306
|
+
if logFile:
|
307
|
+
if addDateTime:
|
308
|
+
timeStr = datetime.now().strftime("%d/%m/%Y-%H:%M:%S.%f")
|
309
|
+
text = "{}: {}".format(strftime(timeStr, localtime()).lower(), text)
|
310
|
+
|
311
|
+
# If the log file is about to be created then we will create a symlink
|
312
|
+
# to the file.
|
313
|
+
if not os.path.isfile(logFile):
|
314
|
+
# We can't create symlinks on a windows platform
|
315
|
+
if platform.system() != "Windows":
|
316
|
+
createSymLink = True
|
317
|
+
|
318
|
+
with open(logFile, 'a') as fd:
|
319
|
+
if addLF:
|
320
|
+
fd.write("{}\n".format(text))
|
321
|
+
else:
|
322
|
+
fd.write(text)
|
323
|
+
|
324
|
+
if createSymLink:
|
325
|
+
#This is helpful as the link will point to the latest log file
|
326
|
+
#which can be useful when debugging. I.E no need to find the
|
327
|
+
#name of the latest file.
|
328
|
+
dirName = self._symLinkDir
|
329
|
+
# if the simlink has not been set then default to the logging file
|
330
|
+
if dirName is None:
|
331
|
+
dirName = os.path.dirname(logFile)
|
332
|
+
absSymLink = os.path.join(dirName, symLinkFile)
|
333
|
+
if os.path.lexists(absSymLink):
|
334
|
+
os.remove(absSymLink)
|
335
|
+
os.symlink(logFile, absSymLink)
|
336
|
+
|
337
|
+
def showProgBar(self, barChar='*'):
|
338
|
+
"""@brief Show a bar that grows and shrinks to indicate an activity is occuring."""
|
339
|
+
if self._progBarGrow:
|
340
|
+
sys.stdout.write(barChar)
|
341
|
+
self._progBarSize+=1
|
342
|
+
if self._progBarSize > UIO.PROG_BAR_LENGTH:
|
343
|
+
self._progBarGrow=False
|
344
|
+
else:
|
345
|
+
sys.stdout.write('\b')
|
346
|
+
sys.stdout.write(' ')
|
347
|
+
sys.stdout.write('\b')
|
348
|
+
self._progBarSize-=1
|
349
|
+
if self._progBarSize == 0:
|
350
|
+
self._progBarGrow=True
|
351
|
+
|
352
|
+
sys.stdout.flush()
|
353
|
+
|
354
|
+
def clearProgBar(self):
|
355
|
+
"""@brief Clear any progress characters that maybe present"""
|
356
|
+
sys.stdout.write('\b' * UIO.PROG_BAR_LENGTH)
|
357
|
+
sys.stdout.write(' '*UIO.PROG_BAR_LENGTH)
|
358
|
+
sys.stdout.write('\r')
|
359
|
+
sys.stdout.flush()
|
360
|
+
|
361
|
+
def getLogFile(self):
|
362
|
+
"""@return the name of the user output log file or None if not set"""
|
363
|
+
return self._logFile
|
364
|
+
|
365
|
+
def getDebugLogFile(self):
|
366
|
+
"""@return The name of the debug log file or None if not set."""
|
367
|
+
return self._debugLogFile
|
368
|
+
|
369
|
+
def getCurrentUsername(self):
|
370
|
+
"""Get the current users username or return unknown_user if not able to read it.
|
371
|
+
This is required as getpass.getuser() does not always work on windows platforms."""
|
372
|
+
username="unknown_user"
|
373
|
+
try:
|
374
|
+
username=getuser()
|
375
|
+
except:
|
376
|
+
pass
|
377
|
+
return username
|
378
|
+
|
379
|
+
def enableSyslog(self, enabled, host="localhost", programName=None):
|
380
|
+
"""@brief Enable/disable syslog.
|
381
|
+
@param enabled If True then syslog is enabled.
|
382
|
+
@param syslogProgramName The name of the program that is being logged. If defined this appears after the username in the syslog output."""
|
383
|
+
self._sysLogEnabled = enabled
|
384
|
+
self._sysLogHost = host
|
385
|
+
if programName:
|
386
|
+
self._syslogProgramName = programName
|
387
|
+
else:
|
388
|
+
self._syslogProgramName = sys.argv[0]
|
389
|
+
|
390
|
+
def _update_syslog(self, pri, msg):
|
391
|
+
"""Send a message to syslog is syslog is enabled
|
392
|
+
Syslog messages will have the following components
|
393
|
+
0 = time/date stamp
|
394
|
+
1 = hostname
|
395
|
+
2 = main python file name
|
396
|
+
3 = PID
|
397
|
+
4 = username under which the program is being executed
|
398
|
+
5 = The syslog message
|
399
|
+
|
400
|
+
The syslog messages will be prefixed withj the application name
|
401
|
+
"""
|
402
|
+
if self._sysLogEnabled:
|
403
|
+
aMsg=msg
|
404
|
+
#Ensure we have no 0x00 characters in the message.
|
405
|
+
# syslog will throw an error if it finds any
|
406
|
+
if "\x00" in aMsg:
|
407
|
+
aMsg=aMsg.replace("\x00", "")
|
408
|
+
|
409
|
+
#Attempt to get the src IP address for syslog messages.
|
410
|
+
srcIP = ""
|
411
|
+
try:
|
412
|
+
netIF = NetIF()
|
413
|
+
srcIP = netIF.getLocalNetworkAddress()
|
414
|
+
except:
|
415
|
+
pass
|
416
|
+
|
417
|
+
try:
|
418
|
+
if self._syslogProgramName:
|
419
|
+
idString = str(self.getCurrentUsername()) + "-" + self._syslogProgramName
|
420
|
+
else:
|
421
|
+
idString = str(self.getCurrentUsername())
|
422
|
+
#send aMsg to syslog with the current process ID and username
|
423
|
+
syslog(pri, "%s %d %s: %s" % (srcIP, os.getpid(), idString, str(aMsg) ), host=self._sysLogHost )
|
424
|
+
#Ignore if se can't resolve address. We don't really syslog want errors to stop the user interface
|
425
|
+
except:
|
426
|
+
pass
|
427
|
+
|
428
|
+
def showTable(self, table, rowSeparatorChar = "-", colSeparatorChar = "|"):
|
429
|
+
"""@brief Show the contents of a table to the user.
|
430
|
+
@param table This must be a list. Each list element must be a table row (list).
|
431
|
+
Each element in each row must be a string.
|
432
|
+
@param rowSeparatorChar The character used for horizontal lines to separate table rows.
|
433
|
+
@param colSeparatorChar The character used to separate table columns."""
|
434
|
+
columnWidths = []
|
435
|
+
# Check we have a table to display
|
436
|
+
if len(table) == 0:
|
437
|
+
raise Exception("No table rows to display")
|
438
|
+
|
439
|
+
# Check all rows have the same number of columns in the table
|
440
|
+
colCount = len(table[0])
|
441
|
+
for row in table:
|
442
|
+
if len(row) != colCount:
|
443
|
+
raise Exception(f"{str(row)} column count different from first row ({colCount})")
|
444
|
+
|
445
|
+
for row in table:
|
446
|
+
for col in row:
|
447
|
+
if not isinstance(col, str):
|
448
|
+
raise Exception(f"Table column is not a string: {col} in {row}")
|
449
|
+
|
450
|
+
# Get the max width for each column
|
451
|
+
for col in range(0,colCount):
|
452
|
+
maxWidth=0
|
453
|
+
for row in table:
|
454
|
+
if len(row[col]) > maxWidth:
|
455
|
+
maxWidth = len(row[col])
|
456
|
+
columnWidths.append(maxWidth)
|
457
|
+
|
458
|
+
tableWidth = 1
|
459
|
+
for columnWidth in columnWidths:
|
460
|
+
tableWidth += columnWidth + 3 # Space each side of the column + a column divider character
|
461
|
+
|
462
|
+
# Add the top line of the table
|
463
|
+
self.info(rowSeparatorChar*tableWidth)
|
464
|
+
|
465
|
+
# The starting row index
|
466
|
+
for rowIndex in range(0, len(table)):
|
467
|
+
rowText = colSeparatorChar
|
468
|
+
colIndex = 0
|
469
|
+
for col in table[rowIndex]:
|
470
|
+
colWidth = columnWidths[colIndex]
|
471
|
+
rowText = rowText + " " + f"{col:>{colWidth}s}" + " " + colSeparatorChar
|
472
|
+
colIndex += 1
|
473
|
+
self.info(rowText)
|
474
|
+
# Add the row separator line
|
475
|
+
self.info(rowSeparatorChar*tableWidth)
|
476
|
+
|
477
|
+
class ConsoleMenu(object):
|
478
|
+
"""@brief Responsible for presenting a list of options to the user on a
|
479
|
+
console/terminal interface and allowing the user to select
|
480
|
+
the options as required."""
|
481
|
+
def __init__(self, uio):
|
482
|
+
"""@brief Constructor
|
483
|
+
@param uio A UIO instance."""
|
484
|
+
self._uio = uio
|
485
|
+
self._menuList = []
|
486
|
+
|
487
|
+
def add(self, menuStr, menuMethod, args=None):
|
488
|
+
"""@brief Add a menu option.
|
489
|
+
@param menuStr The String displayed as the menu option.
|
490
|
+
@param menuMethod The method to be called when this option is selected.
|
491
|
+
@param args An optional list or tuple of arguments to pass to the method."""
|
492
|
+
self._menuList.append( (menuStr, menuMethod, args) )
|
493
|
+
|
494
|
+
def show(self, showSelectedOption=False):
|
495
|
+
"""@brief Show the menu to the user and allow the user to interact with it.
|
496
|
+
@param showSelectedOption If True then the option selected is displayed before executing the method."""
|
497
|
+
while True:
|
498
|
+
selectorId = 1
|
499
|
+
for menuStr, _, _ in self._menuList:
|
500
|
+
self._uio.info("{: >2}: {}".format(selectorId, menuStr))
|
501
|
+
selectorId = selectorId + 1
|
502
|
+
selectedID = self._uio.getIntInput("Select a menu option")
|
503
|
+
if selectedID >= 1 and selectedID <= len(self._menuList):
|
504
|
+
menuStr, selectedMethod, args = self._menuList[selectedID-1]
|
505
|
+
if showSelectedOption:
|
506
|
+
self._uio.info(menuStr)
|
507
|
+
if not args:
|
508
|
+
selectedMethod()
|
509
|
+
else:
|
510
|
+
selectedMethod(*args)
|
511
|
+
|
512
|
+
|
513
|
+
|
514
|
+
|
515
|
+
|
516
|
+
# -------------------------------------------------------------------
|
517
|
+
# @brief An implementation to allow syslog messages to be generated.
|
518
|
+
#
|
519
|
+
class FACILITY:
|
520
|
+
KERN=0
|
521
|
+
USER=1
|
522
|
+
MAIL=2
|
523
|
+
DAEMON=3
|
524
|
+
AUTH=4
|
525
|
+
SYSLOG=5
|
526
|
+
LPR=6
|
527
|
+
NEWS=7
|
528
|
+
UUCP=8
|
529
|
+
CRON=9
|
530
|
+
AUTHPRIV=10
|
531
|
+
FTP=11
|
532
|
+
LOCAL0=16
|
533
|
+
LOCAL1=17
|
534
|
+
LOCAL2=18
|
535
|
+
LOCAL3=19
|
536
|
+
LOCAL4=20
|
537
|
+
LOCAL5=21
|
538
|
+
LOCAL6=22
|
539
|
+
LOCAL7=23
|
540
|
+
|
541
|
+
class PRIORITY:
|
542
|
+
EMERG=0
|
543
|
+
ALERT=1
|
544
|
+
CRIT=2
|
545
|
+
ERROR=3
|
546
|
+
WARNING=4
|
547
|
+
NOTICE=5
|
548
|
+
INFO=6
|
549
|
+
DEBUG=7
|
550
|
+
|
551
|
+
syslogSocket=None
|
552
|
+
lock = Lock()
|
553
|
+
|
554
|
+
def syslog(priority, message, facility=FACILITY.LOCAL0, host='localhost', port=514):
|
555
|
+
"""
|
556
|
+
@brief Send a syslog message.
|
557
|
+
@param priority The syslog priority level.
|
558
|
+
@param message The text message to be sent.
|
559
|
+
@param facility The syslog facility
|
560
|
+
@param host The host address for the systlog server.
|
561
|
+
@param port The syslog port.
|
562
|
+
"""
|
563
|
+
global lock, timer, syslogSocket
|
564
|
+
|
565
|
+
try:
|
566
|
+
lock.acquire()
|
567
|
+
if not syslogSocket:
|
568
|
+
syslogSocket = socket(AF_INET, SOCK_DGRAM)
|
569
|
+
|
570
|
+
smsg = '<%05d>%s' % ( (priority + facility*8), message )
|
571
|
+
syslogSocket.sendto(smsg.encode('ascii', 'ignore'), (host, port))
|
572
|
+
|
573
|
+
finally:
|
574
|
+
lock.release()
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2010 Paul Austen
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: p3lib
|
3
|
+
Version: 1.1.108
|
4
|
+
Summary: A group of python modules for networking, plotting data, config storage, automating boot scripts, ssh access and user input output.
|
5
|
+
Home-page: https://github.com/pjaos/test_equipment
|
6
|
+
License: MIT
|
7
|
+
Author: Paul Austen
|
8
|
+
Author-email: pjaos@gmail.com
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Programming Language :: Python :: 2
|
11
|
+
Classifier: Programming Language :: Python :: 2.7
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
13
|
+
Classifier: Programming Language :: Python :: 3.4
|
14
|
+
Classifier: Programming Language :: Python :: 3.5
|
15
|
+
Classifier: Programming Language :: Python :: 3.6
|
16
|
+
Classifier: Programming Language :: Python :: 3.7
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
22
|
+
Project-URL: Repository, https://github.com/pjaos/test_equipment
|
23
|
+
Description-Content-Type: text/markdown
|
24
|
+
|
25
|
+
# A Python3 library
|
26
|
+
A group of python modules for networking, plotting data, config storage, automating boot scripts, ssh access and user input output.
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
p3lib is available on pypi and can be installed using the following command.
|
30
|
+
|
31
|
+
```
|
32
|
+
pip3 install p3lib
|
33
|
+
```
|
34
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
p3lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
p3lib/ate.py,sha256=_BiqMUYNAlp4O8MkP_PAUe62Bzd8dzV4Ipv62OFS6Ok,4759
|
3
|
+
p3lib/bokeh_auth.py,sha256=zi_Hty2WkCJVNQ4sINNRo5FBXujuJky9phyoF6M55ms,15180
|
4
|
+
p3lib/bokeh_gui.py,sha256=55sajP_x9O1lE0uP3w3-T5f2oMzk7jSolLqxlEdLeLg,40245
|
5
|
+
p3lib/boot_manager.py,sha256=l7WlaoIRt4cu2jAdCs3FnDeNO2v2jzqideNZ3qg2r_4,19652
|
6
|
+
p3lib/conduit.py,sha256=jPkjdtyCx2I6SFqcEo8y2g7rgnZ-jNY7oCuYIETzT5Q,6046
|
7
|
+
p3lib/database_if.py,sha256=XKu1w3zftGbj4Rh54wrWJnoCtqHkhCzJUPN2S70XIKg,11915
|
8
|
+
p3lib/file_io.py,sha256=A7_GKYPlmjRjq6U1YuWhmB0OhLhNm6cWQfQX8qfgYTk,5041
|
9
|
+
p3lib/gnome_desktop_app.py,sha256=zl9SKRBV8ipVg2faCs_gbAr8c42J1N_pntbFGG2BMiE,6694
|
10
|
+
p3lib/helper.py,sha256=RHA0T1ckG3wLgTXgI_1mrtbzvMBaIwd8ow7x9orXUfA,13730
|
11
|
+
p3lib/json_networking.py,sha256=6u4s1SmypjTYPnSxHP712OgQ3ZJaxOqIkgHQ1J7Qews,9738
|
12
|
+
p3lib/login.html,sha256=DADTJGuvWQ-LTO4X6SaFdqK7JMW03DAa3lRieGD0d6g,2748
|
13
|
+
p3lib/mqtt_rpc.py,sha256=6LmFA1kR4HSJs9eWbOJORRHNY01L_lHWjvtE2fmY8P8,10511
|
14
|
+
p3lib/netif.py,sha256=3QV5OGdHhELIf4MBj6mx5MNCtVeZ7JXoNEkeu4KzCaE,9796
|
15
|
+
p3lib/netplotly.py,sha256=PMDx-w1jtRVW6Od5u_kuKbBxNpTS_Y88mMF60puMxLM,9363
|
16
|
+
p3lib/ngt.py,sha256=Y4HsSiaBEYi7OFWkqkDDJende_yyxsvIjUremfArXgA,39204
|
17
|
+
p3lib/pconfig.py,sha256=k1WHvmHzOfmkSbP7CrFyT1iTwn5_UROQjk6gltdh7bk,37280
|
18
|
+
p3lib/ssh.py,sha256=OyoAQ_h1L2RfkjTAChDrvLFfl4Fe_gBNdX5rvK-wKiw,42125
|
19
|
+
p3lib/table_plot.py,sha256=RPncwVlGUkkx5Fw0dHQedXo0TSPlTi__VrJBDzaMsuI,32116
|
20
|
+
p3lib/uio.py,sha256=Aaxc99XiE3d2f9vLjaN-bZsckoNxay5t0ujdK6PXGrw,23265
|
21
|
+
p3lib-1.1.108.dist-info/LICENSE,sha256=igqTy5u0kVWM1n-NUZMvAlinY6lVjAXKoag0okkS8V8,1067
|
22
|
+
p3lib-1.1.108.dist-info/METADATA,sha256=yZIJMQROTciIu_sB7_tTPIIXbP72FkWsi7nGCmFqCas,1337
|
23
|
+
p3lib-1.1.108.dist-info/WHEEL,sha256=IrRNNNJ-uuL1ggO5qMvT1GGhQVdQU54d6ZpYqEZfEWo,92
|
24
|
+
p3lib-1.1.108.dist-info/RECORD,,
|