diff options
author | Slavek Kabrda <bkabrda@redhat.com> | 2015-01-21 12:57:35 +0100 |
---|---|---|
committer | David Sommerseth <davids@redhat.com> | 2015-02-03 21:16:25 +0100 |
commit | 5cc3d02060f67317049b6e0549c87a407e030e61 (patch) | |
tree | 15174b46cabd9ea08a1c9c35ebb831018577f00f | |
parent | 9fa1352cd540aa69dd8b59e56de9b8020563e545 (diff) | |
download | python-dmidecode-5cc3d02060f67317049b6e0549c87a407e030e61.tar.gz python-dmidecode-5cc3d02060f67317049b6e0549c87a407e030e61.tar.xz python-dmidecode-5cc3d02060f67317049b6e0549c87a407e030e61.zip |
Port to Python 3 while maintaining compatibility with Python >= 2.6
Signed-off-by: David Sommerseth <davids@redhat.com>
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | dmidecode.py | 6 | ||||
-rwxr-xr-x | examples/dmidump.py | 108 | ||||
-rw-r--r-- | src/compat.h | 15 | ||||
-rw-r--r-- | src/dmidecodemodule.c | 75 | ||||
-rw-r--r-- | src/setup_common.py | 20 | ||||
-rw-r--r-- | src/xmlpythonizer.c | 7 | ||||
-rw-r--r-- | unit-tests/Makefile | 4 | ||||
-rwxr-xr-x | unit-tests/unit | 27 |
9 files changed, 179 insertions, 98 deletions
@@ -38,11 +38,19 @@ #. $AutoHeaderSerial::20100225 $ #. ******* AUTOHEADER END v1.2 ******* -VERSION := $(shell cd src;python -c "from setup_common import *; print get_version();") +PY_BIN := python2 +VERSION := $(shell cd src;$(PY_BIN) -c "from setup_common import *; print(get_version());") PACKAGE := python-dmidecode -PY_VER := $(shell python -c 'import sys; print "%d.%d"%sys.version_info[0:2]') +PY_VER := $(shell $(PY_BIN) -c 'import sys; print("%d.%d"%sys.version_info[0:2])') +PY_MV := $(shell echo $(PY_VER) | cut -b 1) PY := python$(PY_VER) -SO := build/lib.linux-$(shell uname -m)-$(PY_VER)/dmidecodemod.so +SO_PATH := build/lib.linux-$(shell uname -m)-$(PY_VER) +ifeq ($(PY_MV),2) + SO := $(SO_PATH)/dmidecodemod.so +else + SOABI := $(shell $(PY_BIN) -c 'import sysconfig; print(sysconfig.get_config_var("SOABI"))') + SO := $(SO_PATH)/dmidecodemod.$(SOABI).so +endif SHELL := /bin/bash ############################################################################### @@ -71,6 +79,7 @@ clean: -rm -rf build -rm -rf rpm -rm -rf src/setup_common.py[oc] + -rm -rf __pycache__ src/__pycache__ -rm -rf $(PACKAGE)-$(VERSION) $(PACKAGE)-$(VERSION).tar.gz $(MAKE) -C unit-tests clean diff --git a/dmidecode.py b/dmidecode.py index cd37b40..ac81365 100644 --- a/dmidecode.py +++ b/dmidecode.py @@ -48,7 +48,7 @@ class dmidecodeXML: elif type == DMIXML_DOC: self.restype = DMIXML_DOC else: - raise TypeError, "Invalid result type value" + raise TypeError("Invalid result type value") return True def QuerySection(self, sectname): @@ -65,7 +65,7 @@ class dmidecodeXML: result_type=self.restype, section=sectname) ) else: - raise TypeError, "Invalid result type value" + raise TypeError("Invalid result type value") return ret @@ -83,7 +83,7 @@ class dmidecodeXML: result_type=self.restype, typeid=tpid)) else: - raise TypeError, "Invalid result type value" + raise TypeError("Invalid result type value") return ret diff --git a/examples/dmidump.py b/examples/dmidump.py index 11b04ff..769750d 100755 --- a/examples/dmidump.py +++ b/examples/dmidump.py @@ -35,24 +35,24 @@ def print_warnings(): "Simple function, dumping out warnings with a prefix if warnings are found and clearing warning buffer" warn = dmidecode.get_warnings() if warn: - print "### WARNING: %s" % warn + print("### WARNING: %s" % warn) dmidecode.clear_warnings() # Check if running as root .... provide a warning if not root_user = (os.getuid() == 0 and True or False) if not root_user: - print "####" - print "#### NOT RUNNING AS ROOT" - print "####" - print "#### The first run must always be done as root for this example to work." - print "#### When not run as root, quite some permission errors might appear" - print "####" - print "#### If this script is first run as root, it should be possible to run this script" - print "#### as an unprivileged user afterwards, with less warnings." - print "####" - print - print + print("####") + print("#### NOT RUNNING AS ROOT") + print("####") + print("#### The first run must always be done as root for this example to work.") + print("#### When not run as root, quite some permission errors might appear") + print("####") + print("#### If this script is first run as root, it should be possible to run this script") + print("#### as an unprivileged user afterwards, with less warnings.") + print("####") + print() + print() #. Test for presence of important functions using /dev/mem... Using the legacy API @@ -61,101 +61,101 @@ if not root_user: #. for presence of the legacy API, which "under the hood" uses #. dmidecode.QuerySection(name), where name can be 'bios', 'system', etc. if root_user: - print "*** bios ***\n"; dmidecode.bios() + print("*** bios ***\n"); dmidecode.bios() print_warnings() - print "*** system ***\n"; dmidecode.system() + print("*** system ***\n"); dmidecode.system() print_warnings() - print "*** baseboard ***\n"; dmidecode.baseboard() + print("*** baseboard ***\n"); dmidecode.baseboard() print_warnings() - print "*** chassis ***\n"; dmidecode.chassis() + print("*** chassis ***\n"); dmidecode.chassis() print_warnings() - print "*** processor ***\n"; dmidecode.processor() + print("*** processor ***\n"); dmidecode.processor() print_warnings() - print "*** memory ***\n"; dmidecode.memory() + print("*** memory ***\n"); dmidecode.memory() print_warnings() - print "*** cache ***\n"; dmidecode.cache() + print("*** cache ***\n"); dmidecode.cache() print_warnings() - print "*** connector ***\n"; dmidecode.connector() + print("*** connector ***\n"); dmidecode.connector() print_warnings() - print "*** slot ***\n"; dmidecode.slot() + print("*** slot ***\n"); dmidecode.slot() print_warnings() #. Now test get/set of memory device file... -print "*** get_dev()" -print dmidecode.get_dev() +print("*** get_dev()") +print(dmidecode.get_dev()) print_warnings() -print "*** set_dev('dmidata.dump')" -print dmidecode.set_dev("dmidata.dump"); +print("*** set_dev('dmidata.dump')") +print(dmidecode.set_dev("dmidata.dump")); print_warnings() -print "*** get_dev()" -print dmidecode.get_dev() +print("*** get_dev()") +print(dmidecode.get_dev()) print_warnings() #. Test taking a dump... if root_user: - print "*** Dumping DMI data to dump file" - print dmidecode.dump() + print("*** Dumping DMI data to dump file") + print(dmidecode.dump()) print_warnings() #. Test reading the dump... Using the preferred API -print "*** bios ***\n"; pprint(dmidecode.QuerySection('bios')) +print("*** bios ***\n"); pprint(dmidecode.QuerySection('bios')) print_warnings() -print "*** system ***\n"; pprint(dmidecode.QuerySection('system')) +print("*** system ***\n"); pprint(dmidecode.QuerySection('system')) print_warnings() -print "*** baseboard ***\n"; pprint(dmidecode.QuerySection('baseboard')) +print("*** baseboard ***\n"); pprint(dmidecode.QuerySection('baseboard')) print_warnings() -print "*** chassis ***\n"; pprint(dmidecode.QuerySection('chassis')) +print("*** chassis ***\n"); pprint(dmidecode.QuerySection('chassis')) print_warnings() -print "*** processor ***\n"; pprint(dmidecode.QuerySection('processor')) +print("*** processor ***\n"); pprint(dmidecode.QuerySection('processor')) print_warnings() -print "*** memory ***\n"; pprint(dmidecode.QuerySection('memory')) +print("*** memory ***\n"); pprint(dmidecode.QuerySection('memory')) print_warnings() -print "*** cache ***\n"; pprint(dmidecode.QuerySection('cache')) +print("*** cache ***\n"); pprint(dmidecode.QuerySection('cache')) print_warnings() -print "*** connector ***\n"; pprint(dmidecode.QuerySection('connector')) +print("*** connector ***\n"); pprint(dmidecode.QuerySection('connector')) print_warnings() -print "*** slot ***\n"; pprint(dmidecode.QuerySection('slot')) +print("*** slot ***\n"); pprint(dmidecode.QuerySection('slot')) print_warnings() -print "*** Extracting memory information" +print("*** Extracting memory information") for v in dmidecode.memory().values(): if type(v) == dict and v['dmi_type'] == 17: pprint(v['data']['Size']), -print "*** Querying for DMI type 3 and 7" +print("*** Querying for DMI type 3 and 7") pprint(dmidecode.type(3)) # <-- Legacy API pprint(dmidecode.QueryTypeId(7)) # <-- preferred API print_warnings() -print "*** Querying for the BIOS section" +print("*** Querying for the BIOS section") pprint(dmidecode.QuerySection('bios')) print_warnings() # # Test XML stuff # -print -print -print -print "---------------------------------------" -print "*** *** *** Testing XML API *** *** ***" -print "---------------------------------------" -print -print +print() +print() +print() +print("---------------------------------------") +print("*** *** *** Testing XML API *** *** ***") +print("---------------------------------------") +print() +print() dmixml = dmidecode.dmidecodeXML() # Fetch all DMI data into a libxml2.xmlDoc object -print "*** Getting all DMI data into a XML document variable" +print("*** Getting all DMI data into a XML document variable") dmixml.SetResultType(dmidecode.DMIXML_DOC) # Valid values: dmidecode.DMIXML_DOC, dmidecode.DMIXML_NODE xmldoc = dmixml.QuerySection('all') # Dump the XML to dmidump.xml - formated in UTF-8 decoding -print "*** Dumping XML document to dmidump.xml" +print("*** Dumping XML document to dmidump.xml") xmldoc.saveFormatFileEnc('dmidump.xml','UTF-8',1) # Do some XPath queries on the XML document -print "*** Doing some XPath queries against the XML document" +print("*** Doing some XPath queries against the XML document") dmixp = xmldoc.xpathNewContext() # What to look for - XPath expressions @@ -168,12 +168,12 @@ keys = ['/dmidecode/SystemInfo/Manufacturer', for k in keys: data = dmixp.xpathEval(k) for d in data: - print "%s: %s" % (k, d.get_content()) + print("%s: %s" % (k, d.get_content())) del dmixp del xmldoc # Query for only a particular DMI TypeID - 0x04 - Processor -print "*** Quering for Type ID 0x04 - Processor - dumping XML document to stdout" +print("*** Quering for Type ID 0x04 - Processor - dumping XML document to stdout") dmixml.QueryTypeId(0x04).saveFormatFileEnc('-','UTF-8',1) print_warnings() diff --git a/src/compat.h b/src/compat.h index 80a08c6..170f596 100644 --- a/src/compat.h +++ b/src/compat.h @@ -40,4 +40,19 @@ #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None #endif +// Python 2 vs Python 3 compat +#if PY_MAJOR_VERSION >= 3 +#define IS_PY3K +#define MODINITERROR return NULL +#define PYNUMBER_FROMLONG PyLong_FromLong +#define PYTEXT_FROMSTRING PyUnicode_FromString +#else +#include <bytesobject.h> +#define MODINITERROR return +#define PYNUMBER_FROMLONG PyInt_FromLong +#define PYTEXT_FROMSTRING PyString_FromString +#define PyCapsule_New(pointer, name, destructor) \ + (PyCObject_FromVoidPtr(pointer, destructor)) +#endif + #endif diff --git a/src/dmidecodemodule.c b/src/dmidecodemodule.c index 1056a8f..8db99cb 100644 --- a/src/dmidecodemodule.c +++ b/src/dmidecodemodule.c @@ -53,6 +53,17 @@ #include "dmidump.h" #include <mcheck.h> +#if (PY_VERSION_HEX < 0x03030000) +char *PyUnicode_AsUTF8(PyObject *unicode) { + PyObject *as_bytes = PyUnicode_AsUTF8String(unicode); + if (!as_bytes) { + return NULL; + } + + return PyBytes_AsString(as_bytes); +} +#endif + static void init(options *opt) { opt->devmem = DEFAULT_MEM_DEV; @@ -470,7 +481,12 @@ static PyObject *dmidecode_get_slot(PyObject * self, PyObject * args) static PyObject *dmidecode_get_section(PyObject *self, PyObject *args) { - char *section = PyString_AsString(args); + char *section = NULL; + if (PyUnicode_Check(args)) { + section = PyUnicode_AsUTF8(args); + } else if (PyBytes_Check(args)) { + section = PyBytes_AsString(args); + } if( section != NULL ) { return dmidecode_get_group(global_options, section); @@ -588,7 +604,7 @@ static PyObject *dmidecode_dump(PyObject * self, PyObject * null) static PyObject *dmidecode_get_dev(PyObject * self, PyObject * null) { PyObject *dev = NULL; - dev = PyString_FromString((global_options->dumpfile != NULL + dev = PYTEXT_FROMSTRING((global_options->dumpfile != NULL ? global_options->dumpfile : global_options->devmem)); Py_INCREF(dev); return dev; @@ -596,9 +612,14 @@ static PyObject *dmidecode_get_dev(PyObject * self, PyObject * null) static PyObject *dmidecode_set_dev(PyObject * self, PyObject * arg) { - if(PyString_Check(arg)) { + char *f = NULL; + if(PyUnicode_Check(arg)) { + f = PyUnicode_AsUTF8(arg); + } else if(PyBytes_Check(arg)) { + f = PyBytes_AsString(arg); + } + if(f) { struct stat buf; - char *f = PyString_AsString(arg); if( (f != NULL) && (global_options->dumpfile != NULL ) && (strcmp(global_options->dumpfile, f) == 0) ) { @@ -638,9 +659,9 @@ static PyObject *dmidecode_set_dev(PyObject * self, PyObject * arg) static PyObject *dmidecode_set_pythonxmlmap(PyObject * self, PyObject * arg) { - if(PyString_Check(arg)) { + if(PyBytes_Check(arg)) { struct stat fileinfo; - char *fname = PyString_AsString(arg); + char *fname = PyBytes_AsString(arg); memset(&fileinfo, 0, sizeof(struct stat)); @@ -664,7 +685,7 @@ static PyObject * dmidecode_get_warnings(PyObject *self, PyObject *null) warn = log_retrieve(global_options->logdata, LOG_WARNING); if( warn ) { - ret = PyString_FromString(warn); + ret = PYTEXT_FROMSTRING(warn); free(warn); } else { ret = Py_None; @@ -711,7 +732,7 @@ static PyMethodDef DMIDataMethods[] = { {(char *)"pythonmap", dmidecode_set_pythonxmlmap, METH_O, (char *) "Use another python dict map definition. The default file is " PYTHON_XML_MAP}, - {(char *)"xmlapi", dmidecode_xmlapi, METH_KEYWORDS, + {(char *)"xmlapi", dmidecode_xmlapi, METH_VARARGS | METH_KEYWORDS, (char *) "Internal API for retrieving data as raw XML data"}, @@ -726,6 +747,9 @@ static PyMethodDef DMIDataMethods[] = { void destruct_options(void *ptr) { +#ifdef IS_PY3K + ptr = PyCapsule_GetPointer(ptr, NULL); +#endif options *opt = (options *) ptr; if( opt->mappingxml != NULL ) { @@ -763,8 +787,25 @@ void destruct_options(void *ptr) free(ptr); } +#ifdef IS_PY3K +static struct PyModuleDef dmidecodemod_def = { + PyModuleDef_HEAD_INIT, + "dmidecodemod", + NULL, + -1, + DMIDataMethods, + NULL, + NULL, + NULL, + NULL +}; -PyMODINIT_FUNC initdmidecodemod(void) +PyMODINIT_FUNC +PyInit_dmidecodemod(void) +#else +PyMODINIT_FUNC +initdmidecodemod(void) +#endif { char *dmiver = NULL; PyObject *module = NULL; @@ -777,19 +818,29 @@ PyMODINIT_FUNC initdmidecodemod(void) opt = (options *) malloc(sizeof(options)+2); memset(opt, 0, sizeof(options)+2); init(opt); +#ifdef IS_PY3K + module = PyModule_Create(&dmidecodemod_def); +#else module = Py_InitModule3((char *)"dmidecodemod", DMIDataMethods, "Python extension module for dmidecode"); +#endif + if (module == NULL) + MODINITERROR; - version = PyString_FromString(VERSION); + version = PYTEXT_FROMSTRING(VERSION); Py_INCREF(version); PyModule_AddObject(module, "version", version); opt->dmiversion_n = dmidecode_get_version(opt); dmiver = dmixml_GetContent(opt->dmiversion_n); - PyModule_AddObject(module, "dmi", dmiver ? PyString_FromString(dmiver) : Py_None); + PyModule_AddObject(module, "dmi", dmiver ? PYTEXT_FROMSTRING(dmiver) : Py_None); // Assign this options struct to the module as well with a destructor, that way it will // clean up the memory for us. - PyModule_AddObject(module, "options", PyCObject_FromVoidPtr(opt, destruct_options)); + // TODO: destructor has wrong type under py3? + PyModule_AddObject(module, "options", PyCapsule_New(opt, NULL, destruct_options)); global_options = opt; +#ifdef IS_PY3K + return module; +#endif } diff --git a/src/setup_common.py b/src/setup_common.py index 209ccef..aec1f9b 100644 --- a/src/setup_common.py +++ b/src/setup_common.py @@ -26,17 +26,19 @@ # are deemed to be part of the source code. # -import commands, sys +import subprocess, sys +if sys.version_info[0] < 3: + import commands as subprocess from os import path as os_path from distutils.sysconfig import get_python_lib # libxml2 - C flags def libxml2_include(incdir): - (res, libxml2_cflags) = commands.getstatusoutput("xml2-config --cflags") + (res, libxml2_cflags) = subprocess.getstatusoutput("xml2-config --cflags") if res != 0: - print "Could not build python-dmidecode." - print "Could not run xml2-config, is libxml2 installed?" - print "Also the development libraries?" + print("Could not build python-dmidecode.") + print("Could not run xml2-config, is libxml2 installed?") + print("Also the development libraries?") sys.exit(1) # Parse the xml2-config --cflags response @@ -52,11 +54,11 @@ def libxml2_lib(libdir, libs): if os_path.exists("/etc/debian_version"): #. XXX: Debian Workaround... libdir.append("/usr/lib/pymodules/python%d.%d"%sys.version_info[0:2]) - (res, libxml2_libs) = commands.getstatusoutput("xml2-config --libs") + (res, libxml2_libs) = subprocess.getstatusoutput("xml2-config --libs") if res != 0: - print "Could not build python-dmidecode." - print "Could not run xml2-config, is libxml2 installed?" - print "Also the development libraries?" + print("Could not build python-dmidecode.") + print("Could not run xml2-config, is libxml2 installed?") + print("Also the development libraries?") sys.exit(1) # Parse the xml2-config --libs response diff --git a/src/xmlpythonizer.c b/src/xmlpythonizer.c index e318023..e9c9242 100644 --- a/src/xmlpythonizer.c +++ b/src/xmlpythonizer.c @@ -85,6 +85,7 @@ #include "dmilog.h" #include "xmlpythonizer.h" #include "version.h" +#include "compat.h" /** @@ -646,7 +647,7 @@ inline PyObject *StringToPyObj(Log_t *logp, ptzMAP *val_m, const char *instr) { switch( val_m->type_value ) { case ptzINT: case ptzLIST_INT: - value = PyInt_FromLong(atoi(workstr)); + value = PYNUMBER_FROMLONG(atoi(workstr)); break; case ptzFLOAT: @@ -661,7 +662,7 @@ inline PyObject *StringToPyObj(Log_t *logp, ptzMAP *val_m, const char *instr) { case ptzSTR: case ptzLIST_STR: - value = PyString_FromString(workstr); + value = PyBytes_FromString(workstr); break; default: @@ -850,7 +851,7 @@ PyObject *_deep_pythonize(Log_t *logp, PyObject *retdata, switch( map_p->type_value ) { case ptzCONST: if( _get_key_value(logp, key, 256, map_p, xpctx, 0) != NULL ) { - value = PyString_FromString(map_p->value); + value = PyBytes_FromString(map_p->value); PyADD_DICT_VALUE(retdata, key, value); } else { PyReturnError(PyExc_ValueError, "Could not get key value: %s [%i] (Defining key: %s)", diff --git a/unit-tests/Makefile b/unit-tests/Makefile index 95086c1..765fd99 100644 --- a/unit-tests/Makefile +++ b/unit-tests/Makefile @@ -1,5 +1,7 @@ +PY_BIN := python2 + test : - python unit -vv + $(PY_BIN) unit -vv clean : rm -f *.{py[oc],o,so} *~ diff --git a/unit-tests/unit b/unit-tests/unit index 8f9184b..3b06c04 100755 --- a/unit-tests/unit +++ b/unit-tests/unit @@ -2,8 +2,9 @@ #.awk '$0 ~ /case [0-9]+: .. 3/ { sys.stdout.write($2 }' src/dmidecode.c|tr ':\n' ', ' from pprint import pprint -import os, sys, random, tempfile, time -import commands +import os, sys, subprocess, random, tempfile, time +if sys.version_info[0] < 3: + import commands as subprocess from getopt import getopt # Setup temporary sys.path() with our build dir @@ -32,7 +33,7 @@ try: COLOR = True elif o in ("-h", "--help"): HELP = True -except getopt.GetoptError, err: +except getopt.GetoptError as err: # print help information and exit: HELP = True ERROR = True @@ -138,7 +139,7 @@ def vwrite(msg, vLevel=0): ################################################################################ #. Let's ignore warnings from the module for the test units... -err = open('/dev/null', 'a+', 0) +err = open('/dev/null', 'a+', 1) os.dup2(err.fileno(), sys.stderr.fileno()) vwrite(LINE, 1) @@ -208,7 +209,7 @@ try: "Skip testing API function, missing root privileges: dmidecode.dump()" ), 1) - types = range(0, 42)+range(126, 128) + types = list(range(0, 42))+list(range(126, 128)) bad_types = [-1, -1000, 256] sections = [ "bios", @@ -263,7 +264,7 @@ try: test(output is not False) if output: vwrite(" * %s\n"%black(output.keys()), 1) - except LookupError, e: + except LookupError as e: failed(e, 1) for i in bad_types: @@ -279,15 +280,15 @@ try: try: output = dmidecode.type(i) if dmidecode_bin: - _output = commands.getoutput("dmidecode -t %d"%i).strip().split('\n') + _output = subprocess.getoutput("dmidecode -t %d"%i).strip().split('\n') test(len(_output) == 1 and len(output) == 0 or True) else: test(output is not False) if output: vwrite(" * %s\n"%output.keys(), 1) - except IOError, e: + except IOError as e: failed(e, 1) - except LookupError, e: + except LookupError as e: failed(e, 1) @@ -330,7 +331,7 @@ try: try: output_node = dmixml.QueryTypeId(i) test(isinstance(output_node, libxml2.xmlNode)) - except Exception, e: + except Exception as e: failed(e, 1) except: failed() @@ -347,7 +348,7 @@ try: try: output_doc = dmixml.QuerySection(section) test(isinstance(output_doc, libxml2.xmlDoc)) - except Exception, e: + except Exception as e: failed(e, 1) except: failed() @@ -355,9 +356,9 @@ try: except IOError: skipped() -except ImportError, err: +except ImportError as err: failed() - print err + print(err) vwrite(LINE, 1) vwrite("Devices : %s\n"%cyan(len(devices)), 1) |