rda-python-common 3.0.29__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.
@@ -22,7 +22,7 @@ object that existing callers expect.
22
22
 
23
23
  from . import PgLOG, PgUtil, PgDBI, PgFile, PgLock, PgCMD, PgSIG, PgOPT, PgSplit
24
24
 
25
- __version__ = "3.0.29"
25
+ __version__ = "3.0.30"
26
26
 
27
27
  __all__ = [
28
28
  "PgLOG",
@@ -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
@@ -57,6 +58,7 @@ class PgSIG(PgDBI):
57
58
  'WTINC': 5, # seconds added to the adaptive wait after each loop still waiting
58
59
  'CWTIME': 0, # current adaptive wait (in seconds), 0 before the first wait
59
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
60
62
  'DTIME': 600, # the daemon record refresh time (in seconds)
61
63
  'RTIME': 2400, # the web rda config unlocking and unconfigured system down waiting time (in seconds)
62
64
  'CTIME': 4800, # the lock cleaning & configued system down waiting time (in seconds)
@@ -158,6 +160,7 @@ class PgSIG(PgDBI):
158
160
  os.umask(0)
159
161
  # setup to catch signals in daemon only
160
162
  signal.signal(signal.SIGCHLD, self.clean_dead_child)
163
+ self.open_wake_pipe()
161
164
  signal.signal(signal.SIGQUIT, self.signal_catch)
162
165
  signal.signal(signal.SIGUSR1, self.signal_catch)
163
166
  signal.signal(signal.SIGUSR2, self.signal_catch)
@@ -560,7 +563,7 @@ class PgSIG(PgDBI):
560
563
  signum (int): The signal number received (expected to be SIGCHLD).
561
564
  frame: The current stack frame (unused).
562
565
  """
563
- live = 0
566
+ live = wake = 0
564
567
  while True:
565
568
  try:
566
569
  dpid, status = os.waitpid(-1, os.WNOHANG)
@@ -574,7 +577,57 @@ class PgSIG(PgDBI):
574
577
  if live > 0: break # wait twice if a process is still a live
575
578
  live += 1
576
579
  elif self.PGSIG['PPID'] < 2:
577
- if dpid in self.CPIDS: del self.CPIDS[dpid]
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
578
631
 
579
632
  # send signal to daemon and exit
580
633
  def signal_daemon(self, sname, aname, uname):
@@ -748,6 +801,7 @@ class PgSIG(PgDBI):
748
801
  self.PGSIG['PID'] = pid = os.getpid()
749
802
  self.PGSIG['MPROC'] = 1 # 1 in child process
750
803
  self.CBIDS = {} # empty backgroud proces info in case not
804
+ self.close_wake_pipe() # the wake pipe is the parent's
751
805
  self.PGSIG['DSTR'] += ": CPID {} for {}".format(pid, pname)
752
806
  self.cmdlog("CPID {} for {}".format(pid, pname))
753
807
  self.pgdisconnect(0) # disconnect database in child
@@ -793,6 +847,7 @@ class PgSIG(PgDBI):
793
847
  if logact&self.EXITLG: logact &= ~self.EXITLG
794
848
  ccnt = i = 0
795
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
796
851
  while True:
797
852
  pcnt = 0
798
853
  if not pid and pname: pid = self.pname2cpid(pname)
@@ -845,6 +900,7 @@ class PgSIG(PgDBI):
845
900
  self.check_vuser(uname, aname)
846
901
  signal.signal(signal.SIGQUIT, self.signal_catch) # catch quit signal only
847
902
  signal.signal(signal.SIGCHLD, self.clean_dead_child)
903
+ self.open_wake_pipe()
848
904
  self.PGSIG['DSTR'] = dstr
849
905
  self.PGSIG['DNAME'] = aname
850
906
  self.PGSIG['PPID'] = 0
@@ -1293,14 +1349,26 @@ class PgSIG(PgDBI):
1293
1349
  loop (int): Current loop iteration count, 0 for the first sleep of a wait.
1294
1350
  msg (str): Message to log.
1295
1351
  logact (int, optional): Log action flags. Defaults to ``LOGWRN``.
1296
- dowait (int, optional): If non-zero, sleep for the adaptive wait time
1297
- from ``adaptive_wait_time()``, at most ``PGSIG['WTIME']`` seconds.
1298
- Defaults to 0.
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.
1299
1356
  """
1300
1357
  if logact is None: logact = self.LOGWRN
1301
1358
  if loop > 0 and (loop%30) == 0:
1302
1359
  self.pglog("{} at {}".format(msg, self.current_datetime()), logact)
1303
- if dowait: time.sleep(self.adaptive_wait_time(loop))
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)
1304
1372
 
1305
1373
  # get the adaptive time to sleep for one loop of a wait for processes
1306
1374
  def adaptive_wait_time(self, loop):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_common
3
- Version: 3.0.29
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.29`). If the import
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=sBVlfOY9ijDUndeN_oeqUihFhQ4l5ZUfT91PeQTPoVE,995
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=wLqg-SxvF-tu-9zlpRsiszOb141L_YbxD-SGVnrCTPg,57828
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.29.dist-info/licenses/LICENSE,sha256=1dck4EAQwv8QweDWCXDx-4Or0S8YwiCstaso_H57Pno,1097
24
- rda_python_common-3.0.29.dist-info/METADATA,sha256=51mKJd7UiXR8giX2wKBeFpDIpPd5875s4hEAJ5RifMg,14530
25
- rda_python_common-3.0.29.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
26
- rda_python_common-3.0.29.dist-info/entry_points.txt,sha256=pZgVNWspcK-F1TbPav7C3C9NdeHDZMm_25fW9weix00,65
27
- rda_python_common-3.0.29.dist-info/top_level.txt,sha256=KVQmx7D3DD-jsiheqL8HdTrRE14hpRnZY5_ioMArA5k,18
28
- rda_python_common-3.0.29.dist-info/RECORD,,
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,,