Thread: [Pymoul-svn] SF.net SVN: pymoul: [152] pymoul/trunk/src/moul/osdependent/processinfo.py
Status: Alpha
Brought to you by:
tiran
|
From: <ti...@us...> - 2007-02-06 20:35:25
|
Revision: 152
http://pymoul.svn.sourceforge.net/pymoul/?rev=152&view=rev
Author: tiran
Date: 2007-02-06 12:35:26 -0800 (Tue, 06 Feb 2007)
Log Message:
-----------
Fixed the unicode fix for windows process info code
Modified Paths:
--------------
pymoul/trunk/src/moul/osdependent/processinfo.py
Modified: pymoul/trunk/src/moul/osdependent/processinfo.py
===================================================================
--- pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-06 20:31:12 UTC (rev 151)
+++ pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-06 20:35:26 UTC (rev 152)
@@ -203,10 +203,11 @@
name = u"".join([c for c in modname if c != NULL])
except UnicodeError, msg:
LOG.exception("Can't decode name of pid %i" % pid)
+ else:
+ mapping[pid] = name
modname[:] = sizeof(modname) * NULL
KERNEL.CloseHandle(hProcess)
- mapping[pid] = name
-
+
return mapping
def getPidDetails(self, pid):
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ti...@us...> - 2007-02-27 03:02:02
|
Revision: 198
http://pymoul.svn.sourceforge.net/pymoul/?rev=198&view=rev
Author: tiran
Date: 2007-02-26 19:01:53 -0800 (Mon, 26 Feb 2007)
Log Message:
-----------
Added ps parser to processinfo
Modified Paths:
--------------
pymoul/trunk/src/moul/osdependent/processinfo.py
Modified: pymoul/trunk/src/moul/osdependent/processinfo.py
===================================================================
--- pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-26 17:43:35 UTC (rev 197)
+++ pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-27 03:01:53 UTC (rev 198)
@@ -17,6 +17,13 @@
#
"""Get process informations
+The module contains for implementations:
+
+ - an Unsupported implementation that raises UnsupportedError
+ - a Linux implementation that read the data from /proc
+ - a Unix/POSIX implementation that parses the output of ps
+ - a Windows implementation that uses ctypes to get the infos from psapi.dll
+
API
===
getPids() - list of ints/longs
@@ -56,11 +63,9 @@
LOG = getLogger("processinfo")
_plat = sys.platform.startswith
-if _plat('linux'):
- PLAT = 'linux'
- import os.path
-elif _plat('win') or _plat('cygwin'):
- PLAT = 'win'
+if _plat('win') or _plat('cygwin'):
+ LOG.debug("Using ctypes on Windows")
+ IMPL = 'win'
from ctypes import windll, c_ulong, sizeof, c_buffer, byref
PSAPI = windll.psapi
@@ -68,25 +73,147 @@
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010
PROCESS_FLAGS = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
-#elif _plat('darwin'):
-# pass
else:
- raise OSError("OS %s is not supported" % os.name)
+ import os
+ PROC = '/proc'
+ if os.path.isfile("%s/self/status" % PROC):
+ LOG.debug("Using /proc on Linux")
+ IMPL = 'proc'
+ elif os.system('ps') == 0:
+ LOG.debug("Using the 'ps' command on POSIX os")
+ IMPL = 'ps'
+ from subprocess import Popen
+ from subprocess import PIPE
+ else:
+ LOG.warning("Unsupported OS. Neither /proc nor 'ps' works.")
+ IMPL = "unsupported"
NULL = "\x00"
+class UnsupportedError(OSError):
+ pass
+
+class Unsupported(object):
+ """Unsupported OS
+ """
+ __slots__ = ()
+
+ def supported(self):
+ return False
+ supported = property(supported)
+
+ def getPids(self):
+ """Get a list of pids
+ """
+ raise UnsupportedError
+
+ def getPidNames(self):
+ """Get a list of pid -> name
+ """
+ raise UnsupportedError
+
+ def getPidDetails(self, pid):
+ """Get detailed informations about a process
+ """
+ raise UnsupportedError
+
+class PsParser(object):
+ """Parse the output of the ps command
+ """
+ __slots__ = ()
+
+ CMD = "ps -e --no-header --cols=1024"
+ PIDNAMES = "%s --format=pid,ucmd" % CMD
+ PIDS = "%s --format=pid" % CMD
+
+ def supported(self):
+ return False
+ supported = property(supported)
+
+ def getPids(self):
+ """Get a list of pids
+ """
+ stdout = self._exec(self.PIDS)
+ if stdout is None:
+ return None
+ pids = []
+ for line in stdout:
+ try:
+ pid = int(line.strip())
+ except ValueError:
+ pass
+ else:
+ pids.append(pid)
+ return pids
+
+ def getPidNames(self):
+ """Get a list of pid -> name
+ """
+ stdout = self._exec(self.PIDNAMES)
+ if stdout is None:
+ return None
+ mapping = {}
+ for line in stdout:
+ line = line.strip()
+ idx = line.find(' ')
+ pid, name = line[:idx], line[idx+1:]
+ try:
+ pid = int(pid)
+ except ValueError:
+ pass
+ else:
+ mapping[pid] = name
+ return mapping
+
+ def getPidDetails(self, pid):
+ """Get detailed informations about a process
+
+ TODO
+ """
+ raise UnsupportedError
+
+ def _exec(self, cmd):
+ """Execute command cmd
+
+ The method waits until the command has finished. It returns None of
+ something went wrong.
+
+ @param cmd: Command to execute
+ @type cmd: str
+ @return: None or stdin as file like object
+ """
+ try:
+ popen = Popen(cmd, shell=True, bufsize=-1, stdout=PIPE,
+ env = {'LC_ALL' : 'C'})
+ rc = popen.wait()
+ except (OSError, ValueError):
+ LOG.exception("Failed to execute '%s'" % cmd)
+ return None
+ else:
+ if rc != 0:
+ LOG.error("'%s' returned with error code %i" % (cmd, rc))
+ return None
+ else:
+ return popen.stdout
+
class LinuxProcReader(object):
"""Get process informations under Linux by reading /proc
-
+
Tested under Linux, may work on other POSIX os with /proc, too.
"""
+ __slots__ = ()
- def getPids(self):
+ def supported(self):
+ return True
+ supported = property(supported)
+
+ @staticmethod
+ def getPids():
"""Get a list of pids
"""
pids = []
- for name in os.listdir('/proc'):
- if os.path.isdir('/proc/' + name):
+ for name in os.listdir(PROC):
+ if os.path.isdir(PROC + '/' + name):
try:
pids.append(int(name))
except ValueError:
@@ -113,9 +240,9 @@
def _readProcStatus(self, pid, searchkey=None):
"""Read and parse status informations for PID pid
-
+
pid - pid as long or int or 'self'
-
+
If searchkey is None the method returns a mapping of lower keys
to values (stripped).
If searchkey is given than the method immediatly returns the value
@@ -124,7 +251,7 @@
mapping = {}
try:
# read entiry file to avoid race conditions
- lines = open('/proc/%s/status' % pid, 'r').readlines()
+ lines = open('%s/%i/status' % (PROC, pid), 'r').readlines()
except IOError:
return None
for line in lines:
@@ -142,34 +269,40 @@
if searchkey is not None:
return None
return mapping
-
+
def _readProcCmdline(self, pid):
"""Read cmdline informations for pid and returns a list similar to sys.argv
"""
try:
# read entiry file to avoid race conditions
- data = open('/proc/%s/cmdline' % pid, 'r').read()
+ data = open('%s/%i/cmdline' % (PROC, pid), 'r').read()
except IOError:
return None
return data.split(NULL)
def _readProcOther(self, pid):
"""Read other possible useful things
-
+
cwd -> current work directory (may not exist)
exe -> path to executable (may not exist)
"""
return {
- 'cwd' : os.path.realpath('/proc/%s/cwd' % pid),
- 'exe' : os.path.realpath('/proc/%s/exe' % pid),
+ 'cwd' : os.path.realpath('%s/%i/cwd' % (PROC, pid)),
+ 'exe' : os.path.realpath('%s/%i/exe' % (PROC, pid)),
}
class WinEnumProcesses(object):
"""""Get process informations under Win32 with psapi.dll
-
+
Based on enumprocesses from Eric Koome
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/305279
"""
+ __slots__ = ()
+
+ def supported(self):
+ return True
+ supported = property(supported)
+
def getPids(self):
"""Get a list of pids
"""
@@ -214,32 +347,44 @@
"""
if pid == 'self':
pid = os.getpid()
-
+
hModule = c_ulong()
count = c_ulong()
modname = c_buffer(51)
hProcess = KERNEL.OpenProcess(PROCESS_FLAGS, False, pid)
if not hProcess:
return None
-
+
PSAPI.EnumProcessModules(hProcess, byref(hModule),
sizeof(hModule), byref(count))
PSAPI.GetModuleBaseNameA(hProcess, hModule.value, modname,
sizeof(modname))
- name = u"".join([c for c in modname if c != NULL])
+ try:
+ name = u"".join([c for c in modname if c != NULL])
+ except UnicodeError, msg:
+ LOG.exception("Can't decode name of pid %i" % pid)
+ else:
+ name = None
+
KERNEL.CloseHandle(hProcess)
return {'name' : name}
# Initialize global methods
-if PLAT == 'linux':
+if IMPL == 'proc':
_enumProcesses = LinuxProcReader()
-elif PLAT == 'win':
+elif IMPL == 'ps':
+ _enumProcesses = PsParser()
+elif IMPL == 'win':
_enumProcesses = WinEnumProcesses()
+else:
+ LOG.error("System %s is not supported" % sys.platform)
+ _enumProcesses = Unsupported()
getPids = _enumProcesses.getPids
getPidNames = _enumProcesses.getPidNames
getPidDetails = _enumProcesses.getPidDetails
+supportedOS = _enumProcesses.supported
if __name__ == '__main__':
print getPidNames()
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ti...@us...> - 2007-02-27 03:10:10
|
Revision: 199
http://pymoul.svn.sourceforge.net/pymoul/?rev=199&view=rev
Author: tiran
Date: 2007-02-26 19:10:11 -0800 (Mon, 26 Feb 2007)
Log Message:
-----------
Fixed %i -> %s
Modified Paths:
--------------
pymoul/trunk/src/moul/osdependent/processinfo.py
Modified: pymoul/trunk/src/moul/osdependent/processinfo.py
===================================================================
--- pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-27 03:01:53 UTC (rev 198)
+++ pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-27 03:10:11 UTC (rev 199)
@@ -251,7 +251,7 @@
mapping = {}
try:
# read entiry file to avoid race conditions
- lines = open('%s/%i/status' % (PROC, pid), 'r').readlines()
+ lines = open('%s/%s/status' % (PROC, pid), 'r').readlines()
except IOError:
return None
for line in lines:
@@ -275,7 +275,7 @@
"""
try:
# read entiry file to avoid race conditions
- data = open('%s/%i/cmdline' % (PROC, pid), 'r').read()
+ data = open('%s/%s/cmdline' % (PROC, pid), 'r').read()
except IOError:
return None
return data.split(NULL)
@@ -287,8 +287,8 @@
exe -> path to executable (may not exist)
"""
return {
- 'cwd' : os.path.realpath('%s/%i/cwd' % (PROC, pid)),
- 'exe' : os.path.realpath('%s/%i/exe' % (PROC, pid)),
+ 'cwd' : os.path.realpath('%s/%s/cwd' % (PROC, pid)),
+ 'exe' : os.path.realpath('%s/%s/exe' % (PROC, pid)),
}
class WinEnumProcesses(object):
@@ -334,7 +334,7 @@
try:
name = u"".join([c for c in modname if c != NULL])
except UnicodeError, msg:
- LOG.exception("Can't decode name of pid %i" % pid)
+ LOG.exception("Can't decode name of pid %s" % pid)
else:
mapping[pid] = name
modname[:] = sizeof(modname) * NULL
@@ -362,7 +362,7 @@
try:
name = u"".join([c for c in modname if c != NULL])
except UnicodeError, msg:
- LOG.exception("Can't decode name of pid %i" % pid)
+ LOG.exception("Can't decode name of pid %s" % pid)
else:
name = None
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <ti...@us...> - 2007-02-27 11:46:40
|
Revision: 201
http://pymoul.svn.sourceforge.net/pymoul/?rev=201&view=rev
Author: tiran
Date: 2007-02-27 03:46:40 -0800 (Tue, 27 Feb 2007)
Log Message:
-----------
processinfo cleanup
Modified Paths:
--------------
pymoul/trunk/src/moul/osdependent/processinfo.py
Modified: pymoul/trunk/src/moul/osdependent/processinfo.py
===================================================================
--- pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-27 03:20:16 UTC (rev 200)
+++ pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-27 11:46:40 UTC (rev 201)
@@ -51,69 +51,87 @@
True
>>> getPidDetails(cur)['name'] == mapping[cur]
True
+
+>>> for impl in (WinEnumProcesses(), LinuxProcReader(), PsParser()):
+... if impl.supported():
+... isinstance(impl.getPids(), list) and None
+... isinstance(impl.getPidNames(), dict) and None
"""
__author__ = "Christian Heimes"
__version__ = "$Id$"
__revision__ = "$Revision$"
+__all__ = ['getPids', 'getPidNames', 'getPidDetails', 'supportedOS',
+ 'UnsupportedError']
+import logging
import os
+import os.path
import sys
-from logging import getLogger
-LOG = getLogger("processinfo")
+LOG = logging.getLogger("processinfo")
+NULL = "\x00"
+
_plat = sys.platform.startswith
if _plat('win') or _plat('cygwin'):
- LOG.debug("Using ctypes on Windows")
- IMPL = 'win'
from ctypes import windll, c_ulong, sizeof, c_buffer, byref
-
PSAPI = windll.psapi
KERNEL = windll.kernel32
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010
PROCESS_FLAGS = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
else:
- import os
- PROC = '/proc'
- if os.path.isfile("%s/self/status" % PROC):
- LOG.debug("Using /proc on Linux")
- IMPL = 'proc'
- elif os.system('ps') == 0:
- LOG.debug("Using the 'ps' command on POSIX os")
- IMPL = 'ps'
- from subprocess import Popen
- from subprocess import PIPE
- else:
- LOG.warning("Unsupported OS. Neither /proc nor 'ps' works.")
- IMPL = "unsupported"
+ from subprocess import Popen
+ from subprocess import PIPE
-NULL = "\x00"
-
class UnsupportedError(OSError):
+ """OS or command not supported error
+ """
pass
class Unsupported(object):
- """Unsupported OS
+ """Unsupported OS implementation
+
+ Raises L{UnsupportedError}
"""
__slots__ = ()
- def supported(self):
+ @classmethod
+ def supported(cls):
+ """Supported flag property"""
return False
- supported = property(supported)
- def getPids(self):
+ @classmethod
+ def log(cls):
+ """Log information about the implementation"""
+ LOG.warning("Unsupported OS. Neither proc filesystem nor 'ps' works.")
+
+ @classmethod
+ def getPids(cls):
"""Get a list of pids
+
+ @return: a list of pid numbers
+ @rtype: list(int)
"""
raise UnsupportedError
- def getPidNames(self):
- """Get a list of pid -> name
+ @classmethod
+ def getPidNames(cls):
+ """Get a mapping of pid -> name
+
+ @return: mapping of pid -> name
+ @rtype: dict(int:str)
"""
raise UnsupportedError
- def getPidDetails(self, pid):
+ @classmethod
+ def getPidDetails(pid):
"""Get detailed informations about a process
+
+ @param pid: pid number or 'self'
+ @type pid: int or basestring
+ @return: detailed information about a process
+ @rtype: dict(str:data)
"""
raise UnsupportedError
@@ -126,14 +144,26 @@
PIDNAMES = "%s --format=pid,ucmd" % CMD
PIDS = "%s --format=pid" % CMD
- def supported(self):
- return False
- supported = property(supported)
+ @classmethod
+ def supported(cls):
+ """Supported flag property"""
+ try:
+ cls._exec(cls.PIDS)
+ except Exception: # catch all!
+ return False
+ else:
+ return True
- def getPids(self):
+ @classmethod
+ def log(cls):
+ """Log information about the implementation"""
+ LOG.debug("Using the 'ps' command on POSIX os")
+
+ @classmethod
+ def getPids(cls):
"""Get a list of pids
"""
- stdout = self._exec(self.PIDS)
+ stdout = cls._exec(cls.PIDS)
if stdout is None:
return None
pids = []
@@ -146,10 +176,11 @@
pids.append(pid)
return pids
- def getPidNames(self):
+ @classmethod
+ def getPidNames(cls):
"""Get a list of pid -> name
"""
- stdout = self._exec(self.PIDNAMES)
+ stdout = cls._exec(cls.PIDNAMES)
if stdout is None:
return None
mapping = {}
@@ -165,14 +196,18 @@
mapping[pid] = name
return mapping
- def getPidDetails(self, pid):
+ @classmethod
+ def getPidDetails(cls, pid):
"""Get detailed informations about a process
TODO
"""
- raise UnsupportedError
+ if pid == 'self':
+ pid = os.getpid()
+ raise UnsupportedError("not implemented yet")
- def _exec(self, cmd):
+ @staticmethod
+ def _exec(cmd):
"""Execute command cmd
The method waits until the command has finished. It returns None of
@@ -203,42 +238,53 @@
"""
__slots__ = ()
- def supported(self):
- return True
- supported = property(supported)
+ PROC = "/proc"
- @staticmethod
- def getPids():
+ @classmethod
+ def supported(cls):
+ return os.path.isfile("%s/self/status" % cls.PROC)
+
+ @classmethod
+ def log(cls):
+ """Log information about the implementation"""
+ LOG.debug("Using the proc filesystem on Linux")
+
+ @classmethod
+ def getPids(cls):
"""Get a list of pids
"""
pids = []
- for name in os.listdir(PROC):
- if os.path.isdir(PROC + '/' + name):
+ for name in os.listdir(cls.PROC):
+ if os.path.isdir(cls.PROC + '/' + name):
try:
pids.append(int(name))
except ValueError:
pass
return pids
- def getPidNames(self):
+ @classmethod
+ def getPidNames(cls):
"""Get a list of pid -> name
"""
mapping = {}
- for pid in self.getPids():
- name = self._readProcStatus(pid, searchkey='name')
+ for pid in cls.getPids():
+ name = cls._readProcStatus(pid, searchkey='name')
if name is not None:
mapping[pid] = name
return mapping
- def getPidDetails(self, pid):
+ def getPidDetails(cls, pid):
"""Get detailed informations about a process
"""
- details = self._readProcStatus(pid)
- details.update(self._readProcOther(pid))
- details['cmdline'] = self._readProcCmdline(pid)
+ details = cls._readProcStatus(pid)
+ if details is None:
+ return None
+ details.update(cls._readProcOther(pid))
+ details['cmdline'] = cls._readProcCmdline(pid)
return details
- def _readProcStatus(self, pid, searchkey=None):
+ @classmethod
+ def _readProcStatus(cls, pid, searchkey=None):
"""Read and parse status informations for PID pid
pid - pid as long or int or 'self'
@@ -249,10 +295,12 @@
or returns None if they key can't be found.
"""
mapping = {}
+ status = '%s/%s/status' % (cls.PROC, pid)
try:
# read entiry file to avoid race conditions
- lines = open('%s/%s/status' % (PROC, pid), 'r').readlines()
+ lines = open(status, 'r').readlines()
except IOError:
+ LOG.exception("%s not found" % status)
return None
for line in lines:
try:
@@ -270,25 +318,27 @@
return None
return mapping
- def _readProcCmdline(self, pid):
+ @classmethod
+ def _readProcCmdline(cls, pid):
"""Read cmdline informations for pid and returns a list similar to sys.argv
"""
try:
# read entiry file to avoid race conditions
- data = open('%s/%s/cmdline' % (PROC, pid), 'r').read()
+ data = open('%s/%s/cmdline' % (cls.PROC, pid), 'r').read()
except IOError:
return None
return data.split(NULL)
- def _readProcOther(self, pid):
+ @classmethod
+ def _readProcOther(cls, pid):
"""Read other possible useful things
cwd -> current work directory (may not exist)
exe -> path to executable (may not exist)
"""
return {
- 'cwd' : os.path.realpath('%s/%s/cwd' % (PROC, pid)),
- 'exe' : os.path.realpath('%s/%s/exe' % (PROC, pid)),
+ 'cwd' : os.path.realpath('%s/%s/cwd' % (cls.PROC, pid)),
+ 'exe' : os.path.realpath('%s/%s/exe' % (cls.PROC, pid)),
}
class WinEnumProcesses(object):
@@ -299,11 +349,22 @@
"""
__slots__ = ()
- def supported(self):
- return True
- supported = property(supported)
+ @classmethod
+ def supported(cls):
+ try:
+ cls.getPids()
+ except Exception: # catch all!
+ return False
+ else:
+ return True
- def getPids(self):
+ @classmethod
+ def log(cls):
+ """Log information about the implementation"""
+ LOG.debug("Using ctypes on Windows")
+
+ @classmethod
+ def getPids(cls):
"""Get a list of pids
"""
arr = c_ulong * 256
@@ -316,14 +377,15 @@
nReturned = cbNeeded.value/sizeof(c_ulong()) # number of processes returned
return [pid for pid in lpidProcess][:nReturned]
- def getPidNames(self):
+ @classmethod
+ def getPidNames(cls):
"""Get a list of pid -> name
"""
hModule = c_ulong()
count = c_ulong()
modname = c_buffer(51)
mapping = {}
- for pid in self.getPids():
+ for pid in cls.getPids():
# Get handle to the process based on PID
hProcess = KERNEL.OpenProcess(PROCESS_FLAGS, False, pid)
if hProcess:
@@ -342,7 +404,8 @@
return mapping
- def getPidDetails(self, pid):
+ @classmethod
+ def getPidDetails(cls, pid):
"""Get detailed informations about a process
"""
if pid == 'self':
@@ -371,21 +434,32 @@
# Initialize global methods
-if IMPL == 'proc':
- _enumProcesses = LinuxProcReader()
-elif IMPL == 'ps':
- _enumProcesses = PsParser()
-elif IMPL == 'win':
- _enumProcesses = WinEnumProcesses()
-else:
+_enumProcesses = None
+for impl in (WinEnumProcesses(), LinuxProcReader(), PsParser()):
+ if impl.supported():
+ impl.log()
+ _enumProcesses = impl
+ break
+
+if _enumProcesses is None:
LOG.error("System %s is not supported" % sys.platform)
_enumProcesses = Unsupported()
getPids = _enumProcesses.getPids
getPidNames = _enumProcesses.getPidNames
getPidDetails = _enumProcesses.getPidDetails
-supportedOS = _enumProcesses.supported
+supportedOS = _enumProcesses.supported()
+def test_suite():
+ import unittest
+ from doctest import DocTestSuite
+ return unittest.TestSuite((
+ DocTestSuite()
+ ))
+
if __name__ == '__main__':
+ import unittest
+ logging.basicConfig()
+ unittest.main(defaultTest="test_suite")
+ print getPids()
print getPidNames()
- print getPidDetails('self')
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|