py2docfx 0.1.20rc2245107__py3-none-any.whl → 0.1.21.dev2249770__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.
Files changed (37) hide show
  1. py2docfx/convert_prepare/get_source.py +1 -1
  2. py2docfx/convert_prepare/package_info.py +37 -27
  3. py2docfx/convert_prepare/tests/test_get_source.py +3 -1
  4. py2docfx/convert_prepare/tests/test_package_info.py +159 -1
  5. py2docfx/docfx_yaml/build_finished.py +1 -1
  6. py2docfx/docfx_yaml/logger.py +42 -28
  7. py2docfx/venv/venv1/Lib/site-packages/psutil/__init__.py +39 -19
  8. py2docfx/venv/venv1/Lib/site-packages/psutil/_common.py +3 -5
  9. py2docfx/venv/venv1/Lib/site-packages/psutil/_psaix.py +1 -2
  10. py2docfx/venv/venv1/Lib/site-packages/psutil/_psbsd.py +53 -78
  11. py2docfx/venv/venv1/Lib/site-packages/psutil/_pslinux.py +55 -38
  12. py2docfx/venv/venv1/Lib/site-packages/psutil/_psosx.py +40 -12
  13. py2docfx/venv/venv1/Lib/site-packages/psutil/_psposix.py +0 -1
  14. py2docfx/venv/venv1/Lib/site-packages/psutil/_pssunos.py +1 -2
  15. py2docfx/venv/venv1/Lib/site-packages/psutil/_pswindows.py +33 -13
  16. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/__init__.py +185 -122
  17. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/__main__.py +2 -3
  18. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_bsd.py +5 -10
  19. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_connections.py +3 -4
  20. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_contracts.py +41 -45
  21. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_linux.py +35 -38
  22. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_memleaks.py +4 -8
  23. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_misc.py +6 -12
  24. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_osx.py +17 -8
  25. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_posix.py +29 -17
  26. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_process.py +74 -75
  27. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_process_all.py +11 -13
  28. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_scripts.py +2 -3
  29. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_sudo.py +117 -0
  30. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_system.py +21 -31
  31. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_testutils.py +23 -23
  32. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_unicode.py +15 -8
  33. py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_windows.py +65 -33
  34. {py2docfx-0.1.20rc2245107.dist-info → py2docfx-0.1.21.dev2249770.dist-info}/METADATA +1 -1
  35. {py2docfx-0.1.20rc2245107.dist-info → py2docfx-0.1.21.dev2249770.dist-info}/RECORD +37 -36
  36. {py2docfx-0.1.20rc2245107.dist-info → py2docfx-0.1.21.dev2249770.dist-info}/WHEEL +0 -0
  37. {py2docfx-0.1.20rc2245107.dist-info → py2docfx-0.1.21.dev2249770.dist-info}/top_level.txt +0 -0
@@ -29,7 +29,6 @@ from ._common import memoize
29
29
  from ._common import memoize_when_activated
30
30
  from ._common import usage_percent
31
31
 
32
-
33
32
  __extra__all__ = []
34
33
 
35
34
 
@@ -98,10 +97,7 @@ TCP_STATUSES = {
98
97
  PAGESIZE = cext_posix.getpagesize()
99
98
  AF_LINK = cext_posix.AF_LINK
100
99
 
101
- HAS_PER_CPU_TIMES = hasattr(cext, "per_cpu_times")
102
100
  HAS_PROC_NUM_THREADS = hasattr(cext, "proc_num_threads")
103
- HAS_PROC_OPEN_FILES = hasattr(cext, 'proc_open_files')
104
- HAS_PROC_NUM_FDS = hasattr(cext, 'proc_num_fds')
105
101
 
106
102
  kinfo_proc_map = dict(
107
103
  ppid=0,
@@ -240,36 +236,14 @@ def cpu_times():
240
236
  return scputimes(user, nice, system, idle, irq)
241
237
 
242
238
 
243
- if HAS_PER_CPU_TIMES:
244
-
245
- def per_cpu_times():
246
- """Return system CPU times as a namedtuple."""
247
- ret = []
248
- for cpu_t in cext.per_cpu_times():
249
- user, nice, system, idle, irq = cpu_t
250
- item = scputimes(user, nice, system, idle, irq)
251
- ret.append(item)
252
- return ret
253
-
254
- else:
255
- # XXX
256
- # Ok, this is very dirty.
257
- # On FreeBSD < 8 we cannot gather per-cpu information, see:
258
- # https://github.com/giampaolo/psutil/issues/226
259
- # If num cpus > 1, on first call we return single cpu times to avoid a
260
- # crash at psutil import time.
261
- # Next calls will fail with NotImplementedError
262
- def per_cpu_times():
263
- """Return system CPU times as a namedtuple."""
264
- if cpu_count_logical() == 1:
265
- return [cpu_times()]
266
- if per_cpu_times.__called__:
267
- msg = "supported only starting from FreeBSD 8"
268
- raise NotImplementedError(msg)
269
- per_cpu_times.__called__ = True
270
- return [cpu_times()]
271
-
272
- per_cpu_times.__called__ = False
239
+ def per_cpu_times():
240
+ """Return system CPU times as a namedtuple."""
241
+ ret = []
242
+ for cpu_t in cext.per_cpu_times():
243
+ user, nice, system, idle, irq = cpu_t
244
+ item = scputimes(user, nice, system, idle, irq)
245
+ ret.append(item)
246
+ return ret
273
247
 
274
248
 
275
249
  def cpu_count_logical():
@@ -505,15 +479,36 @@ def boot_time():
505
479
  return cext.boot_time()
506
480
 
507
481
 
482
+ if NETBSD:
483
+
484
+ try:
485
+ INIT_BOOT_TIME = boot_time()
486
+ except Exception as err: # noqa: BLE001
487
+ # Don't want to crash at import time.
488
+ debug(f"ignoring exception on import: {err!r}")
489
+ INIT_BOOT_TIME = 0
490
+
491
+ def adjust_proc_create_time(ctime):
492
+ """Account for system clock updates."""
493
+ if INIT_BOOT_TIME == 0:
494
+ return ctime
495
+
496
+ diff = INIT_BOOT_TIME - boot_time()
497
+ if diff == 0 or abs(diff) < 1:
498
+ return ctime
499
+
500
+ debug("system clock was updated; adjusting process create_time()")
501
+ if diff < 0:
502
+ return ctime - diff
503
+ return ctime + diff
504
+
505
+
508
506
  def users():
509
507
  """Return currently connected users as a list of namedtuples."""
510
508
  retlist = []
511
- rawlist = cext.users()
509
+ rawlist = cext.users() if OPENBSD else cext_posix.users()
512
510
  for item in rawlist:
513
511
  user, tty, hostname, tstamp, pid = item
514
- if pid == -1:
515
- assert OPENBSD
516
- pid = None
517
512
  if tty == '~':
518
513
  continue # reboot or shutdown
519
514
  nt = _common.suser(user, tty or None, hostname, tstamp, pid)
@@ -779,13 +774,17 @@ class Process:
779
774
  memory_full_info = memory_info
780
775
 
781
776
  @wrap_exceptions
782
- def create_time(self):
783
- return self.oneshot()[kinfo_proc_map['create_time']]
777
+ def create_time(self, monotonic=False):
778
+ ctime = self.oneshot()[kinfo_proc_map['create_time']]
779
+ if NETBSD and not monotonic:
780
+ # NetBSD: ctime subject to system clock updates.
781
+ ctime = adjust_proc_create_time(ctime)
782
+ return ctime
784
783
 
785
784
  @wrap_exceptions
786
785
  def num_threads(self):
787
786
  if HAS_PROC_NUM_THREADS:
788
- # FreeBSD
787
+ # FreeBSD / NetBSD
789
788
  return cext.proc_num_threads(self.pid)
790
789
  else:
791
790
  return len(self.threads())
@@ -870,14 +869,7 @@ class Process:
870
869
  # it into None
871
870
  if OPENBSD and self.pid == 0:
872
871
  return "" # ...else it would raise EINVAL
873
- elif NETBSD or HAS_PROC_OPEN_FILES:
874
- # FreeBSD < 8 does not support functions based on
875
- # kinfo_getfile() and kinfo_getvmmap()
876
- return cext.proc_cwd(self.pid)
877
- else:
878
- raise NotImplementedError(
879
- "supported only starting from FreeBSD 8" if FREEBSD else ""
880
- )
872
+ return cext.proc_cwd(self.pid)
881
873
 
882
874
  nt_mmap_grouped = namedtuple(
883
875
  'mmap', 'path rss, private, ref_count, shadow_count'
@@ -886,36 +878,19 @@ class Process:
886
878
  'mmap', 'addr, perms path rss, private, ref_count, shadow_count'
887
879
  )
888
880
 
889
- def _not_implemented(self):
890
- raise NotImplementedError
891
-
892
- # FreeBSD < 8 does not support functions based on kinfo_getfile()
893
- # and kinfo_getvmmap()
894
- if HAS_PROC_OPEN_FILES:
895
-
896
- @wrap_exceptions
897
- def open_files(self):
898
- """Return files opened by process as a list of namedtuples."""
899
- rawlist = cext.proc_open_files(self.pid)
900
- return [_common.popenfile(path, fd) for path, fd in rawlist]
901
-
902
- else:
903
- open_files = _not_implemented
904
-
905
- # FreeBSD < 8 does not support functions based on kinfo_getfile()
906
- # and kinfo_getvmmap()
907
- if HAS_PROC_NUM_FDS:
908
-
909
- @wrap_exceptions
910
- def num_fds(self):
911
- """Return the number of file descriptors opened by this process."""
912
- ret = cext.proc_num_fds(self.pid)
913
- if NETBSD:
914
- self._assert_alive()
915
- return ret
881
+ @wrap_exceptions
882
+ def open_files(self):
883
+ """Return files opened by process as a list of namedtuples."""
884
+ rawlist = cext.proc_open_files(self.pid)
885
+ return [_common.popenfile(path, fd) for path, fd in rawlist]
916
886
 
917
- else:
918
- num_fds = _not_implemented
887
+ @wrap_exceptions
888
+ def num_fds(self):
889
+ """Return the number of file descriptors opened by this process."""
890
+ ret = cext.proc_num_fds(self.pid)
891
+ if NETBSD:
892
+ self._assert_alive()
893
+ return ret
919
894
 
920
895
  # --- FreeBSD only APIs
921
896
 
@@ -47,7 +47,6 @@ from ._common import path_exists_strict
47
47
  from ._common import supports_ipv6
48
48
  from ._common import usage_percent
49
49
 
50
-
51
50
  # fmt: off
52
51
  __extra__all__ = [
53
52
  'PROCFS_PATH',
@@ -81,8 +80,8 @@ HAS_CPU_AFFINITY = hasattr(cext, "proc_cpu_affinity_get")
81
80
  # Number of clock ticks per second
82
81
  CLOCK_TICKS = os.sysconf("SC_CLK_TCK")
83
82
  PAGESIZE = cext_posix.getpagesize()
84
- BOOT_TIME = None # set later
85
83
  LITTLE_ENDIAN = sys.byteorder == 'little'
84
+ UNSET = object()
86
85
 
87
86
  # "man iostat" states that sectors are equivalent with blocks and have
88
87
  # a size of 512 bytes. Despite this value can be queried at runtime
@@ -421,12 +420,6 @@ def virtual_memory():
421
420
  except KeyError:
422
421
  slab = 0
423
422
 
424
- used = total - free - cached - buffers
425
- if used < 0:
426
- # May be symptomatic of running within a LCX container where such
427
- # values will be dramatically distorted over those of the host.
428
- used = total - free
429
-
430
423
  # - starting from 4.4.0 we match free's "available" column.
431
424
  # Before 4.4.0 we calculated it as (free + buffers + cached)
432
425
  # which matched htop.
@@ -457,6 +450,8 @@ def virtual_memory():
457
450
  # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764
458
451
  avail = free
459
452
 
453
+ used = total - avail
454
+
460
455
  percent = usage_percent((total - avail), total, round_=1)
461
456
 
462
457
  # Warn about missing metrics which are set to 0.
@@ -1525,7 +1520,7 @@ def sensors_battery():
1525
1520
  secsleft = _common.POWER_TIME_UNLIMITED
1526
1521
  elif energy_now is not None and power_now is not None:
1527
1522
  try:
1528
- secsleft = int(energy_now / power_now * 3600)
1523
+ secsleft = int(energy_now / abs(power_now) * 3600)
1529
1524
  except ZeroDivisionError:
1530
1525
  secsleft = _common.POWER_TIME_UNKNOWN
1531
1526
  elif time_to_empty is not None:
@@ -1546,7 +1541,7 @@ def sensors_battery():
1546
1541
  def users():
1547
1542
  """Return currently connected users as a list of namedtuples."""
1548
1543
  retlist = []
1549
- rawlist = cext.users()
1544
+ rawlist = cext_posix.users()
1550
1545
  for item in rawlist:
1551
1546
  user, tty, hostname, tstamp, pid = item
1552
1547
  nt = _common.suser(user, tty or None, hostname, tstamp, pid)
@@ -1556,14 +1551,11 @@ def users():
1556
1551
 
1557
1552
  def boot_time():
1558
1553
  """Return the system boot time expressed in seconds since the epoch."""
1559
- global BOOT_TIME
1560
1554
  path = f"{get_procfs_path()}/stat"
1561
1555
  with open_binary(path) as f:
1562
1556
  for line in f:
1563
1557
  if line.startswith(b'btime'):
1564
- ret = float(line.strip().split()[1])
1565
- BOOT_TIME = ret
1566
- return ret
1558
+ return float(line.strip().split()[1])
1567
1559
  msg = f"line 'btime' not found in {path}"
1568
1560
  raise RuntimeError(msg)
1569
1561
 
@@ -1623,9 +1615,9 @@ def ppid_map():
1623
1615
  with open_binary(f"{procfs_path}/{pid}/stat") as f:
1624
1616
  data = f.read()
1625
1617
  except (FileNotFoundError, ProcessLookupError):
1626
- # Note: we should be able to access /stat for all processes
1627
- # aka it's unlikely we'll bump into EPERM, which is good.
1628
1618
  pass
1619
+ except PermissionError as err:
1620
+ raise AccessDenied(pid) from err
1629
1621
  else:
1630
1622
  rpar = data.rfind(b')')
1631
1623
  dset = data[rpar + 2 :].split()
@@ -1664,12 +1656,20 @@ def wrap_exceptions(fun):
1664
1656
  class Process:
1665
1657
  """Linux process implementation."""
1666
1658
 
1667
- __slots__ = ["_cache", "_name", "_ppid", "_procfs_path", "pid"]
1659
+ __slots__ = [
1660
+ "_cache",
1661
+ "_ctime",
1662
+ "_name",
1663
+ "_ppid",
1664
+ "_procfs_path",
1665
+ "pid",
1666
+ ]
1668
1667
 
1669
1668
  def __init__(self, pid):
1670
1669
  self.pid = pid
1671
1670
  self._name = None
1672
1671
  self._ppid = None
1672
+ self._ctime = None
1673
1673
  self._procfs_path = get_procfs_path()
1674
1674
 
1675
1675
  def _is_zombie(self):
@@ -1698,6 +1698,22 @@ class Process:
1698
1698
  # incorrect or incomplete result.
1699
1699
  os.stat(f"{self._procfs_path}/{self.pid}")
1700
1700
 
1701
+ def _readlink(self, path, fallback=UNSET):
1702
+ # * https://github.com/giampaolo/psutil/issues/503
1703
+ # os.readlink('/proc/pid/exe') may raise ESRCH (ProcessLookupError)
1704
+ # instead of ENOENT (FileNotFoundError) when it races.
1705
+ # * ENOENT may occur also if the path actually exists if PID is
1706
+ # a low PID (~0-20 range).
1707
+ # * https://github.com/giampaolo/psutil/issues/2514
1708
+ try:
1709
+ return readlink(path)
1710
+ except (FileNotFoundError, ProcessLookupError):
1711
+ if os.path.lexists(f"{self._procfs_path}/{self.pid}"):
1712
+ self._raise_if_zombie()
1713
+ if fallback is not UNSET:
1714
+ return fallback
1715
+ raise
1716
+
1701
1717
  @wrap_exceptions
1702
1718
  @memoize_when_activated
1703
1719
  def _parse_stat_file(self):
@@ -1770,16 +1786,9 @@ class Process:
1770
1786
 
1771
1787
  @wrap_exceptions
1772
1788
  def exe(self):
1773
- try:
1774
- return readlink(f"{self._procfs_path}/{self.pid}/exe")
1775
- except (FileNotFoundError, ProcessLookupError):
1776
- self._raise_if_zombie()
1777
- # no such file error; might be raised also if the
1778
- # path actually exists for system processes with
1779
- # low pids (about 0-20)
1780
- if os.path.lexists(f"{self._procfs_path}/{self.pid}"):
1781
- return ""
1782
- raise
1789
+ return self._readlink(
1790
+ f"{self._procfs_path}/{self.pid}/exe", fallback=""
1791
+ )
1783
1792
 
1784
1793
  @wrap_exceptions
1785
1794
  def cmdline(self):
@@ -1880,15 +1889,21 @@ class Process:
1880
1889
  return _psposix.wait_pid(self.pid, timeout, self._name)
1881
1890
 
1882
1891
  @wrap_exceptions
1883
- def create_time(self):
1884
- ctime = float(self._parse_stat_file()['create_time'])
1885
- # According to documentation, starttime is in field 21 and the
1886
- # unit is jiffies (clock ticks).
1887
- # We first divide it for clock ticks and then add uptime returning
1888
- # seconds since the epoch.
1889
- # Also use cached value if available.
1890
- bt = BOOT_TIME or boot_time()
1891
- return (ctime / CLOCK_TICKS) + bt
1892
+ def create_time(self, monotonic=False):
1893
+ # The 'starttime' field in /proc/[pid]/stat is expressed in
1894
+ # jiffies (clock ticks per second), a relative value which
1895
+ # represents the number of clock ticks that have passed since
1896
+ # the system booted until the process was created. It never
1897
+ # changes and is unaffected by system clock updates.
1898
+ if self._ctime is None:
1899
+ self._ctime = (
1900
+ float(self._parse_stat_file()['create_time']) / CLOCK_TICKS
1901
+ )
1902
+ if monotonic:
1903
+ return self._ctime
1904
+ # Add the boot time, returning time expressed in seconds since
1905
+ # the epoch. This is subject to system clock updates.
1906
+ return self._ctime + boot_time()
1892
1907
 
1893
1908
  @wrap_exceptions
1894
1909
  def memory_info(self):
@@ -2001,7 +2016,7 @@ class Process:
2001
2016
  else:
2002
2017
  try:
2003
2018
  data[fields[0]] = int(fields[1]) * 1024
2004
- except ValueError:
2019
+ except (ValueError, IndexError):
2005
2020
  if fields[0].startswith(b'VmFlags:'):
2006
2021
  # see issue #369
2007
2022
  continue
@@ -2054,7 +2069,9 @@ class Process:
2054
2069
 
2055
2070
  @wrap_exceptions
2056
2071
  def cwd(self):
2057
- return readlink(f"{self._procfs_path}/{self.pid}/cwd")
2072
+ return self._readlink(
2073
+ f"{self._procfs_path}/{self.pid}/cwd", fallback=""
2074
+ )
2058
2075
 
2059
2076
  @wrap_exceptions
2060
2077
  def num_ctx_switches(
@@ -18,12 +18,12 @@ from ._common import NoSuchProcess
18
18
  from ._common import ZombieProcess
19
19
  from ._common import conn_tmap
20
20
  from ._common import conn_to_ntuple
21
+ from ._common import debug
21
22
  from ._common import isfile_strict
22
23
  from ._common import memoize_when_activated
23
24
  from ._common import parse_environ_block
24
25
  from ._common import usage_percent
25
26
 
26
-
27
27
  __extra__all__ = []
28
28
 
29
29
 
@@ -170,14 +170,16 @@ def cpu_stats():
170
170
  )
171
171
 
172
172
 
173
- def cpu_freq():
174
- """Return CPU frequency.
175
- On macOS per-cpu frequency is not supported.
176
- Also, the returned frequency never changes, see:
177
- https://arstechnica.com/civis/viewtopic.php?f=19&t=465002.
178
- """
179
- curr, min_, max_ = cext.cpu_freq()
180
- return [_common.scpufreq(curr, min_, max_)]
173
+ if cext.has_cpu_freq(): # not always available on ARM64
174
+
175
+ def cpu_freq():
176
+ """Return CPU frequency.
177
+ On macOS per-cpu frequency is not supported.
178
+ Also, the returned frequency never changes, see:
179
+ https://arstechnica.com/civis/viewtopic.php?f=19&t=465002.
180
+ """
181
+ curr, min_, max_ = cext.cpu_freq()
182
+ return [_common.scpufreq(curr, min_, max_)]
181
183
 
182
184
 
183
185
  # =====================================================================
@@ -288,10 +290,33 @@ def boot_time():
288
290
  return cext.boot_time()
289
291
 
290
292
 
293
+ try:
294
+ INIT_BOOT_TIME = boot_time()
295
+ except Exception as err: # noqa: BLE001
296
+ # Don't want to crash at import time.
297
+ debug(f"ignoring exception on import: {err!r}")
298
+ INIT_BOOT_TIME = 0
299
+
300
+
301
+ def adjust_proc_create_time(ctime):
302
+ """Account for system clock updates."""
303
+ if INIT_BOOT_TIME == 0:
304
+ return ctime
305
+
306
+ diff = INIT_BOOT_TIME - boot_time()
307
+ if diff == 0 or abs(diff) < 1:
308
+ return ctime
309
+
310
+ debug("system clock was updated; adjusting process create_time()")
311
+ if diff < 0:
312
+ return ctime - diff
313
+ return ctime + diff
314
+
315
+
291
316
  def users():
292
317
  """Return currently connected users as a list of namedtuples."""
293
318
  retlist = []
294
- rawlist = cext.users()
319
+ rawlist = cext_posix.users()
295
320
  for item in rawlist:
296
321
  user, tty, hostname, tstamp, pid = item
297
322
  if tty == '~':
@@ -470,8 +495,11 @@ class Process:
470
495
  )
471
496
 
472
497
  @wrap_exceptions
473
- def create_time(self):
474
- return self._get_kinfo_proc()[kinfo_proc_map['ctime']]
498
+ def create_time(self, monotonic=False):
499
+ ctime = self._get_kinfo_proc()[kinfo_proc_map['ctime']]
500
+ if not monotonic:
501
+ ctime = adjust_proc_create_time(ctime)
502
+ return ctime
475
503
 
476
504
  @wrap_exceptions
477
505
  def num_ctx_switches(self):
@@ -16,7 +16,6 @@ from ._common import memoize
16
16
  from ._common import sdiskusage
17
17
  from ._common import usage_percent
18
18
 
19
-
20
19
  if MACOS:
21
20
  from . import _psutil_osx
22
21
 
@@ -30,7 +30,6 @@ from ._common import sockfam_to_enum
30
30
  from ._common import socktype_to_enum
31
31
  from ._common import usage_percent
32
32
 
33
-
34
33
  __extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"]
35
34
 
36
35
 
@@ -311,7 +310,7 @@ def boot_time():
311
310
  def users():
312
311
  """Return currently connected users as a list of namedtuples."""
313
312
  retlist = []
314
- rawlist = cext.users()
313
+ rawlist = cext_posix.users()
315
314
  localhost = (':0.0', ':0')
316
315
  for item in rawlist:
317
316
  user, tty, hostname, tstamp, user_process, pid = item
@@ -10,6 +10,7 @@ import functools
10
10
  import os
11
11
  import signal
12
12
  import sys
13
+ import threading
13
14
  import time
14
15
  from collections import namedtuple
15
16
 
@@ -33,7 +34,6 @@ from ._psutil_windows import IDLE_PRIORITY_CLASS
33
34
  from ._psutil_windows import NORMAL_PRIORITY_CLASS
34
35
  from ._psutil_windows import REALTIME_PRIORITY_CLASS
35
36
 
36
-
37
37
  try:
38
38
  from . import _psutil_windows as cext
39
39
  except ImportError as err:
@@ -184,12 +184,21 @@ pio = namedtuple('pio', ['read_count', 'write_count',
184
184
  @functools.lru_cache(maxsize=512)
185
185
  def convert_dos_path(s):
186
186
  r"""Convert paths using native DOS format like:
187
- "\Device\HarddiskVolume1\Windows\systemew\file.txt"
187
+ "\Device\HarddiskVolume1\Windows\systemew\file.txt" or
188
+ "\??\C:\Windows\systemew\file.txt"
188
189
  into:
189
190
  "C:\Windows\systemew\file.txt".
190
191
  """
192
+ if s.startswith('\\\\'):
193
+ return s
191
194
  rawdrive = '\\'.join(s.split('\\')[:3])
192
- driveletter = cext.QueryDosDevice(rawdrive)
195
+ if rawdrive in {"\\??\\UNC", "\\Device\\Mup"}:
196
+ rawdrive = '\\'.join(s.split('\\')[:5])
197
+ driveletter = '\\\\' + '\\'.join(s.split('\\')[3:5])
198
+ elif rawdrive.startswith('\\??\\'):
199
+ driveletter = s.split('\\')[2]
200
+ else:
201
+ driveletter = cext.QueryDosDevice(rawdrive)
193
202
  remainder = s[len(rawdrive) :]
194
203
  return os.path.join(driveletter, remainder)
195
204
 
@@ -322,22 +331,31 @@ def cpu_freq():
322
331
  return [_common.scpufreq(float(curr), min_, float(max_))]
323
332
 
324
333
 
325
- _loadavg_inititialized = False
334
+ _loadavg_initialized = False
335
+ _lock = threading.Lock()
336
+
337
+
338
+ def _getloadavg_impl():
339
+ # Drop to 2 decimal points which is what Linux does
340
+ raw_loads = cext.getloadavg()
341
+ return tuple(round(load, 2) for load in raw_loads)
326
342
 
327
343
 
328
344
  def getloadavg():
329
345
  """Return the number of processes in the system run queue averaged
330
346
  over the last 1, 5, and 15 minutes respectively as a tuple.
331
347
  """
332
- global _loadavg_inititialized
348
+ global _loadavg_initialized
333
349
 
334
- if not _loadavg_inititialized:
335
- cext.init_loadavg_counter()
336
- _loadavg_inititialized = True
350
+ if _loadavg_initialized:
351
+ return _getloadavg_impl()
337
352
 
338
- # Drop to 2 decimal points which is what Linux does
339
- raw_loads = cext.getloadavg()
340
- return tuple(round(load, 2) for load in raw_loads)
353
+ with _lock:
354
+ if not _loadavg_initialized:
355
+ cext.init_loadavg_counter()
356
+ _loadavg_initialized = True
357
+
358
+ return _getloadavg_impl()
341
359
 
342
360
 
343
361
  # =====================================================================
@@ -426,12 +444,14 @@ _last_btime = 0
426
444
 
427
445
 
428
446
  def boot_time():
429
- """The system boot time expressed in seconds since the epoch."""
447
+ """The system boot time expressed in seconds since the epoch. This
448
+ also includes the time spent during hybernate / suspend.
449
+ """
430
450
  # This dirty hack is to adjust the precision of the returned
431
451
  # value which may have a 1 second fluctuation, see:
432
452
  # https://github.com/giampaolo/psutil/issues/1007
433
453
  global _last_btime
434
- ret = float(cext.boot_time())
454
+ ret = time.time() - cext.uptime()
435
455
  if abs(ret - _last_btime) <= 1:
436
456
  return _last_btime
437
457
  else: