rda-python-common 3.0.28__py3-none-any.whl → 3.0.30__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.
- rda_python_common/__init__.py +1 -1
- rda_python_common/pg_sig.py +111 -8
- {rda_python_common-3.0.28.dist-info → rda_python_common-3.0.30.dist-info}/METADATA +2 -2
- {rda_python_common-3.0.28.dist-info → rda_python_common-3.0.30.dist-info}/RECORD +8 -8
- {rda_python_common-3.0.28.dist-info → rda_python_common-3.0.30.dist-info}/WHEEL +0 -0
- {rda_python_common-3.0.28.dist-info → rda_python_common-3.0.30.dist-info}/entry_points.txt +0 -0
- {rda_python_common-3.0.28.dist-info → rda_python_common-3.0.30.dist-info}/licenses/LICENSE +0 -0
- {rda_python_common-3.0.28.dist-info → rda_python_common-3.0.30.dist-info}/top_level.txt +0 -0
rda_python_common/__init__.py
CHANGED
rda_python_common/pg_sig.py
CHANGED
|
@@ -13,6 +13,7 @@ import re
|
|
|
13
13
|
import sys
|
|
14
14
|
import errno
|
|
15
15
|
import signal
|
|
16
|
+
import select
|
|
16
17
|
import time
|
|
17
18
|
import subprocess
|
|
18
19
|
import psutil
|
|
@@ -52,7 +53,12 @@ class PgSIG(PgDBI):
|
|
|
52
53
|
'MPROC': 1, # default number of multiple processes
|
|
53
54
|
'BPROC': 1, # default number of multiple background processes
|
|
54
55
|
'ETIME': 20, # default error waiting time (in seconds)
|
|
55
|
-
'WTIME': 120, # default waiting time (in seconds)
|
|
56
|
+
'WTIME': 120, # default waiting time (in seconds), the cap of an adaptive wait
|
|
57
|
+
'WTMIN': 5, # shortest adaptive wait (in seconds) for child/background processes
|
|
58
|
+
'WTINC': 5, # seconds added to the adaptive wait after each loop still waiting
|
|
59
|
+
'CWTIME': 0, # current adaptive wait (in seconds), 0 before the first wait
|
|
60
|
+
'WLOOP': -1, # loop count of the last adaptive wait sleep
|
|
61
|
+
'WPIPE': None, # (read fd, write fd) of the pipe SIGCHLD writes to wake up a wait
|
|
56
62
|
'DTIME': 600, # the daemon record refresh time (in seconds)
|
|
57
63
|
'RTIME': 2400, # the web rda config unlocking and unconfigured system down waiting time (in seconds)
|
|
58
64
|
'CTIME': 4800, # the lock cleaning & configued system down waiting time (in seconds)
|
|
@@ -154,6 +160,7 @@ class PgSIG(PgDBI):
|
|
|
154
160
|
os.umask(0)
|
|
155
161
|
# setup to catch signals in daemon only
|
|
156
162
|
signal.signal(signal.SIGCHLD, self.clean_dead_child)
|
|
163
|
+
self.open_wake_pipe()
|
|
157
164
|
signal.signal(signal.SIGQUIT, self.signal_catch)
|
|
158
165
|
signal.signal(signal.SIGUSR1, self.signal_catch)
|
|
159
166
|
signal.signal(signal.SIGUSR2, self.signal_catch)
|
|
@@ -556,7 +563,7 @@ class PgSIG(PgDBI):
|
|
|
556
563
|
signum (int): The signal number received (expected to be SIGCHLD).
|
|
557
564
|
frame: The current stack frame (unused).
|
|
558
565
|
"""
|
|
559
|
-
live = 0
|
|
566
|
+
live = wake = 0
|
|
560
567
|
while True:
|
|
561
568
|
try:
|
|
562
569
|
dpid, status = os.waitpid(-1, os.WNOHANG)
|
|
@@ -570,7 +577,57 @@ class PgSIG(PgDBI):
|
|
|
570
577
|
if live > 0: break # wait twice if a process is still a live
|
|
571
578
|
live += 1
|
|
572
579
|
elif self.PGSIG['PPID'] < 2:
|
|
573
|
-
if dpid in self.CPIDS:
|
|
580
|
+
if dpid in self.CPIDS:
|
|
581
|
+
del self.CPIDS[dpid]
|
|
582
|
+
wake = 1
|
|
583
|
+
if wake: self.wake_waiter() # a managed child is done; end any wait for it now
|
|
584
|
+
|
|
585
|
+
# open the pipe a SIGCHLD handler writes to, so a wait for processes wakes up at once
|
|
586
|
+
def open_wake_pipe(self):
|
|
587
|
+
"""Open the non-blocking self-pipe that wakes ``show_wait_message()`` on SIGCHLD.
|
|
588
|
+
|
|
589
|
+
``clean_dead_child()`` writes one byte to it when a child process in ``CPIDS``
|
|
590
|
+
is done, and ``show_wait_message()`` waits on it with the adaptive wait time as
|
|
591
|
+
the timeout, so a free process slot is used at once instead of after a sleep.
|
|
592
|
+
"""
|
|
593
|
+
if self.PGSIG['WPIPE']: return
|
|
594
|
+
try:
|
|
595
|
+
rfd, wfd = os.pipe()
|
|
596
|
+
os.set_blocking(rfd, False)
|
|
597
|
+
os.set_blocking(wfd, False)
|
|
598
|
+
except OSError as e:
|
|
599
|
+
self.pglog("Cannot open wake pipe, wait by sleeping: {}".format(str(e)), self.LOGWRN)
|
|
600
|
+
return
|
|
601
|
+
self.PGSIG['WPIPE'] = (rfd, wfd)
|
|
602
|
+
|
|
603
|
+
# close the wake pipe, such as in a child process that inherited it
|
|
604
|
+
def close_wake_pipe(self):
|
|
605
|
+
"""Close the SIGCHLD wake pipe and forget it."""
|
|
606
|
+
if not self.PGSIG['WPIPE']: return
|
|
607
|
+
for fd in self.PGSIG['WPIPE']:
|
|
608
|
+
try:
|
|
609
|
+
os.close(fd)
|
|
610
|
+
except OSError:
|
|
611
|
+
pass
|
|
612
|
+
self.PGSIG['WPIPE'] = None
|
|
613
|
+
|
|
614
|
+
# write one byte to the wake pipe to end a wait in show_wait_message()
|
|
615
|
+
def wake_waiter(self):
|
|
616
|
+
"""Wake a wait in ``show_wait_message()``; a full pipe already wakes it."""
|
|
617
|
+
if not self.PGSIG['WPIPE']: return
|
|
618
|
+
try:
|
|
619
|
+
os.write(self.PGSIG['WPIPE'][1], b'x')
|
|
620
|
+
except OSError:
|
|
621
|
+
pass # pipe full (a wake is pending already) or closed
|
|
622
|
+
|
|
623
|
+
# read out all pending wake bytes, so a past wake does not end the next wait
|
|
624
|
+
def drain_wake_pipe(self):
|
|
625
|
+
"""Read out any pending bytes of the SIGCHLD wake pipe."""
|
|
626
|
+
if not self.PGSIG['WPIPE']: return
|
|
627
|
+
try:
|
|
628
|
+
while os.read(self.PGSIG['WPIPE'][0], 512): pass
|
|
629
|
+
except OSError:
|
|
630
|
+
pass # BlockingIOError when empty
|
|
574
631
|
|
|
575
632
|
# send signal to daemon and exit
|
|
576
633
|
def signal_daemon(self, sname, aname, uname):
|
|
@@ -744,6 +801,7 @@ class PgSIG(PgDBI):
|
|
|
744
801
|
self.PGSIG['PID'] = pid = os.getpid()
|
|
745
802
|
self.PGSIG['MPROC'] = 1 # 1 in child process
|
|
746
803
|
self.CBIDS = {} # empty backgroud proces info in case not
|
|
804
|
+
self.close_wake_pipe() # the wake pipe is the parent's
|
|
747
805
|
self.PGSIG['DSTR'] += ": CPID {} for {}".format(pid, pname)
|
|
748
806
|
self.cmdlog("CPID {} for {}".format(pid, pname))
|
|
749
807
|
self.pgdisconnect(0) # disconnect database in child
|
|
@@ -789,6 +847,7 @@ class PgSIG(PgDBI):
|
|
|
789
847
|
if logact&self.EXITLG: logact &= ~self.EXITLG
|
|
790
848
|
ccnt = i = 0
|
|
791
849
|
if dowait < 0: ccnt = 1 if (pid or pname) else self.PGSIG['MPROC']
|
|
850
|
+
self.drain_wake_pipe() # the check below covers any child done before now
|
|
792
851
|
while True:
|
|
793
852
|
pcnt = 0
|
|
794
853
|
if not pid and pname: pid = self.pname2cpid(pname)
|
|
@@ -841,6 +900,7 @@ class PgSIG(PgDBI):
|
|
|
841
900
|
self.check_vuser(uname, aname)
|
|
842
901
|
signal.signal(signal.SIGQUIT, self.signal_catch) # catch quit signal only
|
|
843
902
|
signal.signal(signal.SIGCHLD, self.clean_dead_child)
|
|
903
|
+
self.open_wake_pipe()
|
|
844
904
|
self.PGSIG['DSTR'] = dstr
|
|
845
905
|
self.PGSIG['DNAME'] = aname
|
|
846
906
|
self.PGSIG['PPID'] = 0
|
|
@@ -1281,21 +1341,64 @@ class PgSIG(PgDBI):
|
|
|
1281
1341
|
if wtime: time.sleep(wtime)
|
|
1282
1342
|
return wtime
|
|
1283
1343
|
|
|
1284
|
-
# show wait message every
|
|
1344
|
+
# show wait message every 30 loops and then sleep for an adaptive wait time
|
|
1285
1345
|
def show_wait_message(self, loop, msg, logact=None, dowait=0):
|
|
1286
1346
|
"""Log a wait message every 30 loops and optionally sleep.
|
|
1287
1347
|
|
|
1288
1348
|
Args:
|
|
1289
|
-
loop (int): Current loop iteration count.
|
|
1349
|
+
loop (int): Current loop iteration count, 0 for the first sleep of a wait.
|
|
1290
1350
|
msg (str): Message to log.
|
|
1291
1351
|
logact (int, optional): Log action flags. Defaults to ``LOGWRN``.
|
|
1292
|
-
dowait (int, optional): If non-zero,
|
|
1293
|
-
|
|
1352
|
+
dowait (int, optional): If non-zero, wait for the adaptive wait time
|
|
1353
|
+
from ``adaptive_wait_time()``, at most ``PGSIG['WTIME']`` seconds,
|
|
1354
|
+
or until a child process in ``CPIDS`` is done, whichever is first
|
|
1355
|
+
(see ``open_wake_pipe()``). Defaults to 0.
|
|
1294
1356
|
"""
|
|
1295
1357
|
if logact is None: logact = self.LOGWRN
|
|
1296
1358
|
if loop > 0 and (loop%30) == 0:
|
|
1297
1359
|
self.pglog("{} at {}".format(msg, self.current_datetime()), logact)
|
|
1298
|
-
if dowait:
|
|
1360
|
+
if not dowait: return
|
|
1361
|
+
wtime = self.adaptive_wait_time(loop)
|
|
1362
|
+
if self.PGSIG['WPIPE'] and self.PGSIG['PPID'] < 2:
|
|
1363
|
+
# wake as soon as a child is done; the adaptive time is only the longest wait
|
|
1364
|
+
rfd = self.PGSIG['WPIPE'][0]
|
|
1365
|
+
try:
|
|
1366
|
+
select.select([rfd], [], [], wtime)
|
|
1367
|
+
except (OSError, ValueError):
|
|
1368
|
+
time.sleep(wtime)
|
|
1369
|
+
self.drain_wake_pipe()
|
|
1370
|
+
else:
|
|
1371
|
+
time.sleep(wtime)
|
|
1372
|
+
|
|
1373
|
+
# get the adaptive time to sleep for one loop of a wait for processes
|
|
1374
|
+
def adaptive_wait_time(self, loop):
|
|
1375
|
+
"""Return how long to sleep for one loop of a wait for child/background processes.
|
|
1376
|
+
|
|
1377
|
+
The wait starts at ``PGSIG['WTMIN']`` seconds and grows by ``PGSIG['WTINC']``
|
|
1378
|
+
after each loop that still finds no process done, capped at the polling time
|
|
1379
|
+
``PGSIG['WTIME']``, so long-running processes are not checked too often while
|
|
1380
|
+
short ones are not waited on for the full polling time. The current wait
|
|
1381
|
+
carries over to the next wait; it goes back to ``WTMIN`` when the previous
|
|
1382
|
+
wait was over after its first sleep, since the processes are short. Set
|
|
1383
|
+
``PGSIG['WTMIN']`` to ``WTIME`` or larger for the old fixed wait.
|
|
1384
|
+
|
|
1385
|
+
Args:
|
|
1386
|
+
loop (int): Loop count of the current wait, 0 for its first sleep.
|
|
1387
|
+
|
|
1388
|
+
Returns:
|
|
1389
|
+
int: Seconds to sleep.
|
|
1390
|
+
"""
|
|
1391
|
+
wmax = self.PGSIG['WTIME']
|
|
1392
|
+
wmin = min(self.PGSIG['WTMIN'], wmax)
|
|
1393
|
+
wtime = self.PGSIG['CWTIME']
|
|
1394
|
+
if loop == 0:
|
|
1395
|
+
if not wtime or self.PGSIG['WLOOP'] == 0: wtime = wmin
|
|
1396
|
+
else:
|
|
1397
|
+
wtime += self.PGSIG['WTINC']
|
|
1398
|
+
wtime = max(wmin, min(wtime, wmax))
|
|
1399
|
+
self.PGSIG['CWTIME'] = wtime
|
|
1400
|
+
self.PGSIG['WLOOP'] = loop
|
|
1401
|
+
return wtime
|
|
1299
1402
|
|
|
1300
1403
|
# register a time out function to raise a time out error
|
|
1301
1404
|
@contextmanager
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_common
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.30
|
|
4
4
|
Summary: RDA Python common library codes shared by other RDA python packages
|
|
5
5
|
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/NCAR/rda-python-common
|
|
@@ -189,7 +189,7 @@ PgLOG.pglog("hello", PgLOG.LOGWRN)
|
|
|
189
189
|
python -c "import rda_python_common; print(rda_python_common.__version__)"
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
-
You should see the installed version (currently `3.0.
|
|
192
|
+
You should see the installed version (currently `3.0.30`). If the import
|
|
193
193
|
fails, double-check that the active Python environment is the one where you
|
|
194
194
|
ran `pip install`.
|
|
195
195
|
|
|
@@ -7,7 +7,7 @@ rda_python_common/PgOPT.py,sha256=0nQPF19N-1VWwrjFHEeruVSwVDwvFKd4uowPc8dK7e4,56
|
|
|
7
7
|
rda_python_common/PgSIG.py,sha256=0MJPH0fTtSshIsvUXkzQjReHPyCWdzHYOY5LbFSh7-A,34318
|
|
8
8
|
rda_python_common/PgSplit.py,sha256=En7xcdkE4YNRcMX88qXnrUgUOnQU3Kqh8uxRIdlJhEA,9156
|
|
9
9
|
rda_python_common/PgUtil.py,sha256=7hXAznAI8tYh-nJRKBIXejNe2ROQ2Wxn28OAndeg0Iw,48433
|
|
10
|
-
rda_python_common/__init__.py,sha256=
|
|
10
|
+
rda_python_common/__init__.py,sha256=t_HqEyWCT68DilDBF5TWAQxg5jGDA17eAq42v4-30Js,995
|
|
11
11
|
rda_python_common/pg_cmd.py,sha256=MYldUuM8mSUIT34yKQyUd0i4NL4zdSB8C6Jcc28NRxA,33809
|
|
12
12
|
rda_python_common/pg_dbi.py,sha256=ASIW2vsIjMab2TMfhqJiXS7LQhKKGpKjzNMqo7RCePw,122415
|
|
13
13
|
rda_python_common/pg_file.py,sha256=7NipcgMy4t67zbH2MHtRL3vxyY1FHdIgMTQ-kE0H2fA,181122
|
|
@@ -15,14 +15,14 @@ rda_python_common/pg_lock.py,sha256=31EaVDjCkcx3-n8-KnzG18R8Pz7Z6KyFsEqcml6Iq5c,
|
|
|
15
15
|
rda_python_common/pg_log.py,sha256=erQN4YYPwiORa3OE0Jt8s05rlWsjtyIs0fJ_KLbNAQ8,82959
|
|
16
16
|
rda_python_common/pg_opt.py,sha256=LxVAf47wi2STkVGSXMvV3IZphEJcoO8bvYXy6kQ5ox4,82403
|
|
17
17
|
rda_python_common/pg_password.py,sha256=X-eIDwdqBhtrhrbDTNWle-0JtWsyIVZdDOZaBu7cFHM,2343
|
|
18
|
-
rda_python_common/pg_sig.py,sha256
|
|
18
|
+
rda_python_common/pg_sig.py,sha256=-jTKNmacOztzE2OQCE1GYRPwddDAI9s8voaHGWzgC_s,60653
|
|
19
19
|
rda_python_common/pg_split.py,sha256=aAWKUZPmZ-LQ_fJ3DSKzPKxJw0fMDJ2fgP8ZT629M30,16375
|
|
20
20
|
rda_python_common/pg_util.py,sha256=JL9NoW-VoQvsjXsUr09oBUWYJJNltU4GyQIfWlpkf90,86084
|
|
21
21
|
rda_python_common/pgpassword.py,sha256=aTQaO59QokWrv2EKom4TvKhNW2Mr6aFMMBVG4DCbcKI,5023
|
|
22
22
|
rda_python_common/pgpassword.usg,sha256=Wrle6A8sdlMGx3ZEAmE6TNDPOlG84Whe0T3rCNFzPfY,2013
|
|
23
|
-
rda_python_common-3.0.
|
|
24
|
-
rda_python_common-3.0.
|
|
25
|
-
rda_python_common-3.0.
|
|
26
|
-
rda_python_common-3.0.
|
|
27
|
-
rda_python_common-3.0.
|
|
28
|
-
rda_python_common-3.0.
|
|
23
|
+
rda_python_common-3.0.30.dist-info/licenses/LICENSE,sha256=1dck4EAQwv8QweDWCXDx-4Or0S8YwiCstaso_H57Pno,1097
|
|
24
|
+
rda_python_common-3.0.30.dist-info/METADATA,sha256=r3u-SRHrJBXzM7jTvDuuxxDqSs1gt52BZW7pjYmDz94,14530
|
|
25
|
+
rda_python_common-3.0.30.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
26
|
+
rda_python_common-3.0.30.dist-info/entry_points.txt,sha256=pZgVNWspcK-F1TbPav7C3C9NdeHDZMm_25fW9weix00,65
|
|
27
|
+
rda_python_common-3.0.30.dist-info/top_level.txt,sha256=KVQmx7D3DD-jsiheqL8HdTrRE14hpRnZY5_ioMArA5k,18
|
|
28
|
+
rda_python_common-3.0.30.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|