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/boot_manager.py ADDED
@@ -0,0 +1,420 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import os
4
+ import sys
5
+ import platform
6
+ import getpass
7
+
8
+ from subprocess import check_call, DEVNULL, STDOUT, Popen, PIPE
9
+ from datetime import datetime
10
+
11
+ class BootManager(object):
12
+ """Responsible for adding and removing startup processes (python programs) when the computer boots.
13
+ Currently supports the following platforms
14
+ Linux"""
15
+
16
+ LINUX_OS_NAME = "Linux"
17
+ ENABLE_CMD_OPT = "--enable_auto_start"
18
+ DISABLE_CMD_OPT = "--disable_auto_start"
19
+ CHECK_CMD_OPT = "--check_auto_start"
20
+
21
+ @staticmethod
22
+ def AddCmdArgs(parser):
23
+ """@brief Add cmd line arguments to enable, disable and show the systemd boot state.
24
+ @param parser An instance of argparse.ArgumentParser."""
25
+ parser.add_argument(BootManager.ENABLE_CMD_OPT, help="Auto start when this computer starts.", action="store_true", default=False)
26
+ parser.add_argument(BootManager.DISABLE_CMD_OPT, help="Disable auto starting when this computer starts.", action="store_true", default=False)
27
+ parser.add_argument(BootManager.CHECK_CMD_OPT, help="Check the status of an auto started icons_gw instance.", action="store_true", default=False)
28
+
29
+ @staticmethod
30
+ def HandleOptions(uio, options, enable_syslog, serviceName=None, restartSeconds=1):
31
+ """@brief Handle one of the bot manager command line options if the
32
+ user passed it on the cmd line.
33
+ @param uio A UIO instance.
34
+ @param options As returned from parser.parse_args() where parser
35
+ is an instance of argparse.ArgumentParser.
36
+ @param enable_syslog True to enable systemd syslog output.
37
+ @param serviceName The name of the service. If not set then the name of the initially executed
38
+ python file is used.
39
+ @param restartSeconds The number of seconds to sleep before restarting a service that has stopped (default=1).
40
+ @return True if handled , False if not."""
41
+ handled = False
42
+ if options.check_auto_start:
43
+ BootManager.CheckAutoStartStatus(uio, serviceName)
44
+ handled = True
45
+
46
+ elif options.enable_auto_start:
47
+ BootManager.EnableAutoStart(uio, enable_syslog, serviceName, restartSeconds)
48
+ handled = True
49
+
50
+ elif options.disable_auto_start:
51
+ BootManager.DisableAutoStart(uio, serviceName)
52
+ handled = True
53
+
54
+ return handled
55
+
56
+ @staticmethod
57
+ def EnableAutoStart(uio, enable_syslog, serviceName, restartSeconds):
58
+ """@brief Enable this program to auto start when the computer on which it is installed starts.
59
+ @param uio A UIO instance.
60
+ @param options As returned from parser.parse_args() where parser
61
+ is an instance of argparse.ArgumentParser.
62
+ @param enable_syslog True to enable systemd syslog output.
63
+ @param serviceName The name of the service. If not set then the name of the initially executed
64
+ python file is used.
65
+ @param restartSeconds The number of seconds to sleep before restarting a service that has stopped (default=1)."""
66
+ bootManager = BootManager(uio=uio, ensureRootUser=True, serviceName=serviceName, restartSeconds=restartSeconds)
67
+ arsString = " ".join(sys.argv)
68
+ bootManager.add(argString=arsString, enableSyslog=enable_syslog)
69
+
70
+ @staticmethod
71
+ def DisableAutoStart(uio, serviceName):
72
+ """@brief Enable this program to auto start when the computer on which it is installed starts.
73
+ @param uio A UIO instance.
74
+ @param serviceName The name of the service. If not set then the name of the initially executed
75
+ python file is used."""
76
+ bootManager = BootManager(uio=uio, ensureRootUser=True, serviceName=serviceName)
77
+ bootManager.remove()
78
+
79
+ @staticmethod
80
+ def CheckAutoStartStatus(uio, serviceName):
81
+ """@brief Check the status of a process previously set to auto start.
82
+ @param uio A UIO instance.
83
+ @param serviceName The name of the service. If not set then the name of the initially executed
84
+ python file is used."""
85
+ bootManager = BootManager(uio=uio, serviceName=serviceName)
86
+ lines = bootManager.getStatus()
87
+ if lines and len(lines) > 0:
88
+ for line in lines:
89
+ uio.info(line)
90
+
91
+ def __init__(self, uio=None, allowRootUser=True, ensureRootUser=False, serviceName=None, restartSeconds=1):
92
+ """@brief Constructor
93
+ @param uio A UIO instance to display user output. If unset then no output
94
+ is displayed to user.
95
+ @param allowRootUser If True then allow root user to to auto start
96
+ programs. Note that as the BootManager is responsible for
97
+ ensuring programs are started up when a machine boots up
98
+ the installed program should be installed for the root
99
+ user on Linux systems.
100
+ @param ensureRootUser If True the current user must be root user (Linux systems).
101
+ @param serviceName The name of the service. If not set then the name of the initially executed
102
+ python file is used.
103
+ @param restartSeconds The number of seconds to sleep before restarting a service that has stopped (default=1)."""
104
+ self._uio = uio
105
+ self._allowRootUser=allowRootUser
106
+ self._osName = platform.system()
107
+ self._platformBootManager = None
108
+ if self._osName == BootManager.LINUX_OS_NAME:
109
+ self._platformBootManager = LinuxBootManager(uio, self._allowRootUser, ensureRootUser, serviceName, restartSeconds)
110
+ else:
111
+ raise Exception("{} is an unsupported OS.".format(self._osName) )
112
+
113
+ def add(self, user=None, argString=None, enableSyslog=False):
114
+ """@brief Add an executable file to the processes started at boot time.
115
+ @param exeFile The file/program to be executed. This should be an absolute path.
116
+ @param user The user that will run the executable file. If left as None then the current user will be used.
117
+ @param argString The argument string that the program is to be launched with.
118
+ @param enableSyslog If True enable stdout and stderr to be sent to syslog."""
119
+ if self._platformBootManager:
120
+ self._platformBootManager.add(user, argString, enableSyslog)
121
+
122
+ def remove(self):
123
+ """@brief Remove an executable file to the processes started at boot time.
124
+ @param exeFile The file/program to be removed. This should be an absolute path.
125
+ @param user The Linux user that will run the executable file."""
126
+ if self._platformBootManager:
127
+ self._platformBootManager.remove()
128
+
129
+ def getStatus(self):
130
+ """@brief Get a status report.
131
+ @return Lines of text indicating the status of a previously started process."""
132
+ statusLines = []
133
+ if self._platformBootManager:
134
+ statusLines = self._platformBootManager.getStatusLines()
135
+ return statusLines
136
+
137
+
138
+ class LinuxBootManager(object):
139
+ """@brief Responsible for adding/removing Linux services using systemd."""
140
+
141
+ LOG_PATH ="/var/log"
142
+ ROOT_SERVICE_FOLDER = "/etc/systemd/system/"
143
+ SYSTEM_CTL_1 = "/bin/systemctl"
144
+ SYSTEM_CTL_2 = "/usr/bin/systemctl"
145
+
146
+ @staticmethod
147
+ def GetSystemCTLBin():
148
+ """@brief Get the location of the systemctl binary file on this system.
149
+ @return The systemctl bin file."""
150
+ binFile = None
151
+ if os.path.isfile(LinuxBootManager.SYSTEM_CTL_1):
152
+ binFile = LinuxBootManager.SYSTEM_CTL_1
153
+ elif os.path.isfile(LinuxBootManager.SYSTEM_CTL_2):
154
+ binFile = LinuxBootManager.SYSTEM_CTL_2
155
+ else:
156
+ raise Exception("Failed to find the location of the systemctl bin file on this machine.")
157
+ return binFile
158
+
159
+ @staticmethod
160
+ def GetServiceFolder(rootUser):
161
+ """"@brief Get the service folder to use.
162
+ @param rootUser False if non root user.
163
+ @return The folder that should hold the systemctl service files."""
164
+ serviceFolder = None
165
+ if rootUser:
166
+ serviceFolder = LinuxBootManager.ROOT_SERVICE_FOLDER
167
+ else:
168
+ homeFolder = os.path.expanduser('~')
169
+ serviceFolder = os.path.join(homeFolder, '.config/systemd/user/')
170
+ if not os.path.isdir(serviceFolder):
171
+ os.makedirs(serviceFolder)
172
+
173
+ if not os.path.isdir(serviceFolder):
174
+ raise Exception(f"{serviceFolder} folder not found.")
175
+ return serviceFolder
176
+
177
+ def __init__(self, uio, allowRootUser, ensureRootUser, serviceName, restartSeconds):
178
+ """@brief Constructor
179
+ @param uio A UIO instance to display user output. If unset then no output is displayed to user.
180
+ @param allowRootUser If True then allow root user to to auto start programs.
181
+ @param ensureRootUser If True the current user must be root user.
182
+ @param serviceName The name of the service. If not set then the name of the initially executed
183
+ python file is used.
184
+ @param restartSeconds The number of seconds to sleep before restarting a service that has stopped."""
185
+ self._uio = uio
186
+ self._logFile = None
187
+ self._allowRootUser=allowRootUser
188
+ self._info("OS: {}".format(platform.system()) )
189
+ self._rootMode = False # If True run as root, else False.
190
+ self._systemCtlBin = LinuxBootManager.GetSystemCTLBin()
191
+ if ensureRootUser and os.geteuid() != 0:
192
+ self._fatalError(self.__class__.__name__ + ": Not root user. Ensure that you are root user and try again.")
193
+
194
+ if os.geteuid() == 0:
195
+ if not allowRootUser:
196
+ self._fatalError(self.__class__.__name__ + f": You are running as root user but allowRootUser={allowRootUser}.")
197
+ else:
198
+ self._rootMode = True
199
+ if not self._rootMode:
200
+ self._cmdLinePrefix = self._systemCtlBin + " --user"
201
+ else:
202
+ self._cmdLinePrefix = self._systemCtlBin
203
+ self._username = getpass.getuser()
204
+ self._serviceFolder = LinuxBootManager.GetServiceFolder(self._rootMode)
205
+ self._serviceName = serviceName
206
+ self._restartSeconds = restartSeconds
207
+
208
+ def _getInstallledStartupScript(self):
209
+ """@brief Get the startup script full path. The startup script must be
210
+ named the same as the python file executed without the .py suffix.
211
+ @return The startup script file (absolute path)."""""
212
+ startupScript=None
213
+ argList = sys.argv
214
+ # If the first argument in the arg is a file.
215
+ if len(argList) > 0:
216
+ firstArg = argList[0]
217
+ if os.path.isfile(firstArg) or os.path.islink(firstArg):
218
+ startupScript = firstArg
219
+ if startupScript is None:
220
+ raise Exception("Failed to find the startup script.")
221
+ return startupScript
222
+
223
+ def _getPaths(self):
224
+ """@brief Get a list of the paths from the PATH env var.
225
+ @return A list of paths or None if PATH env var not found."""
226
+ pathEnvVar = os.getenv("PATH")
227
+ envPaths = pathEnvVar.split(os.pathsep)
228
+ return envPaths
229
+
230
+ def _fatalError(self, msg):
231
+ """@brief Record a fatal error.
232
+ @param msg The message detailing the error."""
233
+ raise Exception(msg)
234
+
235
+ def _info(self, msg):
236
+ """@brief Display an info level message to the user
237
+ @param msg The message to be displayed."""
238
+ self._log(msg)
239
+ if self._uio:
240
+ self._uio.info(msg)
241
+
242
+ def _error(self, msg):
243
+ """@brief Display an error level message to the user
244
+ @param msg The message to be displayed."""
245
+ self._log(msg)
246
+ if self._uio:
247
+ self._uio.error(msg)
248
+
249
+ def _log(self, msg):
250
+ """@brief Save a message to the log file.
251
+ @param msg The message to save"""
252
+ if self._logFile:
253
+ timeStr = datetime.now().strftime("%d/%m/%Y-%H:%M:%S.%f")
254
+ fd = open(self._logFile, 'a')
255
+ fd.write("{}: {}\n".format(timeStr, msg) )
256
+ fd.close()
257
+
258
+ def _runLocalCmd(self, cmd):
259
+ """@brief Run a command
260
+ @param cmd The command to run.
261
+ @return The return code of the external cmd."""
262
+ self._log("Running: {}".format(cmd) )
263
+ check_call(cmd, shell=True, stdout=DEVNULL, stderr=STDOUT)
264
+
265
+ def _getApp(self):
266
+ """@brief Get details of the app to run
267
+ @return a tuple containing
268
+ 0 = The name of the app
269
+ 1 = The absolute path to the app to run"""
270
+ exeFile = self._getInstallledStartupScript()
271
+ exePath = os.path.dirname(exeFile)
272
+ if len(exePath) == 0:
273
+ self._fatalError("{} is invalid as executable path is undefined.".format(exeFile) )
274
+
275
+ if not os.path.isdir(exePath):
276
+ self._fatalError("{} path not found".format(exePath))
277
+
278
+ appName = os.path.basename(exeFile)
279
+ if len(appName) == 0:
280
+ self._fatalError("No app found to execute.")
281
+
282
+ absApp = os.path.join(exePath, appName)
283
+ if not os.path.isfile( absApp ):
284
+ self._fatalError("{} file not found.".format(absApp) )
285
+
286
+ appName = appName.replace(".py", "")
287
+ if self._rootMode:
288
+ # We can only save to /var/log/ is we are root user.
289
+ self._logFile = os.path.join(LinuxBootManager.LOG_PATH, appName)
290
+
291
+ return (appName, absApp)
292
+
293
+ def _getServiceName(self):
294
+ """@brief Get the name of the service.
295
+ @return The name of the service."""
296
+ if self._serviceName:
297
+ serviceName = self._serviceName
298
+ else:
299
+ appName, _ = self._getApp()
300
+ serviceName = appName
301
+ return "{}.service".format(serviceName)
302
+
303
+ def _getServiceFile(self, appName):
304
+ """@brief Get the name of the service file.
305
+ @param appName The name of the app to execute.
306
+ @return The absolute path to the service file """
307
+ serviceName = self._getServiceName()
308
+ serviceFile = os.path.join(self._serviceFolder, serviceName)
309
+ self._uio.info(f"SERVICE FILE: {serviceFile}")
310
+ return serviceFile
311
+
312
+ def add(self, user, argString=None, enableSyslog=False):
313
+ """@brief Add an executable file to the processes started at boot time.
314
+ This will also start the process. The executable file must be
315
+ named the same as the python file executed without the .py suffix.
316
+ @param user The Linux user that will run the executable file.
317
+ This should not be root as config files will be be saved
318
+ to non root user paths on Linux systems and the startup
319
+ script should then be executed with the same username in
320
+ order that the same config file is used.
321
+ If set to None then the current user is used.
322
+ @param argString The argument string that the program is to be launched with.
323
+ @param enableSyslog If True enable stdout and stderr to be sent to syslog."""
324
+ if user is None:
325
+ user = self._username
326
+
327
+ appName, absApp = self._getApp()
328
+ serviceName = self._getServiceName()
329
+
330
+ serviceFile = self._getServiceFile(serviceName)
331
+
332
+ lines = []
333
+ lines.append("[Unit]")
334
+ lines.append("After=network.target")
335
+ lines.append("StartLimitIntervalSec=0")
336
+ lines.append("")
337
+ lines.append("[Service]")
338
+ lines.append("Type=simple")
339
+ lines.append("Restart=always")
340
+ lines.append(f"RestartSec={self._restartSeconds}")
341
+ if enableSyslog:
342
+ lines.append("StandardOutput=syslog")
343
+ lines.append("StandardError=syslog")
344
+ else:
345
+ lines.append("StandardOutput=null")
346
+ lines.append("StandardError=journal")
347
+ if self._rootMode and user != 'root':
348
+ lines.append("User={}".format(user))
349
+
350
+ #We add the home path env var so that config files (if stored in/under
351
+ # the users home dir) can be found by the prgram.
352
+ if user and len(user) > 0:
353
+ lines.append('Environment="HOME=/home/{}"'.format(user))
354
+ if argString:
355
+ argString = argString.strip()
356
+ if argString.startswith(absApp):
357
+ argString=argString.replace(absApp, "")
358
+ # We don't want the enable cmd opt in the cmd we add to the systemd file.
359
+ if argString.find(BootManager.ENABLE_CMD_OPT):
360
+ argString = argString.replace(BootManager.ENABLE_CMD_OPT, "")
361
+ argString = argString.strip()
362
+ lines.append("ExecStart={} {}".format(absApp, argString))
363
+ else:
364
+ lines.append("ExecStart={}".format(absApp))
365
+ lines.append("")
366
+ lines.append("[Install]")
367
+ lines.append("WantedBy=multi-user.target")
368
+ lines.append("")
369
+
370
+ try:
371
+ fd = open(serviceFile, 'w')
372
+ fd.write( "\n".join(lines) )
373
+ fd.close()
374
+ self._info(f"Created {serviceFile}")
375
+ except IOError:
376
+ self._fatalError("Failed to create {}".format(serviceFile) )
377
+
378
+ cmd = "{} daemon-reload".format(self._cmdLinePrefix)
379
+ self._runLocalCmd(cmd)
380
+ cmd = "{} enable {}".format(self._cmdLinePrefix, serviceName)
381
+ self._info("Enabled {} on restart".format(serviceName))
382
+ self._runLocalCmd(cmd)
383
+ cmd = "{} start {}".format(self._cmdLinePrefix, serviceName)
384
+ self._runLocalCmd(cmd)
385
+ self._info("Started {}".format(serviceName))
386
+
387
+ def remove(self):
388
+ """@brief Remove the executable file to the processes started at boot time.
389
+ Any running processes will be stopped. The executable file must be
390
+ named the same as the python file executed without the .py suffix."""
391
+ appName, _ = self._getApp()
392
+
393
+ serviceName = self._getServiceName()
394
+ serviceFile = self._getServiceFile(appName)
395
+ if os.path.isfile(serviceFile):
396
+ cmd = "{} disable {}".format(self._cmdLinePrefix, serviceName)
397
+ self._runLocalCmd(cmd)
398
+ self._info("Disabled {} on restart".format(serviceName))
399
+
400
+ cmd = "{} stop {}".format(self._cmdLinePrefix, serviceName)
401
+ self._runLocalCmd(cmd)
402
+ self._info("Stopped {}".format(serviceName))
403
+
404
+ os.remove(serviceFile)
405
+ self._log("Removed {}".format(serviceFile))
406
+ else:
407
+ self._info("{} service not found".format(serviceName))
408
+
409
+ def getStatusLines(self):
410
+ """@brief Get a status report.
411
+ @return Lines of text indicating the status of a previously started process."""
412
+ serviceName = self._getServiceName()
413
+ if self._rootMode:
414
+ p = Popen([self._systemCtlBin, 'status', serviceName], stdin=PIPE, stdout=PIPE, stderr=PIPE)
415
+ else:
416
+ p = Popen([self._systemCtlBin, '--user', 'status', serviceName], stdin=PIPE, stdout=PIPE, stderr=PIPE)
417
+ output, err = p.communicate(b"input data that is passed to subprocess' stdin")
418
+ response = output.decode() + "\n" + err.decode()
419
+ lines = response.split("\n")
420
+ return lines
p3lib/conduit.py ADDED
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env python3
2
+
3
+ from queue import Queue, Empty
4
+
5
+ class Conduit(object):
6
+ """@brief A generalised conduit implementation. A conduit is used for
7
+ communication between two processes. Each Conduit has an A
8
+ and a B end. Messages can be pushed into each end and
9
+ pulled from each end.
10
+ """
11
+ CONDUIT_TYPE_QUEUE = 1
12
+ CONDUIT_TYPE_TCP_CONNECTION = 2
13
+
14
+ def __init__(self, uio=None, cName="", conduitType=CONDUIT_TYPE_QUEUE, readBlock=True, readBlockTimeoutSeconds=None, maxSize = 0, ):
15
+ """@brief Responsible for providing a conduit for data between entities.
16
+ @param uio A User input/output object. If supplied then debug info for the conduit will be recorded.
17
+ @param cName The conduit name. Only useful if a uio object has been passed for debugging purposes.
18
+ @param readBlock If true then all getX() methods will block until data is available.
19
+ @param readBlockTimeoutSeconds The time in seconds for a read (when readBlock=True) to timeout. The default = None (block indefinatley).
20
+ @param maxSize Maximum number of elements in the queue. Only valid if conduitType = CONDUIT_TYPE_QUEUE."""
21
+
22
+ if conduitType == Conduit.CONDUIT_TYPE_QUEUE:
23
+ self._conduit = QueueConduit(uio=uio, cName=cName, maxQueueSize=maxSize, readBlock=readBlock, readBlockTimeoutSeconds=readBlockTimeoutSeconds)
24
+
25
+ elif conduitType == Conduit.CONDUIT_TYPE_TCP_CONNECTION:
26
+ raise Exception("TCP conduits not yet implemented.")
27
+
28
+ else:
29
+ raise Exception("%d is an invalid conduit type." % (conduitType) )
30
+
31
+ def putA(self, data):
32
+ """@brief put some data in the A -> B side conduit.
33
+ @param data The data object to be pushed into the conduit."""
34
+ self._conduit.putA(data)
35
+
36
+ def putB(self, data):
37
+ """@brief put some data in the B -> A side conduit.
38
+ @param data The data object to be pushed into the conduit."""
39
+ self._conduit.putB(data)
40
+
41
+ def getA(self):
42
+ """@brief Get some data from the B -> A conduit.
43
+ @return The data from the conduit or None of no data is available."""
44
+ return self._conduit.getA()
45
+
46
+ def getB(self):
47
+ """@brief Get some data from the A -> B conduit.
48
+ @return The data from the conduit or None of no data is available."""
49
+ return self._conduit.getB()
50
+
51
+ def aReadAvailable(self):
52
+ """@return True if there is data available to be read from the A side of the conduit."""
53
+ return self._conduit.aReadAvailable()
54
+
55
+ def bReadAvailable(self):
56
+ """@return True if there is data available to be read from the B side of the conduit."""
57
+ return self._conduit.bReadAvailable()
58
+
59
+ class QueueConduit(Conduit):
60
+ """@brief Responsible for providing the functionality required to communicate between
61
+ threads (ITC= Inter Thread Communication).
62
+
63
+ The ITC has an A side and a B side. data can be sent from the A and B sides
64
+ and is forwarded to the other side."""
65
+
66
+ def __init__(self, uio=None, cName="", maxQueueSize = 0, readBlock=True, readBlockTimeoutSeconds=0):
67
+ """@brief Constructor
68
+ @param uio A User input/output object. If supplied then debug info for the conduit will be recorded.
69
+ @param cName The conduit name. Only useful if a uio object has been passed for debugging purposes.
70
+ @param maxQueueSize The maximum queue size that we will allow (default = 0, no limit)
71
+ @param readBlock If True then reads will block until data is available or a timeout (if > 0) occurs."""
72
+
73
+ self._uio = uio
74
+ self._cName = cName
75
+ self._maxQueueSize = maxQueueSize
76
+ self._readBlock = readBlock
77
+ self._readBlockTimeoutSeconds = readBlockTimeoutSeconds
78
+ self._aToBQueue = Queue(maxQueueSize)
79
+ self._bToAQueue = Queue(maxQueueSize)
80
+
81
+ def _checkQueueSize(self):
82
+ """@brief check that we have not reached the max queue size."""
83
+ if self._maxQueueSize > 0:
84
+ if self._aToBQueue.qsize() >= self._maxQueueSize:
85
+ raise Exception("%s: A -> B queue full." % (self.__class__.__name__) )
86
+
87
+ if self._bToAQueue.qsize() >= self._maxQueueSize:
88
+ raise Exception("%s: B -> A queue full." % (self.__class__.__name__) )
89
+
90
+ def putA(self, data):
91
+ """@brief put some data in the A -> B side queue.
92
+ @param data The data object to be pushed into the queue."""
93
+ self._checkQueueSize()
94
+
95
+ if self._uio:
96
+ qSize = self._aToBQueue.qsize()
97
+ self._uio.debug("%s: A -> B queue size = %d" % (self._cName, qSize) )
98
+
99
+ self._aToBQueue.put(data)
100
+
101
+ def putB(self, data):
102
+ """@brief put some data in the B -> A side queue.
103
+ @param data The data object to be pushed into the queue."""
104
+ self._checkQueueSize()
105
+
106
+ if self._uio:
107
+ qSize = self._bToAQueue.qsize()
108
+ self._uio.debug("%s: B -> A queue size = %d" % (self._cName, qSize) )
109
+
110
+ self._bToAQueue.put(data)
111
+
112
+ def getA(self, block=True, timeoutSeconds=0.0):
113
+ """@brief Get some data from the B -> A queue.
114
+ @param block If true then the call will block
115
+ @param timeoutSeconds The time in seconds before exiting (returning None) if no data can be read from the queue.
116
+ @return The data from the queue or None of no data is available."""
117
+ try:
118
+ return self._bToAQueue.get(block=self._readBlock, timeout=self._readBlockTimeoutSeconds)
119
+ except Empty:
120
+ return None
121
+
122
+ def getB(self, block=True, timeoutSeconds=0.0):
123
+ """@brief Get some data from the A -> B queue.
124
+ @param block If true then the call will block
125
+ @param timeoutSeconds The time in seconds before exiting (returning None) if no data can be read from the queue.
126
+ @return The data from the queue or None of no data is available."""
127
+ try:
128
+ return self._aToBQueue.get(block=self._readBlock, timeout=self._readBlockTimeoutSeconds)
129
+ except Empty:
130
+ return None
131
+
132
+ def aReadAvailable(self):
133
+ """@return True if there is data available to be read from the A side of the queue."""
134
+ return not self._bToAQueue.empty()
135
+
136
+ def bReadAvailable(self):
137
+ """@return True if there is data available to be read from the B side of the queue."""
138
+ return not self._aToBQueue.empty()
139
+
140
+
141
+
142
+
143
+
144
+
145
+