1#!/usr/bin/env python3 2 3# Copyright (c) 2009 Giampaolo Rodola'. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Cross-platform lib for process and system monitoring in Python.""" 8 9from __future__ import print_function 10import contextlib 11import io 12import os 13import platform 14import re 15import shutil 16import struct 17import subprocess 18import sys 19import tempfile 20import warnings 21 22with warnings.catch_warnings(): 23 warnings.simplefilter("ignore") 24 try: 25 import setuptools 26 from setuptools import setup, Extension 27 except ImportError: 28 setuptools = None 29 from distutils.core import setup, Extension 30 31HERE = os.path.abspath(os.path.dirname(__file__)) 32 33# ...so we can import _common.py and _compat.py 34sys.path.insert(0, os.path.join(HERE, "psutil")) 35 36from _common import AIX # NOQA 37from _common import BSD # NOQA 38from _common import FREEBSD # NOQA 39from _common import hilite # NOQA 40from _common import LINUX # NOQA 41from _common import MACOS # NOQA 42from _common import NETBSD # NOQA 43from _common import OPENBSD # NOQA 44from _common import DRAGONFLY # NOQA 45from _common import POSIX # NOQA 46from _common import SUNOS # NOQA 47from _common import WINDOWS # NOQA 48from _compat import PY3 # NOQA 49from _compat import which # NOQA 50 51 52PYPY = '__pypy__' in sys.builtin_module_names 53macros = [] 54if POSIX: 55 macros.append(("PSUTIL_POSIX", 1)) 56if BSD: 57 macros.append(("PSUTIL_BSD", 1)) 58 59# Needed to determine _Py_PARSE_PID in case it's missing (Python 2, PyPy). 60# Taken from Lib/test/test_fcntl.py. 61# XXX: not bullet proof as the (long long) case is missing. 62if struct.calcsize('l') <= 8: 63 macros.append(('PSUTIL_SIZEOF_PID_T', '4')) # int 64else: 65 macros.append(('PSUTIL_SIZEOF_PID_T', '8')) # long 66 67 68sources = ['psutil/_psutil_common.c'] 69if POSIX: 70 sources.append('psutil/_psutil_posix.c') 71 72 73extras_require = {"test": [ 74 "enum34; python_version <= '3.4'", 75 "ipaddress; python_version < '3.0'", 76 "mock; python_version < '3.0'", 77 "unittest2; python_version < '3.0'", 78]} 79if not PYPY: 80 extras_require['test'].extend([ 81 "pywin32; sys.platform == 'win32'", 82 "wmi; sys.platform == 'win32'"]) 83 84 85def get_version(): 86 INIT = os.path.join(HERE, 'psutil/__init__.py') 87 with open(INIT, 'r') as f: 88 for line in f: 89 if line.startswith('__version__'): 90 ret = eval(line.strip().split(' = ')[1]) 91 assert ret.count('.') == 2, ret 92 for num in ret.split('.'): 93 assert num.isdigit(), ret 94 return ret 95 raise ValueError("couldn't find version string") 96 97 98VERSION = get_version() 99macros.append(('PSUTIL_VERSION', int(VERSION.replace('.', '')))) 100 101 102def get_description(): 103 script = os.path.join(HERE, "scripts", "internal", "convert_readme.py") 104 readme = os.path.join(HERE, 'README.rst') 105 p = subprocess.Popen([sys.executable, script, readme], 106 stdout=subprocess.PIPE, stderr=subprocess.PIPE) 107 stdout, stderr = p.communicate() 108 if p.returncode != 0: 109 raise RuntimeError(stderr) 110 data = stdout.decode('utf8') 111 if WINDOWS: 112 data = data.replace('\r\n', '\n') 113 return data 114 115 116@contextlib.contextmanager 117def silenced_output(stream_name): 118 class DummyFile(io.BytesIO): 119 # see: https://github.com/giampaolo/psutil/issues/678 120 errors = "ignore" 121 122 def write(self, s): 123 pass 124 125 orig = getattr(sys, stream_name) 126 try: 127 setattr(sys, stream_name, DummyFile()) 128 yield 129 finally: 130 setattr(sys, stream_name, orig) 131 132 133def missdeps(msg): 134 s = hilite("C compiler or Python headers are not installed ", color="red") 135 s += hilite("on this system. Try to run:\n", color="red") 136 s += hilite(msg, color="red", bold=True) 137 print(s, file=sys.stderr) 138 139 140if WINDOWS: 141 def get_winver(): 142 maj, min = sys.getwindowsversion()[0:2] 143 return '0x0%s' % ((maj * 100) + min) 144 145 if sys.getwindowsversion()[0] < 6: 146 msg = "this Windows version is too old (< Windows Vista); " 147 msg += "psutil 3.4.2 is the latest version which supports Windows " 148 msg += "2000, XP and 2003 server" 149 raise RuntimeError(msg) 150 151 macros.append(("PSUTIL_WINDOWS", 1)) 152 macros.extend([ 153 # be nice to mingw, see: 154 # http://www.mingw.org/wiki/Use_more_recent_defined_functions 155 ('_WIN32_WINNT', get_winver()), 156 ('_AVAIL_WINVER_', get_winver()), 157 ('_CRT_SECURE_NO_WARNINGS', None), 158 # see: https://github.com/giampaolo/psutil/issues/348 159 ('PSAPI_VERSION', 1), 160 ]) 161 162 ext = Extension( 163 'psutil._psutil_windows', 164 sources=sources + [ 165 'psutil/_psutil_windows.c', 166 'psutil/arch/windows/process_utils.c', 167 'psutil/arch/windows/process_info.c', 168 'psutil/arch/windows/process_handles.c', 169 'psutil/arch/windows/disk.c', 170 'psutil/arch/windows/net.c', 171 'psutil/arch/windows/cpu.c', 172 'psutil/arch/windows/security.c', 173 'psutil/arch/windows/services.c', 174 'psutil/arch/windows/socks.c', 175 'psutil/arch/windows/wmi.c', 176 ], 177 define_macros=macros, 178 libraries=[ 179 "psapi", "kernel32", "advapi32", "shell32", "netapi32", 180 "ws2_32", "PowrProf", "pdh", 181 ], 182 # extra_compile_args=["/W 4"], 183 # extra_link_args=["/DEBUG"] 184 ) 185 186elif MACOS: 187 macros.append(("PSUTIL_OSX", 1)) 188 ext = Extension( 189 'psutil._psutil_osx', 190 sources=sources + [ 191 'psutil/_psutil_osx.c', 192 'psutil/arch/osx/process_info.c', 193 ], 194 define_macros=macros, 195 extra_link_args=[ 196 '-framework', 'CoreFoundation', '-framework', 'IOKit' 197 ]) 198 199elif FREEBSD: 200 macros.append(("PSUTIL_FREEBSD", 1)) 201 ext = Extension( 202 'psutil._psutil_bsd', 203 sources=sources + [ 204 'psutil/_psutil_bsd.c', 205 'psutil/arch/freebsd/specific.c', 206 'psutil/arch/freebsd/sys_socks.c', 207 'psutil/arch/freebsd/proc_socks.c', 208 ], 209 define_macros=macros, 210 libraries=["devstat"]) 211 212elif OPENBSD: 213 macros.append(("PSUTIL_OPENBSD", 1)) 214 ext = Extension( 215 'psutil._psutil_bsd', 216 sources=sources + [ 217 'psutil/_psutil_bsd.c', 218 'psutil/arch/openbsd/specific.c', 219 ], 220 define_macros=macros, 221 libraries=["kvm"]) 222 223elif NETBSD: 224 macros.append(("PSUTIL_NETBSD", 1)) 225 ext = Extension( 226 'psutil._psutil_bsd', 227 sources=sources + [ 228 'psutil/_psutil_bsd.c', 229 'psutil/arch/netbsd/specific.c', 230 'psutil/arch/netbsd/socks.c', 231 ], 232 define_macros=macros, 233 libraries=["kvm"]) 234 235elif DRAGONFLY: 236 macros.append(("PSUTIL_DRAGONFLY", 1)) 237 ext = Extension( 238 'psutil._psutil_bsd', 239 sources=sources + [ 240 'psutil/_psutil_bsd.c', 241 'psutil/arch/bsd/dragonfly.c', 242 ], 243 define_macros=macros, 244 libraries=["kvm"]) 245 246elif LINUX: 247 def get_ethtool_macro(): 248 # see: https://github.com/giampaolo/psutil/issues/659 249 from distutils.unixccompiler import UnixCCompiler 250 from distutils.errors import CompileError 251 252 with tempfile.NamedTemporaryFile( 253 suffix='.c', delete=False, mode="wt") as f: 254 f.write("#include <linux/ethtool.h>") 255 256 output_dir = tempfile.mkdtemp() 257 try: 258 compiler = UnixCCompiler() 259 # https://github.com/giampaolo/psutil/pull/1568 260 if os.getenv('CC'): 261 compiler.set_executable('compiler_so', os.getenv('CC')) 262 with silenced_output('stderr'): 263 with silenced_output('stdout'): 264 compiler.compile([f.name], output_dir=output_dir) 265 except CompileError: 266 return ("PSUTIL_ETHTOOL_MISSING_TYPES", 1) 267 else: 268 return None 269 finally: 270 os.remove(f.name) 271 shutil.rmtree(output_dir) 272 273 macros.append(("PSUTIL_LINUX", 1)) 274 ETHTOOL_MACRO = get_ethtool_macro() 275 if ETHTOOL_MACRO is not None: 276 macros.append(ETHTOOL_MACRO) 277 ext = Extension( 278 'psutil._psutil_linux', 279 sources=sources + ['psutil/_psutil_linux.c'], 280 define_macros=macros) 281 282elif SUNOS: 283 macros.append(("PSUTIL_SUNOS", 1)) 284 ext = Extension( 285 'psutil._psutil_sunos', 286 sources=sources + [ 287 'psutil/_psutil_sunos.c', 288 'psutil/arch/solaris/v10/ifaddrs.c', 289 'psutil/arch/solaris/environ.c' 290 ], 291 define_macros=macros, 292 libraries=['kstat', 'nsl', 'socket']) 293 294elif AIX: 295 macros.append(("PSUTIL_AIX", 1)) 296 ext = Extension( 297 'psutil._psutil_aix', 298 sources=sources + [ 299 'psutil/_psutil_aix.c', 300 'psutil/arch/aix/net_connections.c', 301 'psutil/arch/aix/common.c', 302 'psutil/arch/aix/ifaddrs.c'], 303 libraries=['perfstat'], 304 define_macros=macros) 305 306else: 307 sys.exit('platform %s is not supported' % sys.platform) 308 309 310if POSIX: 311 posix_extension = Extension( 312 'psutil._psutil_posix', 313 define_macros=macros, 314 sources=sources) 315 if SUNOS: 316 def get_sunos_update(): 317 # See https://serverfault.com/q/524883 318 # for an explanation of Solaris /etc/release 319 with open('/etc/release') as f: 320 update = re.search(r'(?<=s10s_u)[0-9]{1,2}', f.readline()) 321 if update is None: 322 return 0 323 else: 324 return int(update.group(0)) 325 326 posix_extension.libraries.append('socket') 327 if platform.release() == '5.10': 328 # Detect Solaris 5.10, update >= 4, see: 329 # https://github.com/giampaolo/psutil/pull/1638 330 if get_sunos_update() >= 4: 331 # MIB compliancy starts with SunOS 5.10 Update 4: 332 posix_extension.define_macros.append(('NEW_MIB_COMPLIANT', 1)) 333 posix_extension.sources.append('psutil/arch/solaris/v10/ifaddrs.c') 334 posix_extension.define_macros.append(('PSUTIL_SUNOS10', 1)) 335 else: 336 # Other releases are by default considered to be new mib compliant. 337 posix_extension.define_macros.append(('NEW_MIB_COMPLIANT', 1)) 338 elif AIX: 339 posix_extension.sources.append('psutil/arch/aix/ifaddrs.c') 340 341 extensions = [ext, posix_extension] 342else: 343 extensions = [ext] 344 345 346def main(): 347 kwargs = dict( 348 name='psutil', 349 version=VERSION, 350 description=__doc__ .replace('\n', ' ').strip() if __doc__ else '', 351 long_description=get_description(), 352 long_description_content_type='text/x-rst', 353 keywords=[ 354 'ps', 'top', 'kill', 'free', 'lsof', 'netstat', 'nice', 'tty', 355 'ionice', 'uptime', 'taskmgr', 'process', 'df', 'iotop', 'iostat', 356 'ifconfig', 'taskset', 'who', 'pidof', 'pmap', 'smem', 'pstree', 357 'monitoring', 'ulimit', 'prlimit', 'smem', 'performance', 358 'metrics', 'agent', 'observability', 359 ], 360 author='Giampaolo Rodola', 361 author_email='g.rodola@gmail.com', 362 url='https://github.com/giampaolo/psutil', 363 platforms='Platform Independent', 364 license='BSD', 365 packages=['psutil', 'psutil.tests'], 366 ext_modules=extensions, 367 classifiers=[ 368 'Development Status :: 5 - Production/Stable', 369 'Environment :: Console', 370 'Environment :: Win32 (MS Windows)', 371 'Intended Audience :: Developers', 372 'Intended Audience :: Information Technology', 373 'Intended Audience :: System Administrators', 374 'License :: OSI Approved :: BSD License', 375 'Operating System :: MacOS :: MacOS X', 376 'Operating System :: Microsoft :: Windows :: Windows 10', 377 'Operating System :: Microsoft :: Windows :: Windows 7', 378 'Operating System :: Microsoft :: Windows :: Windows 8', 379 'Operating System :: Microsoft :: Windows :: Windows 8.1', 380 'Operating System :: Microsoft :: Windows :: Windows Server 2003', 381 'Operating System :: Microsoft :: Windows :: Windows Server 2008', 382 'Operating System :: Microsoft :: Windows :: Windows Vista', 383 'Operating System :: Microsoft', 384 'Operating System :: OS Independent', 385 'Operating System :: POSIX :: AIX', 386 'Operating System :: POSIX :: BSD :: FreeBSD', 387 'Operating System :: POSIX :: BSD :: NetBSD', 388 'Operating System :: POSIX :: BSD :: OpenBSD', 389 'Operating System :: POSIX :: BSD', 390 'Operating System :: POSIX :: Linux', 391 'Operating System :: POSIX :: SunOS/Solaris', 392 'Operating System :: POSIX', 393 'Programming Language :: C', 394 'Programming Language :: Python :: 2', 395 'Programming Language :: Python :: 2.6', 396 'Programming Language :: Python :: 2.7', 397 'Programming Language :: Python :: 3', 398 'Programming Language :: Python :: Implementation :: CPython', 399 'Programming Language :: Python :: Implementation :: PyPy', 400 'Programming Language :: Python', 401 'Topic :: Software Development :: Libraries :: Python Modules', 402 'Topic :: Software Development :: Libraries', 403 'Topic :: System :: Benchmark', 404 'Topic :: System :: Hardware :: Hardware Drivers', 405 'Topic :: System :: Hardware', 406 'Topic :: System :: Monitoring', 407 'Topic :: System :: Networking :: Monitoring :: Hardware Watchdog', 408 'Topic :: System :: Networking :: Monitoring', 409 'Topic :: System :: Networking', 410 'Topic :: System :: Operating System', 411 'Topic :: System :: Systems Administration', 412 'Topic :: Utilities', 413 ], 414 ) 415 if setuptools is not None: 416 kwargs.update( 417 python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", 418 extras_require=extras_require, 419 zip_safe=False, 420 ) 421 success = False 422 try: 423 setup(**kwargs) 424 success = True 425 finally: 426 if not success and POSIX and not which('gcc'): 427 py3 = "3" if PY3 else "" 428 if LINUX: 429 if which('dpkg'): 430 missdeps("sudo apt-get install gcc python%s-dev" % py3) 431 elif which('rpm'): 432 missdeps("sudo yum install gcc python%s-devel" % py3) 433 elif MACOS: 434 print(hilite("XCode (https://developer.apple.com/xcode/) " 435 "is not installed"), color="red", file=sys.stderr) 436 elif FREEBSD: 437 missdeps("pkg install gcc python%s" % py3) 438 elif OPENBSD: 439 missdeps("pkg_add -v gcc python%s" % py3) 440 elif NETBSD: 441 missdeps("pkgin install gcc python%s" % py3) 442 elif SUNOS: 443 missdeps("sudo ln -s /usr/bin/gcc /usr/local/bin/cc && " 444 "pkg install gcc") 445 446 447if __name__ == '__main__': 448 main() 449