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/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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py2.py3-none-any