py2docfx 0.1.20.dev2245319__py3-none-any.whl → 0.1.20.dev2246325__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.
- py2docfx/convert_prepare/package_info.py +27 -37
- py2docfx/convert_prepare/pip_utils.py +1 -3
- py2docfx/convert_prepare/tests/test_package_info.py +1 -159
- py2docfx/venv/venv1/Lib/site-packages/psutil/__init__.py +39 -19
- py2docfx/venv/venv1/Lib/site-packages/psutil/_common.py +3 -5
- py2docfx/venv/venv1/Lib/site-packages/psutil/_psaix.py +1 -2
- py2docfx/venv/venv1/Lib/site-packages/psutil/_psbsd.py +53 -78
- py2docfx/venv/venv1/Lib/site-packages/psutil/_pslinux.py +55 -38
- py2docfx/venv/venv1/Lib/site-packages/psutil/_psosx.py +40 -12
- py2docfx/venv/venv1/Lib/site-packages/psutil/_psposix.py +0 -1
- py2docfx/venv/venv1/Lib/site-packages/psutil/_pssunos.py +1 -2
- py2docfx/venv/venv1/Lib/site-packages/psutil/_pswindows.py +33 -13
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/__init__.py +185 -122
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/__main__.py +2 -3
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_bsd.py +5 -10
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_connections.py +3 -4
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_contracts.py +41 -45
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_linux.py +35 -38
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_memleaks.py +4 -8
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_misc.py +6 -12
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_osx.py +17 -8
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_posix.py +29 -17
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_process.py +74 -75
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_process_all.py +11 -13
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_scripts.py +2 -3
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_sudo.py +117 -0
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_system.py +21 -31
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_testutils.py +23 -23
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_unicode.py +15 -8
- py2docfx/venv/venv1/Lib/site-packages/psutil/tests/test_windows.py +65 -33
- {py2docfx-0.1.20.dev2245319.dist-info → py2docfx-0.1.20.dev2246325.dist-info}/METADATA +1 -1
- {py2docfx-0.1.20.dev2245319.dist-info → py2docfx-0.1.20.dev2246325.dist-info}/RECORD +34 -33
- {py2docfx-0.1.20.dev2245319.dist-info → py2docfx-0.1.20.dev2246325.dist-info}/WHEEL +0 -0
- {py2docfx-0.1.20.dev2245319.dist-info → py2docfx-0.1.20.dev2246325.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,7 @@ class PackageInfo:
|
|
13
13
|
path: Source
|
14
14
|
def __init__(self) -> None:
|
15
15
|
pass
|
16
|
-
|
16
|
+
|
17
17
|
@classmethod
|
18
18
|
def report_error(cls, name, value, condition=None):
|
19
19
|
py2docfx_logger = get_logger(__name__)
|
@@ -53,7 +53,6 @@ class PackageInfo:
|
|
53
53
|
|
54
54
|
package_info.version = package_info_dict.get("version", None)
|
55
55
|
package_info.extra_index_url = package_info_dict.get("extra_index_url", None)
|
56
|
-
package_info.extras = package_info_dict.get("extras", [])
|
57
56
|
|
58
57
|
if package_info.install_type == cls.InstallType.SOURCE_CODE:
|
59
58
|
package_info.url = package_info_dict.get("url", None)
|
@@ -94,65 +93,56 @@ class PackageInfo:
|
|
94
93
|
return package_info
|
95
94
|
|
96
95
|
def get_combined_name_version(self):
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
)
|
102
|
-
|
103
|
-
version = getattr(self, "version", None)
|
104
|
-
if not version:
|
105
|
-
return base_name
|
106
|
-
elif re.match("^(<|>|<=|>=|==).+$", version.strip()):
|
107
|
-
return base_name.strip() + version.strip()
|
96
|
+
if not self.version:
|
97
|
+
return self.name
|
98
|
+
elif re.match("^(<|>|<=|>=|==).+$", self.version.strip()):
|
99
|
+
return self.name.strip() + self.version.strip()
|
108
100
|
else:
|
109
|
-
return f"{
|
101
|
+
return f"{self.name.strip()}=={self.version.strip()}"
|
102
|
+
|
103
|
+
def get_install_command(self) -> (str, []):
|
104
|
+
packageInstallName = ""
|
105
|
+
pipInstallExtraOptions = []
|
110
106
|
|
111
|
-
def get_install_command(self) -> tuple[str, list]:
|
112
107
|
if self.install_type == self.InstallType.DIST_FILE:
|
113
|
-
if
|
108
|
+
if hasattr(self, "location") and self.location:
|
109
|
+
packageInstallName = self.location
|
110
|
+
else:
|
114
111
|
self.__class__.report_error(
|
115
112
|
"location", "None", condition="When install_type is dist_file"
|
116
113
|
)
|
117
|
-
return (
|
118
|
-
f"{self.location}[{','.join(extras)}]"
|
119
|
-
if (extras := getattr(self, "extras", None))
|
120
|
-
else self.location,
|
121
|
-
[],
|
122
|
-
)
|
123
114
|
|
124
|
-
|
115
|
+
elif self.install_type == self.InstallType.PYPI:
|
125
116
|
if not hasattr(self, "name") or not self.name:
|
126
117
|
self.__class__.report_error(
|
127
118
|
"name", "None", condition="When install_type is pypi"
|
128
119
|
)
|
129
|
-
|
130
|
-
|
120
|
+
if hasattr(self, "version") and self.version:
|
121
|
+
packageInstallName = self.get_combined_name_version()
|
122
|
+
else:
|
123
|
+
packageInstallName = self.name
|
131
124
|
pipInstallExtraOptions.append("--upgrade")
|
125
|
+
|
132
126
|
if hasattr(self, "extra_index_url") and self.extra_index_url:
|
133
127
|
pipInstallExtraOptions.extend(
|
134
128
|
["--extra-index-url", self.extra_index_url]
|
135
129
|
)
|
136
|
-
return (self.get_combined_name_version(), pipInstallExtraOptions)
|
137
130
|
|
138
|
-
|
139
|
-
if
|
131
|
+
elif self.install_type == self.InstallType.SOURCE_CODE:
|
132
|
+
if hasattr(self, "path") and self.path.source_folder:
|
133
|
+
packageInstallName = self.path.source_folder
|
134
|
+
else:
|
140
135
|
self.__class__.report_error(
|
141
136
|
"path.source_folder",
|
142
137
|
"None",
|
143
138
|
condition="When install_type is source_code",
|
144
139
|
)
|
145
|
-
|
146
|
-
|
147
|
-
if (extras := getattr(self, "extras", None))
|
148
|
-
else self.path.source_folder,
|
149
|
-
[],
|
150
|
-
)
|
151
|
-
|
152
|
-
self.__class__.report_error("install_type", self.install_type)
|
140
|
+
else:
|
141
|
+
self.__class__.report_error("install_type", self.install_type)
|
153
142
|
|
143
|
+
return (packageInstallName, pipInstallExtraOptions)
|
154
144
|
|
155
|
-
def get_exluded_command(self) ->
|
145
|
+
def get_exluded_command(self) -> []:
|
156
146
|
py2docfx_logger = get_logger(__name__)
|
157
147
|
if hasattr(self, "path"):
|
158
148
|
code_location = self.path.source_folder
|
@@ -7,11 +7,9 @@ PYPI = "pypi"
|
|
7
7
|
|
8
8
|
pip_install_common_options = [
|
9
9
|
# "--no-cache-dir", # to reduce install duration after switching to each venv
|
10
|
-
"--quiet",
|
11
10
|
"--no-compile",
|
12
|
-
"--no-warn-conflicts",
|
13
11
|
"--disable-pip-version-check",
|
14
|
-
"
|
12
|
+
"-vvv"
|
15
13
|
]
|
16
14
|
|
17
15
|
async def download(package_name, path, extra_index_url=None, prefer_source_distribution=True):
|
@@ -80,162 +80,4 @@ def test_get_exclude_command_check_extra_exclude(tmp_path):
|
|
80
80
|
]
|
81
81
|
def form_exclude_path(raletive_path):
|
82
82
|
return os.path.join(source_folder, raletive_path)
|
83
|
-
assert exclude_path == [form_exclude_path(path) for path in expected_exclude_path]
|
84
|
-
|
85
|
-
|
86
|
-
def test_get_combined_name_version_with_extras():
|
87
|
-
"""Test get_combined_name_version with extras"""
|
88
|
-
# Test package with extras but no version
|
89
|
-
test_data = {
|
90
|
-
"package_info": {
|
91
|
-
"install_type": "pypi",
|
92
|
-
"name": "test-package",
|
93
|
-
"extras": ["dev", "test"],
|
94
|
-
},
|
95
|
-
}
|
96
|
-
pkg = PackageInfo.parse_from(test_data)
|
97
|
-
assert pkg.get_combined_name_version() == "test-package[dev,test]"
|
98
|
-
|
99
|
-
# Test package with extras and version
|
100
|
-
test_data_with_version = {
|
101
|
-
"package_info": {
|
102
|
-
"install_type": "pypi",
|
103
|
-
"name": "test-package",
|
104
|
-
"version": "1.0.0",
|
105
|
-
"extras": ["dev", "test"],
|
106
|
-
},
|
107
|
-
}
|
108
|
-
pkg_with_version = PackageInfo.parse_from(test_data_with_version)
|
109
|
-
assert (
|
110
|
-
pkg_with_version.get_combined_name_version() == "test-package[dev,test]==1.0.0"
|
111
|
-
)
|
112
|
-
|
113
|
-
# Test package with extras and version operator
|
114
|
-
test_data_with_operator = {
|
115
|
-
"package_info": {
|
116
|
-
"install_type": "pypi",
|
117
|
-
"name": "test-package",
|
118
|
-
"version": ">=1.0.0",
|
119
|
-
"extras": ["dev"],
|
120
|
-
},
|
121
|
-
}
|
122
|
-
pkg_with_operator = PackageInfo.parse_from(test_data_with_operator)
|
123
|
-
assert pkg_with_operator.get_combined_name_version() == "test-package[dev]>=1.0.0"
|
124
|
-
|
125
|
-
|
126
|
-
def test_install_command_pypi_with_extras():
|
127
|
-
"""Test get_install_command for PYPI packages with extras"""
|
128
|
-
# Test PYPI package with extras and version
|
129
|
-
test_data = {
|
130
|
-
"package_info": {
|
131
|
-
"install_type": "pypi",
|
132
|
-
"name": "test-package",
|
133
|
-
"version": "1.0.0",
|
134
|
-
"extras": ["dev", "test"],
|
135
|
-
},
|
136
|
-
}
|
137
|
-
pkg = PackageInfo.parse_from(test_data)
|
138
|
-
install_command = pkg.get_install_command()
|
139
|
-
assert install_command[0] == "test-package[dev,test]==1.0.0"
|
140
|
-
assert install_command[1] == []
|
141
|
-
|
142
|
-
# Test PYPI package with extras but no version (should get --upgrade)
|
143
|
-
test_data_no_version = {
|
144
|
-
"package_info": {
|
145
|
-
"install_type": "pypi",
|
146
|
-
"name": "test-package",
|
147
|
-
"extras": ["dev"],
|
148
|
-
},
|
149
|
-
}
|
150
|
-
pkg_no_version = PackageInfo.parse_from(test_data_no_version)
|
151
|
-
install_command = pkg_no_version.get_install_command()
|
152
|
-
assert install_command[0] == "test-package[dev]"
|
153
|
-
assert install_command[1] == ["--upgrade"]
|
154
|
-
|
155
|
-
|
156
|
-
def test_install_command_source_code_with_extras(tmp_path):
|
157
|
-
"""Test get_install_command for SOURCE_CODE packages with extras"""
|
158
|
-
source_folder = os.path.join(tmp_path, "source_folder")
|
159
|
-
yaml_output_folder = os.path.join(tmp_path, "yaml_output_folder")
|
160
|
-
|
161
|
-
test_data = {
|
162
|
-
"package_info": {
|
163
|
-
"install_type": "source_code",
|
164
|
-
"name": "test-package",
|
165
|
-
"url": "https://github.com/test/test-package.git",
|
166
|
-
"extras": ["dev", "test"],
|
167
|
-
},
|
168
|
-
}
|
169
|
-
pkg = PackageInfo.parse_from(test_data)
|
170
|
-
pkg.path = Source(
|
171
|
-
source_folder=source_folder,
|
172
|
-
yaml_output_folder=yaml_output_folder,
|
173
|
-
package_name="test-package",
|
174
|
-
)
|
175
|
-
|
176
|
-
install_command = pkg.get_install_command()
|
177
|
-
assert install_command[0] == f"{source_folder}[dev,test]"
|
178
|
-
assert install_command[1] == []
|
179
|
-
|
180
|
-
|
181
|
-
def test_install_command_dist_file_with_extras():
|
182
|
-
"""Test get_install_command for DIST_FILE packages with extras"""
|
183
|
-
test_data = {
|
184
|
-
"package_info": {
|
185
|
-
"install_type": "dist_file",
|
186
|
-
"location": "/path/to/package.whl",
|
187
|
-
"extras": ["dev"],
|
188
|
-
},
|
189
|
-
}
|
190
|
-
pkg = PackageInfo.parse_from(test_data)
|
191
|
-
install_command = pkg.get_install_command()
|
192
|
-
assert install_command[0] == "/path/to/package.whl[dev]"
|
193
|
-
assert install_command[1] == []
|
194
|
-
|
195
|
-
|
196
|
-
def test_install_command_without_extras():
|
197
|
-
"""Test that packages without extras work as before"""
|
198
|
-
# Test PYPI package without extras
|
199
|
-
test_data = {
|
200
|
-
"package_info": {
|
201
|
-
"install_type": "pypi",
|
202
|
-
"name": "test-package",
|
203
|
-
"version": "1.0.0",
|
204
|
-
}
|
205
|
-
}
|
206
|
-
pkg = PackageInfo.parse_from(test_data)
|
207
|
-
install_command = pkg.get_install_command()
|
208
|
-
assert install_command[0] == "test-package==1.0.0"
|
209
|
-
assert install_command[1] == []
|
210
|
-
|
211
|
-
|
212
|
-
def test_install_command_empty_extras():
|
213
|
-
"""Test that packages with empty extras list work correctly"""
|
214
|
-
test_data = {
|
215
|
-
"package_info": {
|
216
|
-
"install_type": "pypi",
|
217
|
-
"name": "test-package",
|
218
|
-
"version": "1.0.0",
|
219
|
-
"extras": [],
|
220
|
-
},
|
221
|
-
}
|
222
|
-
pkg = PackageInfo.parse_from(test_data)
|
223
|
-
install_command = pkg.get_install_command()
|
224
|
-
assert install_command[0] == "test-package==1.0.0"
|
225
|
-
assert install_command[1] == []
|
226
|
-
|
227
|
-
|
228
|
-
def test_install_command_single_extra():
|
229
|
-
"""Test package with single extra"""
|
230
|
-
test_data = {
|
231
|
-
"package_info": {
|
232
|
-
"install_type": "pypi",
|
233
|
-
"name": "test-package",
|
234
|
-
"version": "1.0.0",
|
235
|
-
"extras": ["dev"],
|
236
|
-
},
|
237
|
-
}
|
238
|
-
pkg = PackageInfo.parse_from(test_data)
|
239
|
-
install_command = pkg.get_install_command()
|
240
|
-
assert install_command[0] == "test-package[dev]==1.0.0"
|
241
|
-
assert install_command[1] == []
|
83
|
+
assert exclude_path == [form_exclude_path(path) for path in expected_exclude_path]
|
@@ -30,7 +30,6 @@ import sys
|
|
30
30
|
import threading
|
31
31
|
import time
|
32
32
|
|
33
|
-
|
34
33
|
try:
|
35
34
|
import pwd
|
36
35
|
except ImportError:
|
@@ -86,7 +85,6 @@ from ._common import debug
|
|
86
85
|
from ._common import memoize_when_activated
|
87
86
|
from ._common import wrap_numbers as _wrap_numbers
|
88
87
|
|
89
|
-
|
90
88
|
if LINUX:
|
91
89
|
# This is public API and it will be retrieved from _pslinux.py
|
92
90
|
# via sys.modules.
|
@@ -207,7 +205,7 @@ if hasattr(_psplatform.Process, "rlimit"):
|
|
207
205
|
AF_LINK = _psplatform.AF_LINK
|
208
206
|
|
209
207
|
__author__ = "Giampaolo Rodola'"
|
210
|
-
__version__ = "7.
|
208
|
+
__version__ = "7.1.0"
|
211
209
|
version_info = tuple(int(num) for num in __version__.split('.'))
|
212
210
|
|
213
211
|
_timer = getattr(time, 'monotonic', time.time)
|
@@ -377,7 +375,11 @@ class Process:
|
|
377
375
|
won't reuse the same PID after such a short period of time
|
378
376
|
(0.01 secs). Technically this is inherently racy, but
|
379
377
|
practically it should be good enough.
|
378
|
+
|
379
|
+
NOTE: unreliable on FreeBSD and OpenBSD as ctime is subject to
|
380
|
+
system clock updates.
|
380
381
|
"""
|
382
|
+
|
381
383
|
if WINDOWS:
|
382
384
|
# Use create_time() fast method in order to speedup
|
383
385
|
# `process_iter()`. This means we'll get AccessDenied for
|
@@ -386,6 +388,11 @@ class Process:
|
|
386
388
|
# https://github.com/giampaolo/psutil/issues/2366#issuecomment-2381646555
|
387
389
|
self._create_time = self._proc.create_time(fast_only=True)
|
388
390
|
return (self.pid, self._create_time)
|
391
|
+
elif LINUX or NETBSD or OSX:
|
392
|
+
# Use 'monotonic' process starttime since boot to form unique
|
393
|
+
# process identity, since it is stable over changes to system
|
394
|
+
# time.
|
395
|
+
return (self.pid, self._proc.create_time(monotonic=True))
|
389
396
|
else:
|
390
397
|
return (self.pid, self.create_time())
|
391
398
|
|
@@ -426,12 +433,12 @@ class Process:
|
|
426
433
|
# on PID and creation time.
|
427
434
|
if not isinstance(other, Process):
|
428
435
|
return NotImplemented
|
429
|
-
if OPENBSD or NETBSD: # pragma: no cover
|
430
|
-
# Zombie processes on Open/NetBSD have a
|
431
|
-
# 0.0.
|
432
|
-
# (so it has a ctime), then it turned into a
|
433
|
-
# important to do this because is_running()
|
434
|
-
# __eq__.
|
436
|
+
if OPENBSD or NETBSD or SUNOS: # pragma: no cover
|
437
|
+
# Zombie processes on Open/NetBSD/illumos/Solaris have a
|
438
|
+
# creation time of 0.0. This covers the case when a process
|
439
|
+
# started normally (so it has a ctime), then it turned into a
|
440
|
+
# zombie. It's important to do this because is_running()
|
441
|
+
# depends on __eq__.
|
435
442
|
pid1, ident1 = self._ident
|
436
443
|
pid2, ident2 = other._ident
|
437
444
|
if pid1 == pid2:
|
@@ -593,10 +600,13 @@ class Process:
|
|
593
600
|
return None
|
594
601
|
ppid = self.ppid()
|
595
602
|
if ppid is not None:
|
596
|
-
|
603
|
+
# Get a fresh (non-cached) ctime in case the system clock
|
604
|
+
# was updated. TODO: use a monotonic ctime on platforms
|
605
|
+
# where it's supported.
|
606
|
+
proc_ctime = Process(self.pid).create_time()
|
597
607
|
try:
|
598
608
|
parent = Process(ppid)
|
599
|
-
if parent.create_time() <=
|
609
|
+
if parent.create_time() <= proc_ctime:
|
600
610
|
return parent
|
601
611
|
# ...else ppid has been reused by another process
|
602
612
|
except NoSuchProcess:
|
@@ -765,8 +775,11 @@ class Process:
|
|
765
775
|
|
766
776
|
def create_time(self):
|
767
777
|
"""The process creation time as a floating point number
|
768
|
-
expressed in seconds since the epoch
|
769
|
-
The return value is cached after
|
778
|
+
expressed in seconds since the epoch (seconds since January 1,
|
779
|
+
1970, at midnight UTC). The return value, which is cached after
|
780
|
+
first call, is based on the system clock, which means it may be
|
781
|
+
affected by changes such as manual adjustments or time
|
782
|
+
synchronization (e.g. NTP).
|
770
783
|
"""
|
771
784
|
if self._create_time is None:
|
772
785
|
self._create_time = self._proc.create_time()
|
@@ -964,6 +977,10 @@ class Process:
|
|
964
977
|
"""
|
965
978
|
self._raise_if_pid_reused()
|
966
979
|
ppid_map = _ppid_map()
|
980
|
+
# Get a fresh (non-cached) ctime in case the system clock was
|
981
|
+
# updated. TODO: use a monotonic ctime on platforms where it's
|
982
|
+
# supported.
|
983
|
+
proc_ctime = Process(self.pid).create_time()
|
967
984
|
ret = []
|
968
985
|
if not recursive:
|
969
986
|
for pid, ppid in ppid_map.items():
|
@@ -972,7 +989,7 @@ class Process:
|
|
972
989
|
child = Process(pid)
|
973
990
|
# if child happens to be older than its parent
|
974
991
|
# (self) it means child's PID has been reused
|
975
|
-
if
|
992
|
+
if proc_ctime <= child.create_time():
|
976
993
|
ret.append(child)
|
977
994
|
except (NoSuchProcess, ZombieProcess):
|
978
995
|
pass
|
@@ -998,7 +1015,7 @@ class Process:
|
|
998
1015
|
child = Process(child_pid)
|
999
1016
|
# if child happens to be older than its parent
|
1000
1017
|
# (self) it means child's PID has been reused
|
1001
|
-
intime =
|
1018
|
+
intime = proc_ctime <= child.create_time()
|
1002
1019
|
if intime:
|
1003
1020
|
ret.append(child)
|
1004
1021
|
stack.append(child_pid)
|
@@ -1484,7 +1501,7 @@ def process_iter(attrs=None, ad_value=None):
|
|
1484
1501
|
|
1485
1502
|
Every new Process instance is only created once and then cached
|
1486
1503
|
into an internal table which is updated every time this is used.
|
1487
|
-
Cache can optionally be cleared via `process_iter.
|
1504
|
+
Cache can optionally be cleared via `process_iter.cache_clear()`.
|
1488
1505
|
|
1489
1506
|
The sorting order in which processes are yielded is based on
|
1490
1507
|
their PIDs.
|
@@ -2352,9 +2369,12 @@ if hasattr(_psplatform, "sensors_battery"):
|
|
2352
2369
|
|
2353
2370
|
|
2354
2371
|
def boot_time():
|
2355
|
-
"""Return the system boot time expressed in seconds since the epoch
|
2356
|
-
|
2357
|
-
|
2372
|
+
"""Return the system boot time expressed in seconds since the epoch
|
2373
|
+
(seconds since January 1, 1970, at midnight UTC). The returned
|
2374
|
+
value is based on the system clock, which means it may be affected
|
2375
|
+
by changes such as manual adjustments or time synchronization (e.g.
|
2376
|
+
NTP).
|
2377
|
+
"""
|
2358
2378
|
return _psplatform.boot_time()
|
2359
2379
|
|
2360
2380
|
|
@@ -22,7 +22,6 @@ from socket import AF_INET
|
|
22
22
|
from socket import SOCK_DGRAM
|
23
23
|
from socket import SOCK_STREAM
|
24
24
|
|
25
|
-
|
26
25
|
try:
|
27
26
|
from socket import AF_INET6
|
28
27
|
except ImportError:
|
@@ -409,7 +408,7 @@ def memoize(fun):
|
|
409
408
|
except KeyError:
|
410
409
|
try:
|
411
410
|
ret = cache[key] = fun(*args, **kwargs)
|
412
|
-
except Exception as err:
|
411
|
+
except Exception as err:
|
413
412
|
raise err from None
|
414
413
|
return ret
|
415
414
|
|
@@ -458,14 +457,14 @@ def memoize_when_activated(fun):
|
|
458
457
|
# case 2: we never entered oneshot() ctx
|
459
458
|
try:
|
460
459
|
return fun(self)
|
461
|
-
except Exception as err:
|
460
|
+
except Exception as err:
|
462
461
|
raise err from None
|
463
462
|
except KeyError:
|
464
463
|
# case 3: we entered oneshot() ctx but there's no cache
|
465
464
|
# for this entry yet
|
466
465
|
try:
|
467
466
|
ret = fun(self)
|
468
|
-
except Exception as err:
|
467
|
+
except Exception as err:
|
469
468
|
raise err from None
|
470
469
|
try:
|
471
470
|
self._cache[fun] = ret
|
@@ -523,7 +522,6 @@ def path_exists_strict(path):
|
|
523
522
|
return True
|
524
523
|
|
525
524
|
|
526
|
-
@memoize
|
527
525
|
def supports_ipv6():
|
528
526
|
"""Return True if IPv6 is supported on this platform."""
|
529
527
|
if not socket.has_ipv6 or AF_INET6 is None:
|
@@ -29,7 +29,6 @@ from ._common import get_procfs_path
|
|
29
29
|
from ._common import memoize_when_activated
|
30
30
|
from ._common import usage_percent
|
31
31
|
|
32
|
-
|
33
32
|
__extra__all__ = ["PROCFS_PATH"]
|
34
33
|
|
35
34
|
|
@@ -279,7 +278,7 @@ def boot_time():
|
|
279
278
|
def users():
|
280
279
|
"""Return currently connected users as a list of namedtuples."""
|
281
280
|
retlist = []
|
282
|
-
rawlist =
|
281
|
+
rawlist = cext_posix.users()
|
283
282
|
localhost = (':0.0', ':0')
|
284
283
|
for item in rawlist:
|
285
284
|
user, tty, hostname, tstamp, user_process, pid = item
|
@@ -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
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
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
|
-
|
918
|
-
|
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
|
|