diff options
-rw-r--r-- | INSTALL.rst | 2 | ||||
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | docs/conf.py | 26 | ||||
-rw-r--r-- | docs/index.rst | 2 | ||||
-rwxr-xr-x | examples/disk_usage.py | 7 | ||||
-rwxr-xr-x | examples/free.py | 7 | ||||
-rwxr-xr-x | examples/meminfo.py | 7 | ||||
-rwxr-xr-x | examples/netstat.py | 5 | ||||
-rwxr-xr-x | examples/pmap.py | 11 | ||||
-rwxr-xr-x | examples/process_detail.py | 4 | ||||
-rw-r--r-- | examples/ps.py | 26 | ||||
-rwxr-xr-x | examples/who.py | 3 | ||||
-rw-r--r-- | make.bat | 3 | ||||
-rw-r--r-- | psutil/__init__.py | 53 | ||||
-rw-r--r-- | psutil/_common.py | 14 | ||||
-rw-r--r-- | psutil/_compat.py | 246 | ||||
-rw-r--r-- | psutil/_psbsd.py | 8 | ||||
-rw-r--r-- | psutil/_pslinux.py | 365 | ||||
-rw-r--r-- | psutil/_psosx.py | 9 | ||||
-rw-r--r-- | psutil/_psposix.py | 9 | ||||
-rw-r--r-- | psutil/_pssunos.py | 24 | ||||
-rw-r--r-- | psutil/_psutil_linux.c | 6 | ||||
-rw-r--r-- | psutil/_pswindows.py | 28 | ||||
-rw-r--r-- | setup.py | 12 | ||||
-rw-r--r-- | test/_windows.py | 6 | ||||
-rw-r--r-- | test/test_memory_leaks.py | 5 | ||||
-rw-r--r-- | test/test_psutil.py | 222 |
27 files changed, 342 insertions, 782 deletions
diff --git a/INSTALL.rst b/INSTALL.rst index 93ce4691..bb30a06b 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -56,8 +56,6 @@ Compiling on Windows using Visual Studio To use Visual Studio to compile psutil you must have the same version of Visual Studio used to compile your installation of Python which is:: - Python 2.4: VS 2003 - Python 2.5: VS 2003 Python 2.6: VS 2008 Python 2.7: VS 2008 Python 3.3+: VS 2010 @@ -27,20 +27,10 @@ build: clean $(PYTHON) setup.py build install: build - if test $(PYTHON) = python2.4; then \ - $(PYTHON) setup.py install; \ - elif test $(PYTHON) = python2.5; then \ - $(PYTHON) setup.py install; \ - else \ - $(PYTHON) setup.py install --user; \ - fi + $(PYTHON) setup.py install --user; \ uninstall: - if test $(PYTHON) = python2.4; then \ - pip-2.4 uninstall -y -v psutil; \ - else \ - cd ..; $(PYTHON) -m pip uninstall -y -v psutil; \ - fi + cd ..; $(PYTHON) -m pip uninstall -y -v psutil; \ test: install $(PYTHON) $(TSCRIPT) diff --git a/docs/conf.py b/docs/conf.py index 9ec91df7..9fa163b6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,29 +14,17 @@ import datetime import os -import sys -if sys.version_info >= (3, ): - def u(s): - return s -else: - def u(s): - if not isinstance(s, unicode): # NOQA - s = unicode(s, "unicode_escape") # NOQA - return s - - -PROJECT_NAME = u("psutil") -AUTHOR = u("Giampaolo Rodola'") +PROJECT_NAME = "psutil" +AUTHOR = "Giampaolo Rodola'" THIS_YEAR = str(datetime.datetime.now().year) HERE = os.path.abspath(os.path.dirname(__file__)) def get_version(): INIT = os.path.abspath(os.path.join(HERE, '../psutil/__init__.py')) - f = open(INIT, 'r') - try: + with open(INIT, 'r') as f: for line in f: if line.startswith('__version__'): ret = eval(line.strip().split(' = ')[1]) @@ -46,8 +34,6 @@ def get_version(): return ret else: raise ValueError("couldn't find version string") - finally: - f.close() VERSION = get_version() @@ -77,7 +63,7 @@ master_doc = 'index' # General information about the project. project = PROJECT_NAME -copyright = u('2009-%s, %s' % (THIS_YEAR, AUTHOR)) +copyright = '2009-%s, %s' % (THIS_YEAR, AUTHOR) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -223,7 +209,7 @@ htmlhelp_basename = '%s-doc' % PROJECT_NAME # [howto/manual]). latex_documents = [ ('index', '%s.tex' % PROJECT_NAME, - u('%s documentation') % PROJECT_NAME, AUTHOR), + '%s documentation' % PROJECT_NAME, AUTHOR), ] # The name of an image file (relative to this directory) to place at @@ -255,7 +241,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', PROJECT_NAME, u('%s documentation') % PROJECT_NAME, [AUTHOR], 1) + ('index', PROJECT_NAME, '%s documentation' % PROJECT_NAME, [AUTHOR], 1) ] # If true, show URL addresses after external links. diff --git a/docs/index.rst b/docs/index.rst index e053b935..5e0914cb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -38,7 +38,7 @@ From project's home page: ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap*. It currently supports **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and **64-bit** architectures, with Python versions from - **2.4** to **3.4**. + **2.6** to **3.4**. `Pypy <http://pypy.org/>`__ is also known to work. The psutil documentation you're reading is distributed as a single HTML page. diff --git a/examples/disk_usage.py b/examples/disk_usage.py index 787cb0f5..d8600a8c 100755 --- a/examples/disk_usage.py +++ b/examples/disk_usage.py @@ -18,7 +18,6 @@ Device Total Used Free Use % Type Mount import sys import os import psutil -from psutil._compat import print_ def bytes2human(n): @@ -40,8 +39,8 @@ def bytes2human(n): def main(): templ = "%-17s %8s %8s %8s %5s%% %9s %s" - print_(templ % ("Device", "Total", "Used", "Free", "Use ", "Type", - "Mount")) + print(templ % ("Device", "Total", "Used", "Free", "Use ", "Type", + "Mount")) for part in psutil.disk_partitions(all=False): if os.name == 'nt': if 'cdrom' in part.opts or part.fstype == '': @@ -50,7 +49,7 @@ def main(): # partition or just hang. continue usage = psutil.disk_usage(part.mountpoint) - print_(templ % ( + print(templ % ( part.device, bytes2human(usage.total), bytes2human(usage.used), diff --git a/examples/free.py b/examples/free.py index 95e11fb6..913ca58a 100755 --- a/examples/free.py +++ b/examples/free.py @@ -14,15 +14,14 @@ Swap: 0 0 0 """ import psutil -from psutil._compat import print_ def main(): virt = psutil.virtual_memory() swap = psutil.swap_memory() templ = "%-7s %10s %10s %10s %10s %10s %10s" - print_(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache')) - print_(templ % ( + print(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache')) + print(templ % ( 'Mem:', int(virt.total / 1024), int(virt.used / 1024), @@ -30,7 +29,7 @@ def main(): int(getattr(virt, 'shared', 0) / 1024), int(getattr(virt, 'buffers', 0) / 1024), int(getattr(virt, 'cached', 0) / 1024))) - print_(templ % ( + print(templ % ( 'Swap:', int(swap.total / 1024), int(swap.used / 1024), int(swap.free / 1024), diff --git a/examples/meminfo.py b/examples/meminfo.py index 671f907c..c463a3de 100755 --- a/examples/meminfo.py +++ b/examples/meminfo.py @@ -31,7 +31,6 @@ Sout : 0B """ import psutil -from psutil._compat import print_ def bytes2human(n): @@ -56,13 +55,13 @@ def pprint_ntuple(nt): value = getattr(nt, name) if name != 'percent': value = bytes2human(value) - print_('%-10s : %7s' % (name.capitalize(), value)) + print('%-10s : %7s' % (name.capitalize(), value)) def main(): - print_('MEMORY\n------') + print('MEMORY\n------') pprint_ntuple(psutil.virtual_memory()) - print_('\nSWAP\n----') + print('\nSWAP\n----') pprint_ntuple(psutil.swap_memory()) if __name__ == '__main__': diff --git a/examples/netstat.py b/examples/netstat.py index 70bc2317..884622e9 100755 --- a/examples/netstat.py +++ b/examples/netstat.py @@ -23,7 +23,6 @@ import socket from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM import psutil -from psutil._compat import print_ AD = "-" @@ -38,7 +37,7 @@ proto_map = { def main(): templ = "%-5s %-30s %-30s %-13s %-6s %s" - print_(templ % ( + print(templ % ( "Proto", "Local address", "Remote address", "Status", "PID", "Program name")) proc_names = {} @@ -52,7 +51,7 @@ def main(): raddr = "" if c.raddr: raddr = "%s:%s" % (c.raddr) - print_(templ % ( + print(templ % ( proto_map[(c.family, c.type)], laddr, raddr or AD, diff --git a/examples/pmap.py b/examples/pmap.py index 1936c0b2..7593777a 100755 --- a/examples/pmap.py +++ b/examples/pmap.py @@ -33,26 +33,25 @@ ffffffffff600000 0K r-xp [vsyscall] import sys import psutil -from psutil._compat import print_ def main(): if len(sys.argv) != 2: sys.exit('usage: pmap <pid>') p = psutil.Process(int(sys.argv[1])) - print_("pid=%s, name=%s" % (p.pid, p.name())) + print("pid=%s, name=%s" % (p.pid, p.name())) templ = "%-16s %10s %-7s %s" - print_(templ % ("Address", "RSS", "Mode", "Mapping")) + print(templ % ("Address", "RSS", "Mode", "Mapping")) total_rss = 0 for m in p.memory_maps(grouped=False): total_rss += m.rss - print_(templ % ( + print(templ % ( m.addr.split('-')[0].zfill(16), str(m.rss / 1024) + 'K', m.perms, m.path)) - print_("-" * 33) - print_(templ % ("Total", str(total_rss / 1024) + 'K', '', '')) + print("-" * 33) + print(templ % ("Total", str(total_rss / 1024) + 'K', '', '')) if __name__ == '__main__': main() diff --git a/examples/process_detail.py b/examples/process_detail.py index a0787286..a55fc959 100755 --- a/examples/process_detail.py +++ b/examples/process_detail.py @@ -68,8 +68,8 @@ def run(pid): try: p = psutil.Process(pid) pinfo = p.as_dict(ad_value=ACCESS_DENIED) - except psutil.NoSuchProcess: - sys.exit(str(sys.exc_info()[1])) + except psutil.NoSuchProcess as err: + sys.exit(str(err)) try: parent = p.parent() diff --git a/examples/ps.py b/examples/ps.py index 2ead7470..2b67bd18 100644 --- a/examples/ps.py +++ b/examples/ps.py @@ -16,7 +16,6 @@ import os import time import psutil -from psutil._compat import print_ def main(): @@ -27,8 +26,8 @@ def main(): if os.name == 'posix': attrs.append('uids') attrs.append('terminal') - print_(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", - "START", "TIME", "COMMAND")) + print(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", + "START", "TIME", "COMMAND")) for p in psutil.process_iter(): try: pinfo = p.as_dict(attrs, ad_value='') @@ -65,16 +64,17 @@ def main(): int(pinfo['memory_info'].rss / 1024) or '?' memp = pinfo['memory_percent'] and \ round(pinfo['memory_percent'], 1) or '?' - print_(templ % (user[:10], - pinfo['pid'], - pinfo['cpu_percent'], - memp, - vms, - rss, - pinfo.get('terminal', '') or '?', - ctime, - cputime, - pinfo['name'].strip() or '?')) + print(templ % ( + user[:10], + pinfo['pid'], + pinfo['cpu_percent'], + memp, + vms, + rss, + pinfo.get('terminal', '') or '?', + ctime, + cputime, + pinfo['name'].strip() or '?')) if __name__ == '__main__': diff --git a/examples/who.py b/examples/who.py index 8ffbc818..b382bebf 100755 --- a/examples/who.py +++ b/examples/who.py @@ -18,13 +18,12 @@ giampaolo pts/9 2014-02-27 01:32 (:0) from datetime import datetime import psutil -from psutil._compat import print_ def main(): users = psutil.users() for user in users: - print_("%-15s %-15s %s (%s)" % ( + print("%-15s %-15s %s (%s)" % ( user.name, user.terminal or '-', datetime.fromtimestamp(user.started).strftime("%Y-%m-%d %H:%M"), @@ -7,7 +7,6 @@ rem psutil ("make.bat build", "make.bat install") and running tests rem ("make.bat test"). rem rem This script is modeled after my Windows installation which uses: -rem - mingw32 for Python 2.4 and 2.5 rem - Visual studio 2008 for Python 2.6, 2.7, 3.2 rem - Visual studio 2010 for Python 3.3+ rem ...therefore it might not work on your Windows installation. @@ -17,8 +16,6 @@ rem To compile for a specific Python version run: rem rem set PYTHON=C:\Python24\python.exe & make.bat build rem -rem If you compile by using mingw on Python 2.4 and 2.5 you need to patch -rem distutils first: http://stackoverflow.com/questions/13592192 rem ========================================================================== if "%PYTHON%" == "" ( diff --git a/psutil/__init__.py b/psutil/__init__.py index 8cb55187..f1f89e8f 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -38,22 +38,23 @@ __all__ = [ "users", "boot_time", # others ] -import sys +import collections +import errno +import functools import os -import time import signal -import warnings -import errno import subprocess +import sys +import time +import warnings try: import pwd except ImportError: pwd = None from psutil._common import memoize -from psutil._compat import property, callable, long, defaultdict -from psutil._compat import (wraps as _wraps, - PY3 as _PY3) +from psutil._compat import callable, long +from psutil._compat import PY3 as _PY3 from psutil._common import (deprecated_method as _deprecated_method, deprecated as _deprecated, sdiskio as _nt_sys_diskio, @@ -251,7 +252,7 @@ def _assert_pid_not_reused(fun): """Decorator which raises NoSuchProcess in case a process is no longer running or its PID has been reused. """ - @_wraps(fun) + @functools.wraps(fun) def wrapper(self, *args, **kwargs): if not self.is_running(): raise NoSuchProcess(self.pid, self._name) @@ -531,8 +532,7 @@ class Process(object): if self._exe is None: try: exe = self._proc.exe() - except AccessDenied: - err = sys.exc_info()[1] + except AccessDenied as err: return guess_it(fallback=err) else: if not exe: @@ -767,7 +767,7 @@ class Process(object): else: # construct a dict where 'values' are all the processes # having 'key' as their parent - table = defaultdict(list) + table = collections.defaultdict(list) if ppid_map is None: for p in process_iter(): try: @@ -966,8 +966,7 @@ class Process(object): def _send_signal(self, sig): try: os.kill(self.pid, sig) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ESRCH: self._gone = True raise NoSuchProcess(self.pid, self._name) @@ -1888,7 +1887,6 @@ def test(): output. """ import datetime - from psutil._compat import print_ today_day = datetime.date.today() templ = "%-10s %5s %4s %4s %7s %7s %-13s %5s %7s %s" @@ -1897,8 +1895,8 @@ def test(): if _POSIX: attrs.append('uids') attrs.append('terminal') - print_(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", - "START", "TIME", "COMMAND")) + print(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", + "START", "TIME", "COMMAND")) for p in process_iter(): try: pinfo = p.as_dict(attrs, ad_value='') @@ -1935,16 +1933,17 @@ def test(): int(pinfo['memory_info'].rss / 1024) or '?' memp = pinfo['memory_percent'] and \ round(pinfo['memory_percent'], 1) or '?' - print_(templ % (user[:10], - pinfo['pid'], - pinfo['cpu_percent'], - memp, - vms, - rss, - pinfo.get('terminal', '') or '?', - ctime, - cputime, - pinfo['name'].strip() or '?')) + print(templ % ( + user[:10], + pinfo['pid'], + pinfo['cpu_percent'], + memp, + vms, + rss, + pinfo.get('terminal', '') or '?', + ctime, + cputime, + pinfo['name'].strip() or '?')) def _replace_module(): @@ -1984,7 +1983,7 @@ def _replace_module(): _replace_module() -del property, memoize, division, _replace_module +del memoize, division, _replace_module if sys.version_info < (3, 0): del num diff --git a/psutil/_common.py b/psutil/_common.py index 9db5ad37..92d0fd0c 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -8,20 +8,19 @@ from __future__ import division import errno +import functools import os import socket import stat -import sys import warnings try: import threading except ImportError: import dummy_threading as threading +from collections import namedtuple from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM -from psutil._compat import namedtuple, wraps - # --- constants AF_INET6 = getattr(socket, 'AF_INET6', None) @@ -82,7 +81,7 @@ def memoize(fun): >>> foo.cache_clear() >>> """ - @wraps(fun) + @functools.wraps(fun) def wrapper(*args, **kwargs): key = (args, frozenset(sorted(kwargs.items()))) lock.acquire() @@ -119,7 +118,7 @@ def deprecated(replacement=None): if fun.__doc__ is None: fun.__doc__ = msg - @wraps(fun) + @functools.wraps(fun) def inner(*args, **kwargs): warnings.warn(msg, category=DeprecationWarning, stacklevel=2) return fun(*args, **kwargs) @@ -138,7 +137,7 @@ def deprecated_method(replacement): if fun.__doc__ is None: fun.__doc__ = msg - @wraps(fun) + @functools.wraps(fun) def inner(self, *args, **kwargs): warnings.warn(msg, category=DeprecationWarning, stacklevel=2) return getattr(self, replacement)(*args, **kwargs) @@ -153,8 +152,7 @@ def isfile_strict(path): """ try: st = os.stat(path) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in (errno.EPERM, errno.EACCES): raise return False diff --git a/psutil/_compat.py b/psutil/_compat.py index ad3bccee..84fd9ca8 100644 --- a/psutil/_compat.py +++ b/psutil/_compat.py @@ -6,9 +6,10 @@ """Module which provides compatibility with older Python versions.""" -__all__ = ["PY3", "int", "long", "xrange", "exec_", "callable", "namedtuple", - "property", "wraps", "defaultdict", "update_wrapper", "lru_cache"] +__all__ = ["PY3", "int", "long", "xrange", "exec_", "callable", "lru_cache"] +import collections +import functools import sys try: import __builtin__ @@ -24,13 +25,6 @@ if PY3: unicode = str basestring = str exec_ = getattr(__builtin__, "exec") - print_ = getattr(__builtin__, "print") - - def u(s): - return s - - def b(s): - return s.encode("latin-1") else: int = int long = long @@ -38,15 +32,9 @@ else: unicode = unicode basestring = basestring - def u(s): - return unicode(s, "unicode_escape") - - def b(s): - return s - def exec_(code, globs=None, locs=None): if globs is None: - frame = _sys._getframe(1) + frame = sys._getframe(1) globs = frame.f_globals if locs is None: locs = frame.f_locals @@ -55,10 +43,6 @@ else: locs = globs exec("""exec code in globs, locs""") - def print_(s): - sys.stdout.write(s + '\n') - sys.stdout.flush() - # removed in 3.0, reintroduced in 3.2 try: @@ -70,222 +54,6 @@ except NameError: # --- stdlib additions -# py 2.6 collections.namedtuple -# Taken from: http://code.activestate.com/recipes/500261/ -# Credits: Raymond Hettinger -try: - from collections import namedtuple -except ImportError: - from operator import itemgetter as _itemgetter - from keyword import iskeyword as _iskeyword - import sys as _sys - - def namedtuple(typename, field_names, verbose=False, rename=False): - """A collections.namedtuple implementation, see: - http://docs.python.org/library/collections.html#namedtuple - """ - if isinstance(field_names, basestring): - field_names = field_names.replace(',', ' ').split() - field_names = tuple(map(str, field_names)) - if rename: - names = list(field_names) - seen = set() - for i, name in enumerate(names): - if ((not min(c.isalnum() or c == '_' for c in name) - or _iskeyword(name) - or not name or name[0].isdigit() - or name.startswith('_') - or name in seen)): - names[i] = '_%d' % i - seen.add(name) - field_names = tuple(names) - for name in (typename,) + field_names: - if not min(c.isalnum() or c == '_' for c in name): - raise ValueError('Type names and field names can only contain ' - 'alphanumeric characters and underscores: %r' - % name) - if _iskeyword(name): - raise ValueError('Type names and field names cannot be a ' - 'keyword: %r' % name) - if name[0].isdigit(): - raise ValueError('Type names and field names cannot start ' - 'with a number: %r' % name) - seen_names = set() - for name in field_names: - if name.startswith('_') and not rename: - raise ValueError( - 'Field names cannot start with an underscore: %r' % name) - if name in seen_names: - raise ValueError('Encountered duplicate field name: %r' % name) - seen_names.add(name) - - numfields = len(field_names) - argtxt = repr(field_names).replace("'", "")[1:-1] - reprtxt = ', '.join('%s=%%r' % name for name in field_names) - template = '''class %(typename)s(tuple): - '%(typename)s(%(argtxt)s)' \n - __slots__ = () \n - _fields = %(field_names)r \n - def __new__(_cls, %(argtxt)s): - return _tuple.__new__(_cls, (%(argtxt)s)) \n - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - 'Make a new %(typename)s object from a sequence or iterable' - result = new(cls, iterable) - if len(result) != %(numfields)d: - raise TypeError( - 'Expected %(numfields)d arguments, got %%d' %% len(result)) - return result \n - def __repr__(self): - return '%(typename)s(%(reprtxt)s)' %% self \n - def _asdict(self): - 'Return a new dict which maps field names to their values' - return dict(zip(self._fields, self)) \n - def _replace(_self, **kwds): - result = _self._make(map(kwds.pop, %(field_names)r, _self)) - if kwds: - raise ValueError( - 'Got unexpected field names: %%r' %% kwds.keys()) - return result \n - def __getnewargs__(self): - return tuple(self) \n\n''' % locals() - for i, name in enumerate(field_names): - template += ' %s = _property(_itemgetter(%d))\n' % (name, i) - if verbose: - sys.stdout.write(template + '\n') - sys.stdout.flush() - - namespace = dict( - _itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, - _property=property, _tuple=tuple) - try: - exec_(template, namespace) - except SyntaxError: - e = sys.exc_info()[1] - raise SyntaxError(e.message + ':\n' + template) - result = namespace[typename] - try: - result.__module__ = _sys._getframe( - 1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - - return result - - -# hack to support property getter/setter/deleter on python < 2.6 -# http://docs.python.org/library/functions.html?highlight=property#property -if hasattr(property, 'setter'): - property = property -else: - class property(__builtin__.property): - __metaclass__ = type - - def __init__(self, fget, *args, **kwargs): - super(property, self).__init__(fget, *args, **kwargs) - self.__doc__ = fget.__doc__ - - def getter(self, method): - return property(method, self.fset, self.fdel) - - def setter(self, method): - return property(self.fget, method, self.fdel) - - def deleter(self, method): - return property(self.fget, self.fset, method) - - -# py 2.5 collections.defauldict -# Taken from: -# http://code.activestate.com/recipes/523034-emulate-collectionsdefaultdict/ -# Credits: Jason Kirtland -try: - from collections import defaultdict -except ImportError: - class defaultdict(dict): - """Dict subclass that calls a factory function to supply - missing values: - http://docs.python.org/library/collections.html#collections.defaultdict - """ - - def __init__(self, default_factory=None, *a, **kw): - if ((default_factory is not None and - not hasattr(default_factory, '__call__'))): - raise TypeError('first argument must be callable') - dict.__init__(self, *a, **kw) - self.default_factory = default_factory - - def __getitem__(self, key): - try: - return dict.__getitem__(self, key) - except KeyError: - return self.__missing__(key) - - def __missing__(self, key): - if self.default_factory is None: - raise KeyError(key) - self[key] = value = self.default_factory() - return value - - def __reduce__(self): - if self.default_factory is None: - args = tuple() - else: - args = self.default_factory, - return type(self), args, None, None, self.items() - - def copy(self): - return self.__copy__() - - def __copy__(self): - return type(self)(self.default_factory, self) - - def __deepcopy__(self, memo): - import copy - return type(self)(self.default_factory, - copy.deepcopy(self.items())) - - def __repr__(self): - return 'defaultdict(%s, %s)' % (self.default_factory, - dict.__repr__(self)) - - -# py 2.5 functools.wraps -try: - from functools import wraps -except ImportError: - def wraps(original): - def inner(fn): - for attribute in ['__module__', '__name__', '__doc__']: - setattr(fn, attribute, getattr(original, attribute)) - for attribute in ['__dict__']: - if hasattr(fn, attribute): - getattr(fn, attribute).update(getattr(original, attribute)) - else: - setattr(fn, attribute, - getattr(original, attribute).copy()) - return fn - return inner - - -# py 2.5 functools.update_wrapper -try: - from functools import update_wrapper -except ImportError: - WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') - WRAPPER_UPDATES = ('__dict__',) - - def update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, - updated=WRAPPER_UPDATES): - """Update a wrapper function to look like the wrapped function, see: - http://docs.python.org/library/functools.html#functools.update_wrapper - """ - for attr in assigned: - setattr(wrapper, attr, getattr(wrapped, attr)) - for attr in updated: - getattr(wrapper, attr).update(getattr(wrapped, attr, {})) - return wrapper - # py 3.2 functools.lru_cache # Taken from: http://code.activestate.com/recipes/578078 @@ -298,8 +66,8 @@ except ImportError: except ImportError: from dummy_threading import RLock - _CacheInfo = namedtuple("CacheInfo", - ["hits", "misses", "maxsize", "currsize"]) + _CacheInfo = collections.namedtuple( + "CacheInfo", ["hits", "misses", "maxsize", "currsize"]) class _HashedSeq(list): __slots__ = 'hashvalue' @@ -430,6 +198,6 @@ except ImportError: wrapper.__wrapped__ = user_function wrapper.cache_info = cache_info wrapper.cache_clear = cache_clear - return update_wrapper(wrapper, user_function) + return functools.update_wrapper(wrapper, user_function) return decorating_function diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 9dcdfc21..8560df4c 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -7,13 +7,14 @@ """FreeBSD platform implementation.""" import errno +import functools import os import sys +from collections import namedtuple from psutil import _common from psutil import _psposix from psutil._common import conn_tmap, usage_percent -from psutil._compat import namedtuple, wraps import _psutil_bsd as cext import _psutil_posix @@ -206,15 +207,14 @@ def wrap_exceptions(fun): """Decorator which translates bare OSError exceptions into NoSuchProcess and AccessDenied. """ - @wraps(fun) + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError: + except OSError as err: # support for private module import if NoSuchProcess is None or AccessDenied is None: raise - err = sys.exc_info()[1] if err.errno == errno.ESRCH: raise NoSuchProcess(self.pid, self._name) if err.errno in (errno.EPERM, errno.EACCES): diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 2abd59c3..2bedac6f 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -10,17 +10,19 @@ from __future__ import division import base64 import errno +import functools import os import re import socket import struct import sys import warnings +from collections import namedtuple, defaultdict from psutil import _common from psutil import _psposix from psutil._common import (isfile_strict, usage_percent, deprecated) -from psutil._compat import PY3, namedtuple, wraps, b, defaultdict +from psutil._compat import PY3 import _psutil_linux as cext import _psutil_posix @@ -102,11 +104,8 @@ def _get_cputimes_fields(): (user, nice, system, idle, iowait, irq, softirq, [steal, [guest, [guest_nice]]]) """ - f = open('/proc/stat', 'rb') - try: + with open('/proc/stat', 'rb') as f: values = f.readline().split()[1:] - finally: - f.close() fields = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq'] vlen = len(values) if vlen >= 8: @@ -143,15 +142,13 @@ pmmap_ext = namedtuple( def virtual_memory(): total, free, buffers, shared, _, _ = cext.linux_sysinfo() cached = active = inactive = None - f = open('/proc/meminfo', 'rb') - CACHED, ACTIVE, INACTIVE = b("Cached:"), b("Active:"), b("Inactive:") - try: + with open('/proc/meminfo', 'rb') as f: for line in f: - if line.startswith(CACHED): + if line.startswith(b"Cached:"): cached = int(line.split()[1]) * 1024 - elif line.startswith(ACTIVE): + elif line.startswith(b"Active:"): active = int(line.split()[1]) * 1024 - elif line.startswith(INACTIVE): + elif line.startswith(b"Inactive:"): inactive = int(line.split()[1]) * 1024 if (cached is not None and active is not None @@ -164,8 +161,6 @@ def virtual_memory(): "be determined and were set to 0" warnings.warn(msg, RuntimeWarning) cached = active = inactive = 0 - finally: - f.close() avail = free + buffers + cached used = total - free percent = usage_percent((total - avail), total, _round=1) @@ -178,16 +173,14 @@ def swap_memory(): used = total - free percent = usage_percent(used, total, _round=1) # get pgin/pgouts - f = open("/proc/vmstat", "rb") - SIN, SOUT = b('pswpin'), b('pswpout') - sin = sout = None - try: + with open("/proc/vmstat", "rb") as f: + sin = sout = None for line in f: # values are expressed in 4 kilo bytes, we want bytes instead - if line.startswith(SIN): - sin = int(line.split(b(' '))[1]) * 4 * 1024 - elif line.startswith(SOUT): - sout = int(line.split(b(' '))[1]) * 4 * 1024 + if line.startswith(b'pswpin'): + sin = int(line.split(b' ')[1]) * 4 * 1024 + elif line.startswith(b'pswpout'): + sout = int(line.split(b' ')[1]) * 4 * 1024 if sin is not None and sout is not None: break else: @@ -197,8 +190,6 @@ def swap_memory(): "be determined and were set to 0" warnings.warn(msg, RuntimeWarning) sin = sout = 0 - finally: - f.close() return _common.sswap(total, used, free, percent, sin, sout) @@ -221,11 +212,8 @@ def cpu_times(): [guest_nice]]]) Last 3 fields may not be available on all Linux kernel versions. """ - f = open('/proc/stat', 'rb') - try: + with open('/proc/stat', 'rb') as f: values = f.readline().split() - finally: - f.close() fields = values[1:len(scputimes._fields) + 1] fields = [float(x) / CLOCK_TICKS for x in fields] return scputimes(*fields) @@ -236,21 +224,17 @@ def per_cpu_times(): for every CPU available on the system. """ cpus = [] - f = open('/proc/stat', 'rb') - try: + with open('/proc/stat', 'rb') as f: # get rid of the first line which refers to system wide CPU stats f.readline() - CPU = b('cpu') for line in f: - if line.startswith(CPU): + if line.startswith(b'cpu'): values = line.split() fields = values[1:len(scputimes._fields) + 1] fields = [float(x) / CLOCK_TICKS for x in fields] entry = scputimes(*fields) cpus.append(entry) return cpus - finally: - f.close() def cpu_count_logical(): @@ -260,30 +244,21 @@ def cpu_count_logical(): except ValueError: # as a second fallback we try to parse /proc/cpuinfo num = 0 - f = open('/proc/cpuinfo', 'rb') - try: - lines = f.readlines() - finally: - f.close() - PROCESSOR = b('processor') - for line in lines: - if line.lower().startswith(PROCESSOR): - num += 1 + with open('/proc/cpuinfo', 'rb') as f: + for line in f: + if line.lower().startswith(b'processor'): + num += 1 # unknown format (e.g. amrel/sparc architectures), see: # https://github.com/giampaolo/psutil/issues/200 # try to parse /proc/stat as a last resort if num == 0: - f = open('/proc/stat', 'rt') - try: - lines = f.readlines() - finally: - f.close() search = re.compile('cpu\d') - for line in lines: - line = line.split(' ')[0] - if search.match(line): - num += 1 + with open('/proc/stat', 'rt') as f: + for line in f: + line = line.split(' ')[0] + if search.match(line): + num += 1 if num == 0: # mimic os.cpu_count() @@ -293,16 +268,11 @@ def cpu_count_logical(): def cpu_count_physical(): """Return the number of physical CPUs in the system.""" - f = open('/proc/cpuinfo', 'rb') - try: - lines = f.readlines() - finally: - f.close() - found = set() - PHYSICAL_ID = b('physical id') - for line in lines: - if line.lower().startswith(PHYSICAL_ID): - found.add(line.strip()) + with open('/proc/cpuinfo', 'rb') as f: + found = set() + for line in f: + if line.lower().startswith(b'physical id'): + found.add(line.strip()) if found: return len(found) else: @@ -332,24 +302,20 @@ def users(): def boot_time(): """Return the system boot time expressed in seconds since the epoch.""" global BOOT_TIME - f = open('/proc/stat', 'rb') - try: - BTIME = b('btime') + with open('/proc/stat', 'rb') as f: for line in f: - if line.startswith(BTIME): + if line.startswith(b'btime'): ret = float(line.strip().split()[1]) BOOT_TIME = ret return ret raise RuntimeError("line 'btime' not found") - finally: - f.close() # --- processes def pids(): """Returns a list of PIDs currently running on the system.""" - return [int(x) for x in os.listdir(b('/proc')) if x.isdigit()] + return [int(x) for x in os.listdir(b'/proc') if x.isdigit()] def pid_exists(pid): @@ -411,7 +377,7 @@ class Connections: for pid in pids(): try: inodes.update(self.get_proc_inodes(pid)) - except OSError: + except OSError as err: # os.listdir() is gonna raise a lot of access denied # exceptions in case of unprivileged user; that's fine # as we'll just end up returning a connection with PID @@ -419,7 +385,6 @@ class Connections: # Both netstat -an and lsof does the same so it's # unlikely we can do any better. # ENOENT just means a PID disappeared on us. - err = sys.exc_info()[1] if err.errno not in ( errno.ENOENT, errno.ESRCH, errno.EPERM, errno.EACCES): raise @@ -477,59 +442,57 @@ class Connections: if file.endswith('6') and not os.path.exists(file): # IPv6 not supported return - f = open(file, 'rt') - f.readline() # skip the first line - for line in f: - _, laddr, raddr, status, _, _, _, _, _, inode = \ - line.split()[:10] - if inode in inodes: - # We assume inet sockets are unique, so we error - # out if there are multiple references to the - # same inode. We won't do this for UNIX sockets. - if len(inodes[inode]) > 1 and type_ != socket.AF_UNIX: - raise ValueError("ambiguos inode with multiple " - "PIDs references") - pid, fd = inodes[inode][0] - else: - pid, fd = None, -1 - if filter_pid is not None and filter_pid != pid: - continue - else: - if type_ == socket.SOCK_STREAM: - status = TCP_STATUSES[status] + with open(file, 'rt') as f: + f.readline() # skip the first line + for line in f: + _, laddr, raddr, status, _, _, _, _, _, inode = \ + line.split()[:10] + if inode in inodes: + # We assume inet sockets are unique, so we error + # out if there are multiple references to the + # same inode. We won't do this for UNIX sockets. + if len(inodes[inode]) > 1 and type_ != socket.AF_UNIX: + raise ValueError("ambiguos inode with multiple " + "PIDs references") + pid, fd = inodes[inode][0] else: - status = _common.CONN_NONE - laddr = self.decode_address(laddr, family) - raddr = self.decode_address(raddr, family) - yield (fd, family, type_, laddr, raddr, status, pid) - f.close() + pid, fd = None, -1 + if filter_pid is not None and filter_pid != pid: + continue + else: + if type_ == socket.SOCK_STREAM: + status = TCP_STATUSES[status] + else: + status = _common.CONN_NONE + laddr = self.decode_address(laddr, family) + raddr = self.decode_address(raddr, family) + yield (fd, family, type_, laddr, raddr, status, pid) def process_unix(self, file, family, inodes, filter_pid=None): """Parse /proc/net/unix files.""" - f = open(file, 'rt') - f.readline() # skip the first line - for line in f: - tokens = line.split() - _, _, _, _, type_, _, inode = tokens[0:7] - if inode in inodes: - # With UNIX sockets we can have a single inode - # referencing many file descriptors. - pairs = inodes[inode] - else: - pairs = [(None, -1)] - for pid, fd in pairs: - if filter_pid is not None and filter_pid != pid: - continue + with open(file, 'rt') as f: + f.readline() # skip the first line + for line in f: + tokens = line.split() + _, _, _, _, type_, _, inode = tokens[0:7] + if inode in inodes: + # With UNIX sockets we can have a single inode + # referencing many file descriptors. + pairs = inodes[inode] else: - if len(tokens) == 8: - path = tokens[-1] + pairs = [(None, -1)] + for pid, fd in pairs: + if filter_pid is not None and filter_pid != pid: + continue else: - path = "" - type_ = int(type_) - raddr = None - status = _common.CONN_NONE - yield (fd, family, type_, path, raddr, status, pid) - f.close() + if len(tokens) == 8: + path = tokens[-1] + else: + path = "" + type_ = int(type_) + raddr = None + status = _common.CONN_NONE + yield (fd, family, type_, path, raddr, status, pid) def retrieve(self, kind, pid=None): if kind not in self.tmap: @@ -573,12 +536,8 @@ def net_io_counters(): """Return network I/O statistics for every network interface installed on the system as a dict of raw tuples. """ - f = open("/proc/net/dev", "rt") - try: + with open("/proc/net/dev", "rt") as f: lines = f.readlines() - finally: - f.close() - retdict = {} for line in lines[2:]: colon = line.rfind(':') @@ -611,11 +570,8 @@ def disk_io_counters(): # determine partitions we want to look for partitions = [] - f = open("/proc/partitions", "rt") - try: + with open("/proc/partitions", "rt") as f: lines = f.readlines()[2:] - finally: - f.close() for line in reversed(lines): _, _, _, name = line.split() if name[-1].isdigit(): @@ -631,11 +587,8 @@ def disk_io_counters(): partitions.append(name) # retdict = {} - f = open("/proc/diskstats", "rt") - try: + with open("/proc/diskstats", "rt") as f: lines = f.readlines() - finally: - f.close() for line in lines: # http://www.mjmwired.net/kernel/Documentation/iostats.txt fields = line.split() @@ -660,13 +613,10 @@ def disk_io_counters(): def disk_partitions(all=False): """Return mounted disk partitions as a list of nameduples""" phydevs = [] - f = open("/proc/filesystems", "r") - try: + with open("/proc/filesystems", "r") as f: for line in f: if not line.startswith("nodev"): phydevs.append(line.strip()) - finally: - f.close() retlist = [] partitions = cext.disk_partitions() @@ -691,18 +641,17 @@ def wrap_exceptions(fun): """Decorator which translates bare OSError and IOError exceptions into NoSuchProcess and AccessDenied. """ - @wraps(fun) + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError: + except EnvironmentError as err: # support for private module import if NoSuchProcess is None or AccessDenied is None: raise # ENOENT (no such file or directory) gets raised on open(). # ESRCH (no such process) can get raised on read() if # process is gone in meantime. - err = sys.exc_info()[1] if err.errno in (errno.ENOENT, errno.ESRCH): raise NoSuchProcess(self.pid, self._name) if err.errno in (errno.EPERM, errno.EACCES): @@ -727,18 +676,14 @@ class Process(object): f = open(fname, "rt", encoding=DEFAULT_ENCODING) else: f = open(fname, "rt") - try: - name = f.read().split(' ')[1].replace('(', '').replace(')', '') - finally: - f.close() - # XXX - gets changed later and probably needs refactoring - return name + with f: + # XXX - gets changed later and probably needs refactoring + return f.read().split(' ')[1].replace('(', '').replace(')', '') def exe(self): try: exe = os.readlink("/proc/%s/exe" % self.pid) - except (OSError, IOError): - err = sys.exc_info()[1] + except (OSError, IOError) as err: if err.errno in (errno.ENOENT, errno.ESRCH): # no such file error; might be raised also if the # path actually exists for system processes with @@ -769,20 +714,15 @@ class Process(object): f = open(fname, "rt", encoding=DEFAULT_ENCODING) else: f = open(fname, "rt") - try: + with f: # return the args as a list return [x for x in f.read().split('\x00') if x] - finally: - f.close() @wrap_exceptions def terminal(self): tmap = _psposix._get_terminal_map() - f = open("/proc/%s/stat" % self.pid, 'rb') - try: - tty_nr = int(f.read().split(b(' '))[6]) - finally: - f.close() + with open("/proc/%s/stat" % self.pid, 'rb') as f: + tty_nr = int(f.read().split(b' ')[6]) try: return tmap[tty_nr] except KeyError: @@ -792,27 +732,22 @@ class Process(object): @wrap_exceptions def io_counters(self): fname = "/proc/%s/io" % self.pid - f = open(fname, 'rb') - SYSCR, SYSCW = b("syscr"), b("syscw") - READ_BYTES, WRITE_BYTES = b("read_bytes"), b("write_bytes") - try: + with open(fname, 'rb') as f: rcount = wcount = rbytes = wbytes = None for line in f: - if rcount is None and line.startswith(SYSCR): + if rcount is None and line.startswith(b"syscr"): rcount = int(line.split()[1]) - elif wcount is None and line.startswith(SYSCW): + elif wcount is None and line.startswith(b"syscw"): wcount = int(line.split()[1]) - elif rbytes is None and line.startswith(READ_BYTES): + elif rbytes is None and line.startswith(b"read_bytes"): rbytes = int(line.split()[1]) - elif wbytes is None and line.startswith(WRITE_BYTES): + elif wbytes is None and line.startswith(b"write_bytes"): wbytes = int(line.split()[1]) for x in (rcount, wcount, rbytes, wbytes): if x is None: raise NotImplementedError( "couldn't read all necessary info from %r" % fname) return _common.pio(rcount, wcount, rbytes, wbytes) - finally: - f.close() else: def io_counters(self): raise NotImplementedError("couldn't find /proc/%s/io (kernel " @@ -820,14 +755,11 @@ class Process(object): @wrap_exceptions def cpu_times(self): - f = open("/proc/%s/stat" % self.pid, 'rb') - try: + with open("/proc/%s/stat" % self.pid, 'rb') as f: st = f.read().strip() - finally: - f.close() # ignore the first two values ("pid (exe)") - st = st[st.find(b(')')) + 2:] - values = st.split(b(' ')) + st = st[st.find(b')') + 2:] + values = st.split(b' ') utime = float(values[11]) / CLOCK_TICKS stime = float(values[12]) / CLOCK_TICKS return _common.pcputimes(utime, stime) @@ -844,14 +776,11 @@ class Process(object): @wrap_exceptions def create_time(self): - f = open("/proc/%s/stat" % self.pid, 'rb') - try: + with open("/proc/%s/stat" % self.pid, 'rb') as f: st = f.read().strip() - finally: - f.close() # ignore the first two values ("pid (exe)") - st = st[st.rfind(b(')')) + 2:] - values = st.split(b(' ')) + st = st[st.rfind(b')') + 2:] + values = st.split(b' ') # According to documentation, starttime is in field 21 and the # unit is jiffies (clock ticks). # We first divide it for clock ticks and then add uptime returning @@ -862,13 +791,10 @@ class Process(object): @wrap_exceptions def memory_info(self): - f = open("/proc/%s/statm" % self.pid, 'rb') - try: + with open("/proc/%s/statm" % self.pid, 'rb') as f: vms, rss = f.readline().split()[:2] return _common.pmem(int(rss) * PAGESIZE, int(vms) * PAGESIZE) - finally: - f.close() @wrap_exceptions def memory_info_ex(self): @@ -883,12 +809,9 @@ class Process(object): # | data | data + stack | drs | DATA | # | dirty | dirty pages (unused in Linux 2.6) | dt | | # ============================================================ - f = open("/proc/%s/statm" % self.pid, "rb") - try: + with open("/proc/%s/statm" % self.pid, "rb") as f: vms, rss, shared, text, lib, data, dirty = \ [int(x) * PAGESIZE for x in f.readline().split()[:7]] - finally: - f.close() return pextmem(rss, vms, shared, text, lib, data, dirty) if os.path.exists('/proc/%s/smaps' % os.getpid()): @@ -947,13 +870,12 @@ class Process(object): data.get('Anonymous:', 0), data.get('Swap:', 0)) f.close() - except EnvironmentError: + except EnvironmentError as err: # XXX - Can't use wrap_exceptions decorator as we're # returning a generator; this probably needs some # refactoring in order to avoid this code duplication. if f is not None: f.close() - err = sys.exc_info()[1] if err.errno in (errno.ENOENT, errno.ESRCH): raise NoSuchProcess(self.pid, self._name) if err.errno in (errno.EPERM, errno.EACCES): @@ -983,14 +905,11 @@ class Process(object): @wrap_exceptions def num_ctx_switches(self): vol = unvol = None - f = open("/proc/%s/status" % self.pid, "rb") - VOLUNTARY = b("voluntary_ctxt_switches") - NON_VOLUNTARY = b("nonvoluntary_ctxt_switches") - try: + with open("/proc/%s/status" % self.pid, "rb") as f: for line in f: - if line.startswith(VOLUNTARY): + if line.startswith(b"voluntary_ctxt_switches"): vol = int(line.split()[1]) - elif line.startswith(NON_VOLUNTARY): + elif line.startswith(b"nonvoluntary_ctxt_switches"): unvol = int(line.split()[1]) if vol is not None and unvol is not None: return _common.pctxsw(vol, unvol) @@ -998,20 +917,14 @@ class Process(object): "'voluntary_ctxt_switches' and 'nonvoluntary_ctxt_switches'" "fields were not found in /proc/%s/status; the kernel is " "probably older than 2.6.23" % self.pid) - finally: - f.close() @wrap_exceptions def num_threads(self): - f = open("/proc/%s/status" % self.pid, "rb") - try: - THREADS = b("Threads:") + with open("/proc/%s/status" % self.pid, "rb") as f: for line in f: - if line.startswith(THREADS): + if line.startswith(b"Threads:"): return int(line.split()[1]) raise NotImplementedError("line not found") - finally: - f.close() @wrap_exceptions def threads(self): @@ -1022,21 +935,18 @@ class Process(object): for thread_id in thread_ids: try: f = open("/proc/%s/task/%s/stat" % (self.pid, thread_id), 'rb') - except EnvironmentError: - err = sys.exc_info()[1] + except EnvironmentError as err: if err.errno == errno.ENOENT: # no such file or directory; it means thread # disappeared on us hit_enoent = True continue raise - try: + with f: st = f.read().strip() - finally: - f.close() # ignore the first two values ("pid (exe)") - st = st[st.find(b(')')) + 2:] - values = st.split(b(' ')) + st = st[st.find(b')') + 2:] + values = st.split(b' ') utime = float(values[11]) / CLOCK_TICKS stime = float(values[12]) / CLOCK_TICKS ntuple = _common.pthread(int(thread_id), utime, stime) @@ -1048,12 +958,9 @@ class Process(object): @wrap_exceptions def nice_get(self): - # f = open('/proc/%s/stat' % self.pid, 'r') - # try: + # with open('/proc/%s/stat' % self.pid, 'r') as f: # data = f.read() # return int(data.split()[18]) - # finally: - # f.close() # Use C implementation return _psutil_posix.getpriority(self.pid) @@ -1070,8 +977,7 @@ class Process(object): def cpu_affinity_set(self, cpus): try: cext.proc_cpu_affinity_set(self.pid, cpus) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.EINVAL: allcpus = tuple(range(len(per_cpu_times()))) for cpu in cpus: @@ -1131,19 +1037,15 @@ class Process(object): @wrap_exceptions def status(self): - f = open("/proc/%s/status" % self.pid, 'rb') - try: - STATE = b("State:") + with open("/proc/%s/status" % self.pid, 'rb') as f: for line in f: - if line.startswith(STATE): + if line.startswith(b"State:"): letter = line.split()[1] if PY3: letter = letter.decode() # XXX is '?' legit? (we're not supposed to return # it anyway) return PROC_STATUSES.get(letter, '?') - finally: - f.close() @wrap_exceptions def open_files(self): @@ -1155,9 +1057,8 @@ class Process(object): if os.path.islink(file): try: file = os.readlink(file) - except OSError: + except OSError as err: # ENOENT == file which is gone in the meantime - err = sys.exc_info()[1] if err.errno in (errno.ENOENT, errno.ESRCH): hit_enoent = True continue @@ -1188,39 +1089,27 @@ class Process(object): @wrap_exceptions def ppid(self): - f = open("/proc/%s/status" % self.pid, 'rb') - try: - PPID = b("PPid:") + with open("/proc/%s/status" % self.pid, 'rb') as f: for line in f: - if line.startswith(PPID): + if line.startswith(b"PPid:"): # PPid: nnnn return int(line.split()[1]) raise NotImplementedError("line not found") - finally: - f.close() @wrap_exceptions def uids(self): - f = open("/proc/%s/status" % self.pid, 'rb') - try: - UID = b('Uid:') + with open("/proc/%s/status" % self.pid, 'rb') as f: for line in f: - if line.startswith(UID): + if line.startswith(b'Uid:'): _, real, effective, saved, fs = line.split() return _common.puids(int(real), int(effective), int(saved)) raise NotImplementedError("line not found") - finally: - f.close() @wrap_exceptions def gids(self): - f = open("/proc/%s/status" % self.pid, 'rb') - try: - GID = b('Gid:') + with open("/proc/%s/status" % self.pid, 'rb') as f: for line in f: - if line.startswith(GID): + if line.startswith(b'Gid:'): _, real, effective, saved, fs = line.split() return _common.pgids(int(real), int(effective), int(saved)) raise NotImplementedError("line not found") - finally: - f.close() diff --git a/psutil/_psosx.py b/psutil/_psosx.py index 8953867d..c40ef1d7 100644 --- a/psutil/_psosx.py +++ b/psutil/_psosx.py @@ -7,13 +7,13 @@ """OSX platform implementation.""" import errno +import functools import os -import sys +from collections import namedtuple from psutil import _common from psutil import _psposix from psutil._common import conn_tmap, usage_percent, isfile_strict -from psutil._compat import namedtuple, wraps import _psutil_osx as cext import _psutil_posix @@ -176,15 +176,14 @@ def wrap_exceptions(fun): """Decorator which translates bare OSError exceptions into NoSuchProcess and AccessDenied. """ - @wraps(fun) + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError: + except OSError as err: # support for private module import if NoSuchProcess is None or AccessDenied is None: raise - err = sys.exc_info()[1] if err.errno == errno.ESRCH: raise NoSuchProcess(self.pid, self._name) if err.errno in (errno.EPERM, errno.EACCES): diff --git a/psutil/_psposix.py b/psutil/_psposix.py index 81076043..280cdeeb 100644 --- a/psutil/_psposix.py +++ b/psutil/_psposix.py @@ -31,8 +31,7 @@ def pid_exists(pid): return True try: os.kill(pid, 0) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ESRCH: # ESRCH == No such process return False @@ -78,8 +77,7 @@ def wait_pid(pid, timeout=None): while 1: try: retpid, status = waitcall() - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.EINTR: delay = check_timeout(delay) continue @@ -150,8 +148,7 @@ def _get_terminal_map(): assert name not in ret try: ret[os.stat(name).st_rdev] = name - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno != errno.ENOENT: raise return ret diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index 2fc1ec45..7b5385ef 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -11,11 +11,12 @@ import os import socket import subprocess import sys +from collections import namedtuple from psutil import _common from psutil import _psposix from psutil._common import usage_percent, isfile_strict -from psutil._compat import namedtuple, PY3 +from psutil._compat import PY3 import _psutil_posix import _psutil_sunos as cext @@ -232,14 +233,13 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except EnvironmentError: + except EnvironmentError as err: # support for private module import if NoSuchProcess is None or AccessDenied is None: raise # ENOENT (no such file or directory) gets raised on open(). # ESRCH (no such process) can get raised on read() if # process is gone in meantime. - err = sys.exc_info()[1] if err.errno in (errno.ENOENT, errno.ESRCH): raise NoSuchProcess(self.pid, self._name) if err.errno in (errno.EPERM, errno.EACCES): @@ -293,8 +293,7 @@ class Process(object): # fine. try: return _psutil_posix.getpriority(self.pid) - except EnvironmentError: - err = sys.exc_info()[1] + except EnvironmentError as err: if err.errno in (errno.ENOENT, errno.ESRCH): if pid_exists(self.pid): raise AccessDenied(self.pid, self._name) @@ -338,8 +337,7 @@ class Process(object): for x in (0, 1, 2, 255): try: return os.readlink('/proc/%d/path/%d' % (self.pid, x)) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ENOENT: hit_enoent = True continue @@ -356,8 +354,7 @@ class Process(object): # Reference: http://goo.gl/55XgO try: return os.readlink("/proc/%s/path/cwd" % self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ENOENT: os.stat("/proc/%s" % self.pid) return None @@ -388,9 +385,8 @@ class Process(object): try: utime, stime = cext.query_process_thread( self.pid, tid) - except EnvironmentError: + except EnvironmentError as err: # ENOENT == thread gone in meantime - err = sys.exc_info()[1] if err.errno == errno.ENOENT: hit_enoent = True continue @@ -413,9 +409,8 @@ class Process(object): if os.path.islink(path): try: file = os.readlink(path) - except OSError: + except OSError as err: # ENOENT == file which is gone in the meantime - err = sys.exc_info()[1] if err.errno == errno.ENOENT: hit_enoent = True continue @@ -495,8 +490,7 @@ class Process(object): if not name.startswith('['): try: name = os.readlink('/proc/%s/path/%s' % (self.pid, name)) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ENOENT: # sometimes the link may not be resolved by # readlink() even if it exists (ls shows it). diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c index 4602178e..164b9973 100644 --- a/psutil/_psutil_linux.c +++ b/psutil/_psutil_linux.c @@ -373,10 +373,8 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) } if (!PySequence_Check(py_cpu_set)) { - // does not work on Python 2.4 - // PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s", - // Py_TYPE(py_cpu_set)->tp_name); - PyErr_Format(PyExc_TypeError, "sequence argument expected"); + PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s", + Py_TYPE(py_cpu_set)->tp_name); goto error; } diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index b540bdac..6068446b 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -7,12 +7,13 @@ """Windows platform implementation.""" import errno +import functools import os -import sys +from collections import namedtuple from psutil import _common from psutil._common import conn_tmap, usage_percent, isfile_strict -from psutil._compat import PY3, xrange, wraps, lru_cache, namedtuple +from psutil._compat import PY3, xrange, lru_cache import _psutil_windows as cext # process priority constants, import from __init__.py: @@ -200,15 +201,14 @@ def wrap_exceptions(fun): """Decorator which translates bare OSError and WindowsError exceptions into NoSuchProcess and AccessDenied. """ - @wraps(fun) + @functools.wraps(fun) def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError: + except OSError as err: # support for private module import if NoSuchProcess is None or AccessDenied is None: raise - err = sys.exc_info()[1] if err.errno in ACCESS_DENIED_SET: raise AccessDenied(self.pid, self._name) if err.errno == errno.ESRCH: @@ -265,8 +265,7 @@ class Process(object): def _get_raw_meminfo(self): try: return cext.proc_memory_info(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: return cext.proc_memory_info_2(self.pid) raise @@ -287,10 +286,9 @@ class Process(object): def memory_maps(self): try: raw = cext.proc_memory_maps(self.pid) - except OSError: + except OSError as err: # XXX - can't use wrap_exceptions decorator as we're # returning a generator; probably needs refactoring. - err = sys.exc_info()[1] if err.errno in ACCESS_DENIED_SET: raise AccessDenied(self.pid, self._name) if err.errno == errno.ESRCH: @@ -334,8 +332,7 @@ class Process(object): return boot_time() try: return cext.proc_create_time(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: return cext.proc_create_time_2(self.pid) raise @@ -357,8 +354,7 @@ class Process(object): def cpu_times(self): try: ret = cext.proc_cpu_times(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: ret = cext.proc_cpu_times_2(self.pid) else: @@ -431,8 +427,7 @@ class Process(object): def io_counters(self): try: ret = cext.proc_io_counters(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: ret = cext.proc_io_counters_2(self.pid) else: @@ -478,8 +473,7 @@ class Process(object): def num_handles(self): try: return cext.proc_num_handles(self.pid) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: return cext.proc_num_handles_2(self.pid) raise @@ -22,8 +22,7 @@ HERE = os.path.abspath(os.path.dirname(__file__)) def get_version(): INIT = os.path.join(HERE, 'psutil/__init__.py') - f = open(INIT, 'r') - try: + with open(INIT, 'r') as f: for line in f: if line.startswith('__version__'): ret = eval(line.strip().split(' = ')[1]) @@ -33,17 +32,12 @@ def get_version(): return ret else: raise ValueError("couldn't find version string") - finally: - f.close() def get_description(): README = os.path.join(HERE, 'README.rst') - f = open(README, 'r') - try: + with open(README, 'r') as f: return f.read() - finally: - f.close() # POSIX @@ -166,8 +160,6 @@ def main(): 'Operating System :: POSIX', 'Programming Language :: C', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.4', - 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', diff --git a/test/_windows.py b/test/_windows.py index e1b27cc0..5f61bd20 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -37,8 +37,7 @@ def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno in ACCESS_DENIED_SET: raise psutil.AccessDenied(None, None) if err.errno == errno.ESRCH: @@ -215,8 +214,7 @@ class WindowsSpecificTestCase(unittest.TestCase): break try: usage = psutil.disk_usage(ps_part.mountpoint) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno == errno.ENOENT: # usually this is the floppy break diff --git a/test/test_memory_leaks.py b/test/test_memory_leaks.py index c33d37f0..a8f6fc68 100644 --- a/test/test_memory_leaks.py +++ b/test/test_memory_leaks.py @@ -227,11 +227,8 @@ class TestProcessObjectLeaks(Base): @skip_if_linux() def test_open_files(self): safe_remove(TESTFN) # needed after UNIX socket test has run - f = open(TESTFN, 'w') - try: + with open(TESTFN, 'w'): self.execute('open_files') - finally: - f.close() # OSX implementation is unbelievably slow @unittest.skipIf(OSX, "OSX implementation is too slow") diff --git a/test/test_psutil.py b/test/test_psutil.py index 40bfac4b..3cf70ce9 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -16,8 +16,10 @@ https://pypi.python.org/pypi/unittest2 from __future__ import division import atexit +import collections import datetime import errno +import functools import os import pickle import re @@ -51,7 +53,7 @@ else: import unittest import psutil -from psutil._compat import PY3, callable, long, wraps, unicode +from psutil._compat import PY3, callable, long, unicode # =================================================================== @@ -161,16 +163,16 @@ def pyrun(src): # >= 2.6 only fd, path = tempfile.mkstemp(prefix=TESTFILE_PREFIX) _testfiles.append(path) - f = open(path, 'wb') - try: - f.write(src) - f.flush() - subp = get_test_subprocess([PYTHON, f.name], stdout=None, stderr=None) - wait_for_pid(subp.pid) - return subp - finally: - os.close(fd) - f.close() + with open(path, 'wb') as f: + try: + f.write(src) + f.flush() + subp = get_test_subprocess([PYTHON, f.name], stdout=None, + stderr=None) + wait_for_pid(subp.pid) + return subp + finally: + os.close(fd) def warn(msg): @@ -323,8 +325,7 @@ def check_connection(conn): s = socket.socket(conn.family, conn.type) try: s.bind((conn.laddr[0], 0)) - except socket.error: - err = sys.exc_info()[1] + except socket.error as err: if err.errno != errno.EADDRNOTAVAIL: raise s.close() @@ -339,15 +340,12 @@ def check_connection(conn): try: try: dupsock = socket.fromfd(conn.fd, conn.family, conn.type) - except (socket.error, OSError): - err = sys.exc_info()[1] + except (socket.error, OSError) as err: if err.args[0] != errno.EBADF: raise else: - # python >= 2.5 - if hasattr(dupsock, "family"): - assert dupsock.family == conn.family - assert dupsock.type == conn.type + assert dupsock.family == conn.family + assert dupsock.type == conn.type finally: if dupsock is not None: dupsock.close() @@ -357,8 +355,7 @@ def safe_remove(file): "Convenience function for removing temporary test files" try: os.remove(file) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno != errno.ENOENT: # file is being used by another process if WINDOWS and isinstance(err, WindowsError) and err.errno == 13: @@ -370,8 +367,7 @@ def safe_rmdir(dir): "Convenience function for removing temporary test directories" try: os.rmdir(dir) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.errno != errno.ENOENT: raise @@ -394,7 +390,7 @@ def retry_before_failing(ntimes=None): actually failing. """ def decorator(fun): - @wraps(fun) + @functools.wraps(fun) def wrapper(*args, **kwargs): for x in range(ntimes or NO_RETRIES): try: @@ -409,7 +405,7 @@ def retry_before_failing(ntimes=None): def skip_on_access_denied(only_if=None): """Decorator to Ignore AccessDenied exceptions.""" def decorator(fun): - @wraps(fun) + @functools.wraps(fun) def wrapper(*args, **kwargs): try: return fun(*args, **kwargs) @@ -428,7 +424,7 @@ def skip_on_access_denied(only_if=None): def skip_on_not_implemented(only_if=None): """Decorator to Ignore NotImplementedError exceptions.""" def decorator(fun): - @wraps(fun) + @functools.wraps(fun) def wrapper(*args, **kwargs): try: return fun(*args, **kwargs) @@ -513,12 +509,6 @@ class ThreadTask(threading.Thread): self.join() -# python 2.4 -if not hasattr(subprocess.Popen, 'terminate'): - subprocess.Popen.terminate = \ - lambda self: psutil.Process(self.pid).terminate() - - # =================================================================== # --- System-related API tests # =================================================================== @@ -921,8 +911,7 @@ class TestSystemAPIs(unittest.TestCase): fname = tempfile.mktemp() try: psutil.disk_usage(fname) - except OSError: - err = sys.exc_info()[1] + except OSError as err: if err.args[0] != errno.ENOENT: raise else: @@ -976,10 +965,9 @@ class TestSystemAPIs(unittest.TestCase): if not WINDOWS: try: os.stat(disk.mountpoint) - except OSError: + except OSError as err: # http://mail.python.org/pipermail/python-dev/ # 2012-June/120787.html - err = sys.exc_info()[1] if err.errno not in (errno.EPERM, errno.EACCES): raise else: @@ -1296,9 +1284,8 @@ class TestProcess(unittest.TestCase): p = psutil.Process() # test reads io1 = p.io_counters() - f = open(PYTHON, 'rb') - f.read() - f.close() + with open(PYTHON, 'rb') as f: + f.read() io2 = p.io_counters() if not BSD: assert io2.read_count > io1.read_count, (io1, io2) @@ -1662,11 +1649,10 @@ class TestProcess(unittest.TestCase): p = psutil.Process() files = p.open_files() self.assertFalse(TESTFN in files) - f = open(TESTFN, 'w') - call_until(p.open_files, "len(ret) != %i" % len(files)) - filenames = [x.path for x in p.open_files()] - self.assertIn(TESTFN, filenames) - f.close() + with open(TESTFN, 'w'): + call_until(p.open_files, "len(ret) != %i" % len(files)) + filenames = [x.path for x in p.open_files()] + self.assertIn(TESTFN, filenames) for file in filenames: assert os.path.isfile(file), file @@ -1687,25 +1673,24 @@ class TestProcess(unittest.TestCase): def test_open_files2(self): # test fd and path fields - fileobj = open(TESTFN, 'w') - p = psutil.Process() - for path, fd in p.open_files(): - if path == fileobj.name or fd == fileobj.fileno(): - break - else: - self.fail("no file found; files=%s" % repr(p.open_files())) - self.assertEqual(path, fileobj.name) - if WINDOWS: - self.assertEqual(fd, -1) - else: - self.assertEqual(fd, fileobj.fileno()) - # test positions - ntuple = p.open_files()[0] - self.assertEqual(ntuple[0], ntuple.path) - self.assertEqual(ntuple[1], ntuple.fd) - # test file is gone - fileobj.close() - self.assertTrue(fileobj.name not in p.open_files()) + with open(TESTFN, 'w') as fileobj: + p = psutil.Process() + for path, fd in p.open_files(): + if path == fileobj.name or fd == fileobj.fileno(): + break + else: + self.fail("no file found; files=%s" % repr(p.open_files())) + self.assertEqual(path, fileobj.name) + if WINDOWS: + self.assertEqual(fd, -1) + else: + self.assertEqual(fd, fileobj.fileno()) + # test positions + ntuple = p.open_files()[0] + self.assertEqual(ntuple[0], ntuple.path) + self.assertEqual(ntuple[1], ntuple.fd) + # test file is gone + self.assertTrue(fileobj.name not in p.open_files()) def compare_proc_sys_cons(self, pid, proc_cons): from psutil._common import pconn @@ -1973,8 +1958,7 @@ class TestProcess(unittest.TestCase): def test_children_duplicates(self): # find the process which has the highest number of children - from psutil._compat import defaultdict - table = defaultdict(int) + table = collections.defaultdict(int) for p in psutil.process_iter(): try: table[p.ppid()] += 1 @@ -2241,8 +2225,7 @@ class TestFetchAllProcesses(unittest.TestCase): msg = "%r was skipped because not implemented" % ( self.__class__.__name__ + '.test_' + name) warn(msg) - except (psutil.NoSuchProcess, psutil.AccessDenied): - err = sys.exc_info()[1] + except (psutil.NoSuchProcess, psutil.AccessDenied) as err: self.assertEqual(err.pid, p.pid) if err.name: # make sure exception's name attr is set @@ -2255,8 +2238,7 @@ class TestFetchAllProcesses(unittest.TestCase): assert ret, ret meth = getattr(self, name) meth(ret) - except Exception: - err = sys.exc_info()[1] + except Exception as err: s = '\n' + '=' * 70 + '\n' s += "FAIL: test_%s (proc=%s" % (name, p) if ret != default: @@ -2394,8 +2376,7 @@ class TestFetchAllProcesses(unittest.TestCase): assert os.path.isabs(ret), ret try: st = os.stat(ret) - except OSError: - err = sys.exc_info()[1] + except OSError as err: # directory has been removed in mean time if err.errno != errno.ENOENT: raise @@ -2460,58 +2441,53 @@ class TestFetchAllProcesses(unittest.TestCase): # --- Limited user tests # =================================================================== -if hasattr(os, 'getuid') and os.getuid() == 0: - - class LimitedUserTestCase(TestProcess): - """Repeat the previous tests by using a limited user. - Executed only on UNIX and only if the user who run the test script - is root. - """ - # the uid/gid the test suite runs under - PROCESS_UID = os.getuid() - PROCESS_GID = os.getgid() - - def __init__(self, *args, **kwargs): - TestProcess.__init__(self, *args, **kwargs) - # re-define all existent test methods in order to - # ignore AccessDenied exceptions - for attr in [x for x in dir(self) if x.startswith('test')]: - meth = getattr(self, attr) - - def test_(self): - try: - meth() - except psutil.AccessDenied: - pass - setattr(self, attr, types.MethodType(test_, self)) - - def setUp(self): - safe_remove(TESTFN) - os.setegid(1000) - os.seteuid(1000) - TestProcess.setUp(self) +@unittest.skipUnless(hasattr(os, 'getuid') and os.getuid() == 0, + "super user privileges are required") +class LimitedUserTestCase(TestProcess): + """Repeat the previous tests by using a limited user. + Executed only on UNIX and only if the user who run the test script + is root. + """ + # the uid/gid the test suite runs under + PROCESS_UID = os.getuid() + PROCESS_GID = os.getgid() + + def __init__(self, *args, **kwargs): + TestProcess.__init__(self, *args, **kwargs) + # re-define all existent test methods in order to + # ignore AccessDenied exceptions + for attr in [x for x in dir(self) if x.startswith('test')]: + meth = getattr(self, attr) + + def test_(self): + try: + meth() + except psutil.AccessDenied: + pass + setattr(self, attr, types.MethodType(test_, self)) - def tearDown(self): - os.setegid(self.PROCESS_UID) - os.seteuid(self.PROCESS_GID) - TestProcess.tearDown(self) + def setUp(self): + safe_remove(TESTFN) + os.setegid(1000) + os.seteuid(1000) + TestProcess.setUp(self) - def test_nice(self): - try: - psutil.Process().nice(-1) - except psutil.AccessDenied: - pass - else: - self.fail("exception not raised") + def tearDown(self): + os.setegid(self.PROCESS_UID) + os.seteuid(self.PROCESS_GID) + TestProcess.tearDown(self) - def test_zombie_process(self): - # causes problems if test test suite is run as root + def test_nice(self): + try: + psutil.Process().nice(-1) + except psutil.AccessDenied: pass -else: + else: + self.fail("exception not raised") - class LimitedUserTestCase(unittest.TestCase): - def test_it(self): - unittest.skip("super user privileges are required") + def test_zombie_process(self): + # causes problems if test test suite is run as root + pass # =================================================================== @@ -2550,7 +2526,7 @@ class TestMisc(unittest.TestCase): def test__all__(self): for name in dir(psutil): - if name in ('callable', 'defaultdict', 'error', 'namedtuple', + if name in ('callable', 'error', 'namedtuple', 'long', 'test', 'NUM_CPUS', 'BOOT_TIME', 'TOTAL_PHYMEM'): continue @@ -2639,8 +2615,7 @@ class TestExampleScripts(unittest.TestCase): exe = exe + ' ' + args try: out = sh(sys.executable + ' ' + exe).strip() - except RuntimeError: - err = sys.exc_info()[1] + except RuntimeError as err: if 'AccessDenied' in str(err): return str(err) else: @@ -2650,11 +2625,8 @@ class TestExampleScripts(unittest.TestCase): def assert_syntax(self, exe, args=None): exe = os.path.join(EXAMPLES_DIR, exe) - f = open(exe, 'r') - try: + with open(exe, 'r') as f: src = f.read() - finally: - f.close() ast.parse(src) def test_check_presence(self): |