| #!/usr/bin/env python3 |
| |
| # Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Cross-platform lib for process and system monitoring in Python.""" |
| |
| import contextlib |
| import glob |
| import io |
| import os |
| import pathlib |
| import shutil |
| import struct |
| import subprocess |
| import sys |
| import sysconfig |
| import tempfile |
| import warnings |
| |
| with warnings.catch_warnings(): |
| warnings.simplefilter("ignore") |
| try: |
| import setuptools |
| from setuptools import Extension |
| from setuptools import setup |
| except ImportError: |
| if "CIBUILDWHEEL" in os.environ: |
| raise |
| setuptools = None |
| from distutils.core import Extension |
| from distutils.core import setup |
| |
| |
| ROOT_DIR = pathlib.Path(__file__).resolve().parent |
| sys.path.insert(0, str(ROOT_DIR)) |
| |
| from _bootstrap import get_version # noqa: E402 |
| from _bootstrap import load_module # noqa: E402 |
| |
| _common = load_module(ROOT_DIR / "psutil" / "_common.py") |
| |
| AIX = _common.AIX |
| BSD = _common.BSD |
| FREEBSD = _common.FREEBSD |
| LINUX = _common.LINUX |
| MACOS = _common.MACOS |
| NETBSD = _common.NETBSD |
| OPENBSD = _common.OPENBSD |
| POSIX = _common.POSIX |
| SUNOS = _common.SUNOS |
| WINDOWS = _common.WINDOWS |
| hilite = _common.hilite |
| |
| PYPY = '__pypy__' in sys.builtin_module_names |
| PY36_PLUS = sys.version_info[:2] >= (3, 6) |
| PY37_PLUS = sys.version_info[:2] >= (3, 7) |
| CP36_PLUS = PY36_PLUS and sys.implementation.name == "cpython" |
| CP37_PLUS = PY37_PLUS and sys.implementation.name == "cpython" |
| Py_GIL_DISABLED = sysconfig.get_config_var("Py_GIL_DISABLED") |
| |
| # Test deps, installable via `pip install .[test]` or |
| # `make install-pydeps-test`. |
| TEST_DEPS = [ |
| "psleak", |
| "pytest", |
| "pytest-instafail", |
| "pytest-xdist", |
| "setuptools", |
| 'pywin32 ; os_name == "nt" and implementation_name != "pypy"', |
| 'wheel ; os_name == "nt" and implementation_name != "pypy"', |
| 'wmi ; os_name == "nt" and implementation_name != "pypy"', |
| ] |
| |
| # Linter deps, installable via `pip install .[lint]` or |
| # `make install-pydeps-lint`. |
| LINT_DEPS = [ |
| "black", |
| "rstwrap", |
| "ruff", |
| "sphinx-lint", |
| "toml-sort", |
| ] |
| |
| # Development deps, installable via `pip install .[dev]` or |
| # `make install-pydeps-dev`. |
| DEV_DEPS = [ |
| *TEST_DEPS, |
| *LINT_DEPS, |
| "abi3audit", |
| "check-manifest", |
| "coverage", |
| "packaging", |
| "pylint", # not enforced |
| "pyperf", |
| "pypinfo", |
| "pytest-cov", |
| "requests", |
| "twine", |
| "validate-pyproject[all]", |
| "virtualenv", |
| "vulture", |
| "wheel", |
| 'colorama ; os_name == "nt"', |
| 'pyreadline3 ; os_name == "nt"', |
| ] |
| |
| # The pre-processor macros that are passed to the C compiler when |
| # building the extension. |
| macros = [] |
| |
| if POSIX: |
| macros.append(("PSUTIL_POSIX", 1)) |
| if BSD: |
| macros.append(("PSUTIL_BSD", 1)) |
| |
| # Needed to determine _Py_PARSE_PID in case it's missing (PyPy). |
| # Taken from Lib/test/test_fcntl.py. |
| # XXX: not bullet proof as the (long long) case is missing. |
| if struct.calcsize('l') <= 8: |
| macros.append(('PSUTIL_SIZEOF_PID_T', '4')) # int |
| else: |
| macros.append(('PSUTIL_SIZEOF_PID_T', '8')) # long |
| |
| |
| sources = glob.glob("psutil/arch/all/*.c") |
| if POSIX: |
| sources.extend(glob.glob("psutil/arch/posix/*.c")) |
| |
| |
| VERSION = get_version() |
| macros.append(('PSUTIL_VERSION', int(VERSION.replace('.', '')))) |
| |
| # Py_LIMITED_API lets us create a single wheel which works with multiple |
| # python versions, including unreleased ones. |
| if setuptools and CP36_PLUS and (MACOS or LINUX) and not Py_GIL_DISABLED: |
| py_limited_api = {"py_limited_api": True} |
| options = {"bdist_wheel": {"py_limited_api": "cp36"}} |
| macros.append(('Py_LIMITED_API', '0x03060000')) |
| elif setuptools and CP37_PLUS and WINDOWS and not Py_GIL_DISABLED: |
| # PyErr_SetFromWindowsErr / PyErr_SetFromWindowsErrWithFilename are |
| # part of the stable API/ABI starting with CPython 3.7 |
| py_limited_api = {"py_limited_api": True} |
| options = {"bdist_wheel": {"py_limited_api": "cp37"}} |
| macros.append(('Py_LIMITED_API', '0x03070000')) |
| else: |
| py_limited_api = {} |
| options = {} |
| |
| |
| def get_long_description(): |
| script = ROOT_DIR / "scripts" / "internal" / "convert_readme.py" |
| readme = ROOT_DIR / 'README.rst' |
| p = subprocess.Popen( |
| [sys.executable, script, readme], |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE, |
| universal_newlines=True, |
| ) |
| stdout, stderr = p.communicate() |
| if p.returncode != 0: |
| raise RuntimeError(stderr) |
| return stdout |
| |
| |
| @contextlib.contextmanager |
| def silenced_output(): |
| with contextlib.redirect_stdout(io.StringIO()): |
| with contextlib.redirect_stderr(io.StringIO()): |
| yield |
| |
| |
| def has_python_h(): |
| include_dir = sysconfig.get_path("include") |
| return os.path.exists(os.path.join(include_dir, "Python.h")) |
| |
| |
| def get_sysdeps(): |
| if LINUX: |
| pyimpl = "pypy" if PYPY else "python" |
| if shutil.which("dpkg"): |
| return "sudo apt-get install gcc {}3-dev".format(pyimpl) |
| elif shutil.which("rpm"): |
| return "sudo yum install gcc {}3-devel".format(pyimpl) |
| elif shutil.which("pacman"): |
| return "sudo pacman -S gcc python" |
| elif shutil.which("apk"): |
| return "sudo apk add gcc {}3-dev musl-dev linux-headers".format( |
| pyimpl |
| ) |
| elif MACOS: |
| return "xcode-select --install" |
| elif FREEBSD: |
| if shutil.which("pkg"): |
| return "pkg install gcc python3" |
| elif shutil.which("mport"): # MidnightBSD |
| return "mport install gcc python3" |
| elif OPENBSD: |
| return "pkg_add -v gcc python3" |
| elif NETBSD: |
| return "pkgin install gcc python3" |
| elif SUNOS: |
| return "pkg install gcc" |
| |
| |
| def print_install_instructions(): |
| reasons = [] |
| if not shutil.which("gcc"): |
| reasons.append("gcc is not installed.") |
| if not has_python_h(): |
| reasons.append("Python header files are not installed.") |
| if reasons: |
| sysdeps = get_sysdeps() |
| if sysdeps: |
| s = "psutil could not be compiled from sources. " |
| s += " ".join(reasons) |
| s += " Try running:\n" |
| s += " {}".format(sysdeps) |
| print(hilite(s, color="red", bold=True), file=sys.stderr) |
| |
| |
| def unix_can_compile(c_code): |
| from distutils.errors import CompileError |
| from distutils.unixccompiler import UnixCCompiler |
| |
| with tempfile.NamedTemporaryFile( |
| suffix='.c', delete=False, mode="wt" |
| ) as f: |
| f.write(c_code) |
| |
| tempdir = tempfile.mkdtemp() |
| try: |
| compiler = UnixCCompiler() |
| # https://github.com/giampaolo/psutil/pull/1568 |
| if os.getenv('CC'): |
| compiler.set_executable('compiler_so', os.getenv('CC')) |
| with silenced_output(): |
| compiler.compile([f.name], output_dir=tempdir) |
| compiler.compile([f.name], output_dir=tempdir) |
| except CompileError: |
| return False |
| else: |
| return True |
| finally: |
| os.remove(f.name) |
| shutil.rmtree(tempdir) |
| |
| |
| if WINDOWS: |
| |
| def get_winver(): |
| maj, min = sys.getwindowsversion()[0:2] |
| return "0x0{}".format((maj * 100) + min) |
| |
| if sys.getwindowsversion()[0] < 6: |
| msg = "this Windows version is too old (< Windows Vista); " |
| msg += "psutil 3.4.2 is the latest version which supports Windows " |
| msg += "2000, XP and 2003 server" |
| raise RuntimeError(msg) |
| |
| macros.append(("PSUTIL_WINDOWS", 1)) |
| macros.extend([ |
| # be nice to mingw, see: |
| # http://www.mingw.org/wiki/Use_more_recent_defined_functions |
| ('_WIN32_WINNT', get_winver()), |
| ('_AVAIL_WINVER_', get_winver()), |
| ('_CRT_SECURE_NO_WARNINGS', None), |
| # see: https://github.com/giampaolo/psutil/issues/348 |
| ('PSAPI_VERSION', 1), |
| ]) |
| |
| if Py_GIL_DISABLED: |
| macros.append(('Py_GIL_DISABLED', 1)) |
| |
| ext = Extension( |
| 'psutil._psutil_windows', |
| sources=( |
| sources |
| + ["psutil/_psutil_windows.c"] |
| + glob.glob("psutil/arch/windows/*.c") |
| ), |
| define_macros=macros, |
| libraries=[ |
| "advapi32", |
| "kernel32", |
| "netapi32", |
| "pdh", |
| "PowrProf", |
| "psapi", |
| "shell32", |
| "ws2_32", |
| ], |
| # extra_compile_args=["/W 4"], |
| # extra_link_args=["/DEBUG"], |
| **py_limited_api, |
| ) |
| |
| elif MACOS: |
| macros.extend([("PSUTIL_OSX", 1), ("PSUTIL_MACOS", 1)]) |
| ext = Extension( |
| 'psutil._psutil_osx', |
| sources=( |
| sources |
| + ["psutil/_psutil_osx.c"] |
| + glob.glob("psutil/arch/osx/*.c") |
| ), |
| define_macros=macros, |
| extra_link_args=[ |
| '-framework', |
| 'CoreFoundation', |
| '-framework', |
| 'IOKit', |
| ], |
| **py_limited_api, |
| ) |
| |
| elif FREEBSD: |
| macros.append(("PSUTIL_FREEBSD", 1)) |
| |
| ext = Extension( |
| 'psutil._psutil_bsd', |
| sources=( |
| sources |
| + ["psutil/_psutil_bsd.c"] |
| + glob.glob("psutil/arch/bsd/*.c") |
| + glob.glob("psutil/arch/freebsd/*.c") |
| ), |
| define_macros=macros, |
| libraries=["devstat"], |
| **py_limited_api, |
| ) |
| |
| elif OPENBSD: |
| macros.append(("PSUTIL_OPENBSD", 1)) |
| |
| ext = Extension( |
| 'psutil._psutil_bsd', |
| sources=( |
| sources |
| + ["psutil/_psutil_bsd.c"] |
| + glob.glob("psutil/arch/bsd/*.c") |
| + glob.glob("psutil/arch/openbsd/*.c") |
| ), |
| define_macros=macros, |
| libraries=["kvm"], |
| **py_limited_api, |
| ) |
| |
| elif NETBSD: |
| macros.append(("PSUTIL_NETBSD", 1)) |
| |
| ext = Extension( |
| 'psutil._psutil_bsd', |
| sources=( |
| sources |
| + ["psutil/_psutil_bsd.c"] |
| + glob.glob("psutil/arch/bsd/*.c") |
| + glob.glob("psutil/arch/netbsd/*.c") |
| ), |
| define_macros=macros, |
| libraries=["kvm", "jemalloc"], |
| **py_limited_api, |
| ) |
| |
| elif LINUX: |
| # see: https://github.com/giampaolo/psutil/issues/659 |
| if not unix_can_compile("#include <linux/ethtool.h>"): |
| macros.append(("PSUTIL_ETHTOOL_MISSING_TYPES", 1)) |
| |
| macros.append(("PSUTIL_LINUX", 1)) |
| ext = Extension( |
| 'psutil._psutil_linux', |
| sources=( |
| sources |
| + ["psutil/_psutil_linux.c"] |
| + glob.glob("psutil/arch/linux/*.c") |
| ), |
| define_macros=macros, |
| **py_limited_api, |
| ) |
| |
| elif SUNOS: |
| macros.append(("PSUTIL_SUNOS", 1)) |
| |
| ext = Extension( |
| 'psutil._psutil_sunos', |
| sources=( |
| sources |
| + ["psutil/_psutil_sunos.c"] |
| + glob.glob("psutil/arch/sunos/*.c") |
| ), |
| define_macros=macros, |
| libraries=["kstat", "nsl", "socket"], |
| **py_limited_api, |
| ) |
| |
| elif AIX: |
| macros.append(("PSUTIL_AIX", 1)) |
| |
| ext = Extension( |
| 'psutil._psutil_aix', |
| sources=( |
| sources |
| + ["psutil/_psutil_aix.c"] |
| + glob.glob("psutil/arch/aix/*.c") |
| ), |
| libraries=["perfstat"], |
| define_macros=macros, |
| **py_limited_api, |
| ) |
| |
| else: |
| sys.exit("platform {} is not supported".format(sys.platform)) |
| |
| |
| def main(): |
| kwargs = dict( |
| name='psutil', |
| version=VERSION, |
| description="Cross-platform lib for process and system monitoring.", |
| long_description=get_long_description(), |
| long_description_content_type='text/x-rst', |
| # fmt: off |
| keywords=[ |
| 'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'df', 'uptime', |
| 'taskmgr', 'process', 'monitoring', 'performance', 'metrics', |
| 'observability', |
| ], |
| # fmt: on |
| author='Giampaolo Rodola', |
| author_email='g.rodola@gmail.com', |
| url='https://github.com/giampaolo/psutil', |
| platforms='Platform Independent', |
| license='BSD-3-Clause', |
| packages=['psutil'], |
| ext_modules=[ext], |
| options=options, |
| # https://docs.pypi.org/project_metadata/ |
| project_urls={ |
| 'Homepage': 'https://github.com/giampaolo/psutil', |
| 'Source': 'https://github.com/giampaolo/psutil', |
| 'Issues': 'https://github.com/giampaolo/psutil/issues', |
| 'Documentation': 'https://psutil.readthedocs.io/', |
| 'Changelog': 'https://psutil.readthedocs.io/latest/changelog.html', |
| 'Funding': 'https://github.com/sponsors/giampaolo', |
| }, |
| # https://pypi.org/classifiers/ |
| classifiers=[ |
| 'Development Status :: 5 - Production/Stable', |
| 'Environment :: Console', |
| 'Intended Audience :: Developers', |
| 'Intended Audience :: Information Technology', |
| 'Intended Audience :: System Administrators', |
| 'License :: OSI Approved :: BSD License', |
| 'Operating System :: MacOS :: MacOS X', |
| 'Operating System :: Microsoft :: Windows :: Windows 10', |
| 'Operating System :: Microsoft :: Windows :: Windows 11', |
| 'Operating System :: Microsoft :: Windows :: Windows 7', |
| 'Operating System :: Microsoft :: Windows :: Windows 8', |
| 'Operating System :: Microsoft :: Windows :: Windows 8.1', |
| 'Operating System :: Microsoft :: Windows :: Windows Server 2003', |
| 'Operating System :: Microsoft :: Windows :: Windows Server 2008', |
| 'Operating System :: Microsoft :: Windows :: Windows Vista', |
| 'Operating System :: Microsoft :: Windows', |
| 'Operating System :: Microsoft', |
| 'Operating System :: OS Independent', |
| 'Operating System :: POSIX :: AIX', |
| 'Operating System :: POSIX :: BSD :: FreeBSD', |
| 'Operating System :: POSIX :: BSD :: NetBSD', |
| 'Operating System :: POSIX :: BSD :: OpenBSD', |
| 'Operating System :: POSIX :: BSD', |
| 'Operating System :: POSIX :: Linux', |
| 'Operating System :: POSIX :: SunOS/Solaris', |
| 'Operating System :: POSIX', |
| 'Programming Language :: C', |
| 'Programming Language :: Python :: 3', |
| 'Programming Language :: Python :: Implementation :: CPython', |
| 'Programming Language :: Python :: Implementation :: PyPy', |
| 'Programming Language :: Python', |
| 'Topic :: Software Development :: Libraries :: Python Modules', |
| 'Topic :: Software Development :: Libraries', |
| 'Topic :: System :: Benchmark', |
| 'Topic :: System :: Hardware', |
| 'Topic :: System :: Monitoring', |
| 'Topic :: System :: Networking :: Monitoring :: Hardware Watchdog', |
| 'Topic :: System :: Networking :: Monitoring', |
| 'Topic :: System :: Networking', |
| 'Topic :: System :: Operating System', |
| 'Topic :: System :: Systems Administration', |
| 'Topic :: Utilities', |
| ], |
| ) |
| if setuptools is not None: |
| extras_require = { |
| "test": TEST_DEPS, |
| "lint": LINT_DEPS, |
| "dev": DEV_DEPS, |
| } |
| kwargs.update( |
| python_requires=">=3.7", |
| extras_require=extras_require, |
| zip_safe=False, |
| ) |
| success = False |
| try: |
| setup(**kwargs) |
| success = True |
| finally: |
| cmd = sys.argv[1] if len(sys.argv) >= 2 else '' |
| if ( |
| not success |
| and POSIX |
| and cmd.startswith( |
| ("build", "install", "sdist", "bdist", "develop") |
| ) |
| ): |
| print_install_instructions() |
| |
| |
| if __name__ == '__main__': |
| main() |