Port to Python 3
authorPaul Wise <pabs3@bonedaddy.net>
Sun, 2 Sep 2012 01:43:13 +0000 (2 09:43 +0800)
committerGuillaume Chazarain <guichaz@gmail.com>
Mon, 3 Sep 2012 20:13:19 +0000 (3 22:13 +0200)
Not entirely sure about all parts of this but it works in Python 2/3

iotop/data.py
iotop/genetlink.py
iotop/ioprio.py
iotop/netlink.py
iotop/ui.py

index b51a083..d817492 100644 (file)
@@ -49,23 +49,23 @@ else:
     vm_event_counters = True
 
 if not ioaccounting or not has_ctypes or not vm_event_counters:
-    print 'Could not run iotop as some of the requirements are not met:'
+    print('Could not run iotop as some of the requirements are not met:')
     if not ioaccounting or not vm_event_counters:
-        print '- Linux >= 2.6.20 with'
+        print('- Linux >= 2.6.20 with')
         if not ioaccounting:
-            print '  - I/O accounting support ' \
+            print('  - I/O accounting support ' \
               '(CONFIG_TASKSTATS, CONFIG_TASK_DELAY_ACCT, ' \
-              'CONFIG_TASK_IO_ACCOUNTING)'
+              'CONFIG_TASK_IO_ACCOUNTING)')
         if not vm_event_counters:
-            print '  - VM event counters (CONFIG_VM_EVENT_COUNTERS)'
+            print('  - VM event counters (CONFIG_VM_EVENT_COUNTERS)')
     if not has_ctypes:
-        print '- Python >= 2.5 or Python 2.4 with the ctypes module'
+        print('- Python >= 2.5 or Python 2.4 with the ctypes module')
 
     sys.exit(1)
 
 from iotop import ioprio, vmstat
-from netlink import Connection, NETLINK_GENERIC, U32Attr, NLM_F_REQUEST
-from genetlink import Controller, GeNlMessage
+from iotop.netlink import Connection, NETLINK_GENERIC, U32Attr, NLM_F_REQUEST
+from iotop.genetlink import Controller, GeNlMessage
 
 class DumpableObject(object):
     """Base class for all objects that allows easy introspection when printed"""
@@ -154,12 +154,12 @@ class TaskStatsNetlink(object):
         thread.task_stats_request.send(self.connection)
         try:
             reply = GeNlMessage.recv(self.connection)
-        except OSError, e:
+        except OSError as e:
             if e.errno == errno.ESRCH:
                 # OSError: Netlink error: No such process (3)
                 return
             raise
-        for attr_type, attr_value in reply.attrs.iteritems():
+        for attr_type, attr_value in reply.attrs.items():
             if attr_type == TASKSTATS_TYPE_AGGR_PID:
                 reply = attr_value.nested()
                 break
@@ -214,6 +214,8 @@ def safe_utf8_decode(s):
         return s.decode('utf-8')
     except UnicodeDecodeError:
         return s.encode('string_escape')
+    except AttributeError:
+        return s
 
 class ThreadInfo(DumpableObject):
     """Stats for a single thread"""
@@ -285,7 +287,7 @@ class ProcessInfo(DumpableObject):
         if uid is not None and not self.user:
             try:
                 self.user = safe_utf8_decode(pwd.getpwuid(uid).pw_name)
-            except KeyError:
+            except (KeyError, AttributeError):
                 self.user = str(uid)
         return self.user or '{none}'
 
@@ -329,7 +331,7 @@ class ProcessInfo(DumpableObject):
         return False
 
     def get_ioprio(self):
-        priorities = set(t.get_ioprio() for t in self.threads.itervalues())
+        priorities = set(t.get_ioprio() for t in self.threads.values())
         if len(priorities) == 1:
             return priorities.pop()
         return '?dif'
@@ -351,10 +353,9 @@ class ProcessInfo(DumpableObject):
     def update_stats(self):
         stats_delta = Stats.build_all_zero()
         for tid, thread in self.threads.items():
-            if thread.mark:
-                del self.threads[tid]
-            else:
+            if not thread.mark:
                 stats_delta.accumulate(thread.stats_delta, stats_delta)
+        self.threads = dict([(tid, thread) for tid, thread in self.threads.items() if not thread.mark])
 
         nr_threads = len(self.threads)
         if not nr_threads:
@@ -442,15 +443,13 @@ class ProcessList(DumpableObject):
         return self.vmstat.delta()
 
     def refresh_processes(self):
-        for process in self.processes.itervalues():
-            for thread in process.threads.itervalues():
+        for process in self.processes.values():
+            for thread in process.threads.values():
                 thread.mark = True
 
         total_read_and_write = self.update_process_counts()
 
-        for pid, process in self.processes.items():
-            if not process.update_stats():
-                del self.processes[pid]
+        self.processes = dict([(pid,process) for pid, process in self.processes.items() if process.update_stats()])
 
         return total_read_and_write
 
index f9abf15..66f3d02 100644 (file)
@@ -7,8 +7,8 @@ GPLv2+; See copying for details.
 '''
 
 import struct
-from netlink import NLM_F_REQUEST, NLMSG_MIN_TYPE, Message, parse_attributes
-from netlink import NulStrAttr, Connection, NETLINK_GENERIC
+from iotop.netlink import NLM_F_REQUEST, NLMSG_MIN_TYPE, Message, parse_attributes
+from iotop.netlink import NulStrAttr, Connection, NETLINK_GENERIC
 
 CTRL_CMD_UNSPEC       = 0
 CTRL_CMD_NEWFAMILY    = 1
index 86a729f..afe6054 100644 (file)
@@ -175,6 +175,6 @@ if __name__ == '__main__':
         pid = int(sys.argv[1])
     else:
         pid = os.getpid()
-    print 'pid:', pid
-    print 'ioprio:', get(pid)
+    print('pid:', pid)
+    print('ioprio:', get(pid))
 
index 326045a..39f3b92 100644 (file)
@@ -102,7 +102,7 @@ class Attr:
         hdr = struct.pack("HH", len(self.data)+4, self.type)
         length = len(self.data)
         pad = ((length + 4 - 1) & ~3 ) - length
-        return hdr + self.data + '\0' * pad
+        return hdr + self.data + b'\0' * pad
 
     def __repr__(self):
         return '<Attr type %d, data "%s">' % (self.type, repr(self.data))
@@ -124,11 +124,12 @@ class Attr:
 
 class StrAttr(Attr):
     def __init__(self, attr_type, data):
-        Attr.__init__(self, attr_type, "%ds" % len(data), data)
+        Attr.__init__(self, attr_type, "%ds" % len(data), data.encode('utf-8'))
 
 class NulStrAttr(Attr):
     def __init__(self, attr_type, data):
-        Attr.__init__(self, attr_type, "%dsB" % len(data), data, 0)
+        print(data)
+        Attr.__init__(self, attr_type, "%dsB" % len(data), data.encode('utf-8'), 0)
 
 class U32Attr(Attr):
     def __init__(self, attr_type, val):
@@ -181,7 +182,7 @@ class Message:
             contents = []
             for attr in payload:
                 contents.append(attr._dump())
-            self.payload = ''.join(contents)
+            self.payload = b''.join(contents)
         else:
             self.payload = payload
 
index bfec6d8..187ba18 100644 (file)
@@ -16,6 +16,9 @@
 #
 # Copyright (c) 2007 Guillaume Chazarain <guichaz@gmail.com>
 
+# Allow printing with same syntax in Python 2/3
+from __future__ import print_function 
+
 import curses
 import errno
 import locale
@@ -30,8 +33,8 @@ import time
 from iotop.data import find_uids, TaskStatsNetlink, ProcessList, Stats
 from iotop.data import ThreadInfo
 from iotop.version import VERSION
-import ioprio
-from ioprio import IoprioSetError
+import iotop.ioprio
+from iotop.ioprio import IoprioSetError
 
 #
 # Utility functions for the UI
@@ -82,9 +85,9 @@ def format_stats(options, process, duration):
 
 def get_max_pid_width():
     try:
-        return len(file('/proc/sys/kernel/pid_max').read().strip())
-    except Exception, e:
-        print e
+        return len(open('/proc/sys/kernel/pid_max').read().strip())
+    except Exception as e:
+        print(e)
         # Reasonable default in case something fails
         return 5
 
@@ -160,7 +163,7 @@ class IOTopUI(object):
 
             try:
                 events = poll.poll(self.options.delay_seconds * 1000.0)
-            except select.error, e:
+            except select.error as e:
                 if e.args and e.args[0] == errno.EINTR:
                     events = 0
                 else:
@@ -315,7 +318,7 @@ class IOTopUI(object):
                 exec_unit.set_ioprio(ioprio_class, ioprio_data)
                 self.process_list.clear()
                 self.process_list.refresh_processes()
-            except IoprioSetError, e:
+            except IoprioSetError as e:
                 self.prompt_error('Error setting I/O priority: %s' % e.err)
             except InvalidPid:
                 self.prompt_error('Invalid process id!')
@@ -396,7 +399,7 @@ class IOTopUI(object):
             return not self.options.only or \
                    p.did_some_io(self.options.accumulated)
 
-        processes = filter(should_format, self.process_list.processes.values())
+        processes = list(filter(should_format, self.process_list.processes.values()))
         key = IOTopUI.sorting_keys[self.sorting_key][0]
         if self.options.accumulated:
             stats_lambda = lambda p: p.stats_accum
@@ -406,7 +409,7 @@ class IOTopUI(object):
                        reverse=self.sorting_reverse)
         if not self.options.batch:
             del processes[self.height - 2:]
-        return map(format, processes)
+        return list(map(format, processes))
 
     def refresh_display(self, first_time, total_read, total_write, duration):
         summary = 'Total DISK READ: %s | Total DISK WRITE: %s' % (
@@ -428,18 +431,18 @@ class IOTopUI(object):
             summary = current_time + summary
         if self.options.batch:
             if self.options.quiet <= 2:
-                print summary
+                print(summary)
                 if self.options.quiet <= int(first_time):
-                    print ''.join(titles)
+                    print(''.join(titles))
             for l in lines:
-                print l.encode('utf-8')
+                print(l.encode('utf-8'))
             sys.stdout.flush()
         else:
             self.win.erase()
             self.win.addstr(summary[:self.width])
             self.win.hline(1, 0, ord(' ') | curses.A_REVERSE, self.width)
             remaining_cols = self.width
-            for i in xrange(len(titles)):
+            for i in range(len(titles)):
                 attr = curses.A_REVERSE
                 title = titles[i]
                 if i == self.sorting_key:
@@ -456,9 +459,9 @@ class IOTopUI(object):
                 status_msg = ('CONFIG_TASK_DELAY_ACCT not enabled in kernel, '
                               'cannot determine SWAPIN and IO %')
             num_lines = min(len(lines), self.height - 2 - int(bool(status_msg)))
-            for i in xrange(num_lines):
+            for i in range(num_lines):
                 try:
-                    self.win.addstr(i + 2, 0, lines[i].encode('utf-8'))
+                    self.win.addstr(i + 2, 0, lines[i])
                 except curses.error:
                     pass
             if status_msg:
@@ -479,17 +482,17 @@ def run_iotop(options):
             return run_iotop_window(None, options)
         else:
             return curses.wrapper(run_iotop_window, options)
-    except OSError, e:
+    except OSError as e:
         if e.errno == errno.EPERM:
-            print >> sys.stderr, e
-            print >> sys.stderr, ('''
+            print(e, file=sys.stderr)
+            print('''
 The Linux kernel interfaces that iotop relies on now require root priviliges
 or the NET_ADMIN capability. This change occured because a security issue
 (CVE-2011-2494) was found that allows leakage of sensitive data across user
 boundaries. If you require the ability to run iotop as a non-root user, please
 configure sudo to allow you to run iotop as root.
 
-Please do not file bugs on iotop about this.''')
+Please do not file bugs on iotop about this.''', file=sys.stderr)
             sys.exit(1)
         else:
             raise
@@ -503,14 +506,14 @@ def _profile(continuation):
     try:
         import cProfile
         import pstats
-        print 'Profiling using cProfile'
+        print('Profiling using cProfile')
         cProfile.runctx('continuation()', globals(), locals(), prof_file)
         stats = pstats.Stats(prof_file)
     except ImportError:
         import hotshot
         import hotshot.stats
         prof = hotshot.Profile(prof_file, lineevents=1)
-        print 'Profiling using hotshot'
+        print('Profiling using hotshot')
         prof.runcall(continuation)
         prof.close()
         stats = hotshot.stats.load(prof_file)
@@ -540,7 +543,7 @@ def main():
     try:
         locale.setlocale(locale.LC_ALL, '')
     except locale.Error:
-        print 'unable to set locale, falling back to the default locale'
+        print('unable to set locale, falling back to the default locale')
     parser = optparse.OptionParser(usage=USAGE, version='iotop ' + VERSION)
     parser.add_option('-o', '--only', action='store_true',
                       dest='only', default=False,