changeset 112:9343eee50605

Update to Python 3. Currently crashes during tests (and also, two tests are now failing, even with Py2).
author Chris Cannam
date Wed, 17 Jun 2015 12:35:41 +0100
parents 4f590fc46ace
children 1c7c4bd74363
files Makefile.inc Makefile.linux native/PyPluginObject.cpp native/PyRealTime.cpp native/StringConversion.h native/VectorConversion.cpp native/vampyhost.cpp test/test_collect.py test/test_process.py vamp/__init__.py vamp/collect.py vamp/process.py
diffstat 12 files changed, 315 insertions(+), 136 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.inc	Wed Jun 17 11:04:15 2015 +0100
+++ b/Makefile.inc	Wed Jun 17 12:35:41 2015 +0100
@@ -26,8 +26,9 @@
 .tests:		$(LIBRARY) $(PY) $(TESTPLUG) $(TESTS)
 		VAMP_PATH=$(TESTPLUG_DIR) $(NOSE)
 		@touch $@
-		
-test:		$(LIBRARY) $(PY) $(TESTPLUG) $(TESTS)
+
+.PHONY:		test
+test:		
 		VAMP_PATH=$(TESTPLUG_DIR) $(NOSE)
 
 $(LIBRARY):	$(OBJECTS)
@@ -51,8 +52,10 @@
 # DO NOT DELETE
 
 native/PyPluginObject.o: native/PyPluginObject.h native/FloatConversion.h
-native/PyPluginObject.o: native/VectorConversion.h native/PyRealTime.h
+native/PyPluginObject.o: native/VectorConversion.h native/StringConversion.h
+native/PyPluginObject.o: native/PyRealTime.h
 native/PyRealTime.o: native/PyRealTime.h
-native/VectorConversion.o: native/FloatConversion.h native/VectorConversion.h
+native/VectorConversion.o: native/VectorConversion.h native/FloatConversion.h
+native/VectorConversion.o: native/StringConversion.h
 native/vampyhost.o: native/PyRealTime.h native/PyPluginObject.h
-native/vampyhost.o: native/VectorConversion.h
+native/vampyhost.o: native/VectorConversion.h native/StringConversion.h
--- a/Makefile.linux	Wed Jun 17 11:04:15 2015 +0100
+++ b/Makefile.linux	Wed Jun 17 12:35:41 2015 +0100
@@ -6,7 +6,7 @@
 
 #PY_INCLUDE_PATH		:= /usr/include/python3.4m
 #NUMPY_INCLUDE_PATH 	:= /usr/lib/python3.4m/site-packages/numpy/core/include
-#PY_LIB			:= python3
+#PY_LIB			:= python3.4m
 #PY_TEST			:= nosetests3
 
 CFLAGS 			:= -O2 -Wall -Werror -fno-strict-aliasing -fPIC \
--- a/native/PyPluginObject.cpp	Wed Jun 17 11:04:15 2015 +0100
+++ b/native/PyPluginObject.cpp	Wed Jun 17 12:35:41 2015 +0100
@@ -51,6 +51,7 @@
 
 #include "FloatConversion.h"
 #include "VectorConversion.h"
+#include "StringConversion.h"
 #include "PyRealTime.h"
 
 #include <string>
@@ -78,28 +79,6 @@
     }
 }
 
-static
-PyObject *
-pystr(const string &s)
-{
-#if PY_MAJOR_VERSION < 3
-    return PyString_FromString(s.c_str());
-#else
-    return PyUnicode_FromString(s.c_str());
-#endif
-}
-
-static
-string
-py2str(PyObject *obj)
-{
-#if PY_MAJOR_VERSION < 3
-    return PyString_AsString(obj);
-#else
-    return PyBytes_AsString(PyUnicode_AsUTF8String(obj));
-#endif
-}
-
 PyObject *
 PyPluginObject_From_Plugin(Plugin *plugin)
 {
@@ -111,21 +90,23 @@
     pd->blockSize = 0;
     pd->stepSize = 0;
 
+    StringConversion strconv;
+    
     PyObject *infodict = PyDict_New();
     PyDict_SetItemString
         (infodict, "apiVersion", PyLong_FromLong(plugin->getVampApiVersion()));
     PyDict_SetItemString
         (infodict, "pluginVersion", PyLong_FromLong(plugin->getPluginVersion()));
     PyDict_SetItemString
-        (infodict, "identifier", pystr(plugin->getIdentifier()));
+        (infodict, "identifier", strconv.string2py(plugin->getIdentifier()));
     PyDict_SetItemString
-        (infodict, "name", pystr(plugin->getName()));
+        (infodict, "name", strconv.string2py(plugin->getName()));
     PyDict_SetItemString
-        (infodict, "description", pystr(plugin->getDescription()));
+        (infodict, "description", strconv.string2py(plugin->getDescription()));
     PyDict_SetItemString
-        (infodict, "maker", pystr(plugin->getMaker()));
+        (infodict, "maker", strconv.string2py(plugin->getMaker()));
     PyDict_SetItemString
-        (infodict, "copyright", pystr(plugin->getCopyright()));
+        (infodict, "copyright", strconv.string2py(plugin->getCopyright()));
     pd->info = infodict;
 
     pd->inputDomain = plugin->getInputDomain();
@@ -138,13 +119,13 @@
     for (int i = 0; i < (int)pl.size(); ++i) {
         PyObject *paramdict = PyDict_New();
         PyDict_SetItemString
-            (paramdict, "identifier", pystr(pl[i].identifier));
+            (paramdict, "identifier", strconv.string2py(pl[i].identifier));
         PyDict_SetItemString
-            (paramdict, "name", pystr(pl[i].name));
+            (paramdict, "name", strconv.string2py(pl[i].name));
         PyDict_SetItemString
-            (paramdict, "description", pystr(pl[i].description));
+            (paramdict, "description", strconv.string2py(pl[i].description));
         PyDict_SetItemString
-            (paramdict, "unit", pystr(pl[i].unit));
+            (paramdict, "unit", strconv.string2py(pl[i].unit));
         PyDict_SetItemString
             (paramdict, "minValue", PyFloat_FromDouble(pl[i].minValue));
         PyDict_SetItemString
@@ -174,7 +155,7 @@
     PyObject *progs = PyList_New(prl.size());
 
     for (int i = 0; i < (int)prl.size(); ++i) {
-        PyList_SET_ITEM(progs, i, pystr(prl[i]));
+        PyList_SET_ITEM(progs, i, strconv.string2py(prl[i]));
     }
 
     pd->programs = progs;
@@ -193,16 +174,17 @@
 convertOutput(const Plugin::OutputDescriptor &desc, int ix)
 {
     VectorConversion conv;
+    StringConversion strconv;
     
     PyObject *outdict = PyDict_New();
     PyDict_SetItemString
-        (outdict, "identifier", pystr(desc.identifier));
+        (outdict, "identifier", strconv.string2py(desc.identifier));
     PyDict_SetItemString
-        (outdict, "name", pystr(desc.name));
+        (outdict, "name", strconv.string2py(desc.name));
     PyDict_SetItemString
-        (outdict, "description", pystr(desc.description));
+        (outdict, "description", strconv.string2py(desc.description));
     PyDict_SetItemString
-        (outdict, "unit", pystr(desc.unit));
+        (outdict, "unit", strconv.string2py(desc.unit));
     PyDict_SetItemString
         (outdict, "hasFixedBinCount", PyLong_FromLong(desc.hasFixedBinCount));
     if (desc.hasFixedBinCount) {
@@ -253,11 +235,17 @@
     PyPluginObject *pd = getPluginObject(self);
     if (!pd) return 0;
 
-    int n = -1;
+    ssize_t n = -1;
     PyObject *pyId = 0;
     
     if (!PyArg_ParseTuple(args, "n", &n) &&
-        !PyArg_ParseTuple(args, "S", &pyId)) {
+        !PyArg_ParseTuple(args,
+#if (PY_MAJOR_VERSION >= 3)
+                          "U",
+#else
+                          "S",
+#endif
+                          &pyId)) {
         PyErr_SetString(PyExc_TypeError,
                         "get_output takes either output id (string) or output index (int) argument");
         return 0;
@@ -267,8 +255,10 @@
     
     Plugin::OutputList ol = pd->plugin->getOutputDescriptors();
 
+    StringConversion strconv;
+    
     if (pyId) {
-        string id = py2str(pyId);
+        string id = strconv.py2string(pyId);
         for (int i = 0; i < int(ol.size()); ++i) {
             if (ol[i].identifier == id) {
                 return convertOutput(ol[i], i);
@@ -304,12 +294,12 @@
 static PyObject *
 initialise(PyObject *self, PyObject *args)
 {
-    size_t channels, blockSize, stepSize;
+    ssize_t channels, blockSize, stepSize;
 
     if (!PyArg_ParseTuple (args, "nnn",
-                           (size_t) &channels,
-                           (size_t) &stepSize,
-                           (size_t) &blockSize)) {
+                           &channels,
+                           &stepSize,
+                           &blockSize)) {
         PyErr_SetString(PyExc_TypeError,
                         "initialise() takes channel count, step size, and block size arguments");
         return 0;
@@ -367,7 +357,13 @@
 {
     PyObject *pyParam;
 
-    if (!PyArg_ParseTuple(args, "S", &pyParam)) {
+    if (!PyArg_ParseTuple(args,
+#if (PY_MAJOR_VERSION >= 3)
+                          "U",
+#else
+                          "S",
+#endif
+                          &pyParam)) {
         PyErr_SetString(PyExc_TypeError,
                         "get_parameter_value() takes parameter id (string) argument");
         return 0; }
@@ -375,7 +371,9 @@
     PyPluginObject *pd = getPluginObject(self);
     if (!pd) return 0;
 
-    string param = py2str(pyParam);
+    StringConversion strconv;
+    
+    string param = strconv.py2string(pyParam);
     
     if (!hasParameter(pd, param)) {
         PyErr_SetString(PyExc_Exception,
@@ -393,7 +391,13 @@
     PyObject *pyParam;
     float value;
 
-    if (!PyArg_ParseTuple(args, "Sf", &pyParam, &value)) {
+    if (!PyArg_ParseTuple(args,
+#if (PY_MAJOR_VERSION >= 3)
+                          "Uf",
+#else
+                          "Sf",
+#endif
+                          &pyParam, &value)) {
         PyErr_SetString(PyExc_TypeError,
                         "set_parameter_value() takes parameter id (string), and value (float) arguments");
         return 0; }
@@ -401,7 +405,9 @@
     PyPluginObject *pd = getPluginObject(self);
     if (!pd) return 0;
 
-    string param = py2str(pyParam);
+    StringConversion strconv;
+    
+    string param = strconv.py2string(pyParam);
     
     if (!hasParameter(pd, param)) {
         PyErr_SetString(PyExc_Exception,
@@ -454,7 +460,8 @@
                             "Parameter dict values must be convertible to float");
             return 0;
         }
-        string param = py2str(key);
+        StringConversion strconv;
+        string param = strconv.py2string(key);
         if (paramIds.find(param) == paramIds.end()) {
             PyErr_SetString(PyExc_Exception,
                             (string("Unknown parameter id \"") + param + "\"").c_str());
@@ -471,7 +478,13 @@
 {
     PyObject *pyParam;
 
-    if (!PyArg_ParseTuple(args, "S", &pyParam)) {
+    if (!PyArg_ParseTuple(args,
+#if (PY_MAJOR_VERSION >= 3)
+                          "U",
+#else
+                          "S",
+#endif
+                          &pyParam)) {
         PyErr_SetString(PyExc_TypeError,
                         "select_program() takes parameter id (string) argument");
         return 0; }
@@ -479,7 +492,9 @@
     PyPluginObject *pd = getPluginObject(self);
     if (!pd) return 0;
 
-    pd->plugin->selectProgram(py2str(pyParam));
+    StringConversion strconv;
+    
+    pd->plugin->selectProgram(strconv.py2string(pyParam));
     return Py_True;
 }
 
@@ -515,8 +530,10 @@
                         (pyF, "duration", PyRealTime_FromRealTime(f.duration));
                 }
 
+                StringConversion strconv;
+    
                 PyDict_SetItemString
-                    (pyF, "label", pystr(f.label));
+                    (pyF, "label", strconv.string2py(f.label));
 
                 if (!f.values.empty()) {
                     PyDict_SetItemString
@@ -764,8 +781,7 @@
 /* Doc:: 10.3 Type Objects */ /* static */ 
 PyTypeObject Plugin_Type = 
 {
-    PyObject_HEAD_INIT(NULL)
-    0,                                  /*ob_size*/
+    PyVarObject_HEAD_INIT(NULL, 0)
     "vampyhost.Plugin",                 /*tp_name*/
     sizeof(PyPluginObject),             /*tp_basicsize*/
     0,                                  /*tp_itemsize*/
--- a/native/PyRealTime.cpp	Wed Jun 17 11:04:15 2015 +0100
+++ b/native/PyRealTime.cpp	Wed Jun 17 12:35:41 2015 +0100
@@ -182,10 +182,15 @@
 
 /* Object Protocol */
 
+#if (PY_MAJOR_VERSION >= 3)
+#define PyInt_AS_LONG PyLong_AS_LONG
+#define PyInt_FromSsize_t PyLong_FromSsize_t
+#endif
+
 static int
 RealTime_setattr(RealTimeObject *self, char *name, PyObject *value)
 {
-    if ( !string(name).compare("sec")) { 
+    if ( !string(name).compare("sec")) {
         self->rt->sec= (int) PyInt_AS_LONG(value);
         return 0;
     }
@@ -199,8 +204,15 @@
 }
 
 static PyObject *
-RealTime_getattr(RealTimeObject *self, char *name)
+RealTime_getattro(RealTimeObject *self, PyObject *nameobj)
 {
+    string name;
+#if PY_MAJOR_VERSION < 3
+    name = PyString_AsString(nameobj);
+#else
+    name = PyBytes_AsString(PyUnicode_AsUTF8String(nameobj));
+#endif
+        
     if ( !string(name).compare("sec") ) { 
         return PyInt_FromSsize_t(
             (Py_ssize_t) self->rt->sec); 
@@ -211,24 +223,35 @@
             (Py_ssize_t) self->rt->nsec); 
     } 
 
-    return Py_FindMethod(RealTime_methods, 
-                         (PyObject *)self, name);
+    return PyObject_GenericGetAttr((PyObject *)self, nameobj);
 }
 
-static int
-RealTime_compare(PyObject *self, PyObject *other)
+static PyObject *
+RealTime_richcompare(PyObject *self, PyObject *other, int op)
 {
     if (!PyRealTime_Check(self) || !PyRealTime_Check(other)) {
         PyErr_SetString(PyExc_TypeError, "RealTime Object Expected.");
-        return -1;
+        return Py_False;
     }
 
-    RealTime *rt1 = PyRealTime_AS_REALTIME(self);
-    RealTime *rt2 = PyRealTime_AS_REALTIME(other);
+    RealTime *a = PyRealTime_AS_REALTIME(self);
+    RealTime *b = PyRealTime_AS_REALTIME(other);
 
-    if (*rt1 == *rt2) return 0;
-    else if (*rt1 > *rt2) return 1;
-    else return -1;
+    if (op == Py_LT) {
+        return (a < b) ? Py_True : Py_False;
+    } else if (op == Py_LE) {
+        return (a <= b) ? Py_True : Py_False;
+    } else if (op == Py_EQ) {
+        return (a == b) ? Py_True : Py_False;
+    } else if (op == Py_NE) {
+        return (a != b) ? Py_True : Py_False;
+    } else if (op == Py_GT) {
+        return (a > b) ? Py_True : Py_False;
+    } else if (op == Py_GE) {
+        return (a >= b) ? Py_True : Py_False;
+    } else {
+        return Py_False;
+    }
 }
 
 /* String representation called by e.g. str(realtime), print realtime*/
@@ -269,10 +292,12 @@
 
 static PyNumberMethods realtime_as_number = 
 {
-    RealTime_add,                       /*nb_add*/
-    RealTime_subtract,          /*nb_subtract*/
+    (binaryfunc)RealTime_add,                       /*nb_add*/
+    (binaryfunc)RealTime_subtract,          /*nb_subtract*/
     0,                                          /*nb_multiply*/
+#if (PY_MAJOR_VERSION < 3)
     0,                                          /*nb_divide*/
+#endif
     0,                                          /*nb_remainder*/
     0,                      /*nb_divmod*/
     0,                          /*nb_power*/
@@ -286,12 +311,12 @@
     0,                                  /*nb_and*/
     0,                                  /*nb_xor*/
     0,                                  /*nb_or*/
+#if (PY_MAJOR_VERSION < 3)
     0,                      /*nb_coerce*/
+#endif
     0,                                          /*nb_int*/
     0,                                  /*nb_long*/
     (unaryfunc)RealTime_float,/*nb_float*/
-    0,                          /*nb_oct*/
-    0,                          /*nb_hex*/
 };
 
 /* REAL-TIME TYPE OBJECT */
@@ -302,17 +327,16 @@
 /* Doc:: 10.3 Type Objects */ /* static */ 
 PyTypeObject RealTime_Type = 
 {
-    PyObject_HEAD_INIT(NULL)
-    0,                          /*ob_size*/
+    PyVarObject_HEAD_INIT(NULL, 0)
     "vampy.RealTime",           /*tp_name*/
     sizeof(RealTimeObject),     /*tp_basicsize*/
     0,                          /*tp_itemsize*/
     /*          methods         */
     (destructor)RealTimeObject_dealloc, /*tp_dealloc*/
     0,                                  /*tp_print*/
-    (getattrfunc)RealTime_getattr, /*tp_getattr*/
+    0, /*tp_getattr*/
     (setattrfunc)RealTime_setattr, /*tp_setattr*/
-    (cmpfunc)RealTime_compare,     /*tp_compare*/
+    0,     /*tp_compare*/
     RealTime_repr,                 /*tp_repr*/
     &realtime_as_number,        /*tp_as_number*/
     0,                          /*tp_as_sequence*/
@@ -320,14 +344,14 @@
     0,                          /*tp_hash*/
     0,                      /*tp_call*/
     0,                      /*tp_str*/
-    0,                      /*tp_getattro*/
+    (getattrofunc)RealTime_getattro,                      /*tp_getattro*/
     0,                      /*tp_setattro*/
     0,                      /*tp_as_buffer*/
     Py_TPFLAGS_DEFAULT,     /*tp_flags*/
     "RealTime object, used for Vamp plugin timestamps.",      /*tp_doc*/
     0,                      /*tp_traverse*/
     0,                      /*tp_clear*/
-    0,                      /*tp_richcompare*/
+    (richcmpfunc)RealTime_richcompare,                      /*tp_richcompare*/
     0,                      /*tp_weaklistoffset*/
     0,                      /*tp_iter*/
     0,                      /*tp_iternext*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/StringConversion.h	Wed Jun 17 12:35:41 2015 +0100
@@ -0,0 +1,80 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+  VampyHost
+
+  Use Vamp audio analysis plugins in Python
+
+  Gyorgy Fazekas and Chris Cannam
+  Centre for Digital Music, Queen Mary, University of London
+  Copyright 2008-2014 Queen Mary, University of London
+  
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation
+  files (the "Software"), to deal in the Software without
+  restriction, including without limitation the rights to use, copy,
+  modify, merge, publish, distribute, sublicense, and/or sell copies
+  of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+  ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+  Except as contained in this notice, the names of the Centre for
+  Digital Music; Queen Mary, University of London; and the authors
+  shall not be used in advertising or otherwise to promote the sale,
+  use or other dealings in this Software without prior written
+  authorization.
+*/
+
+/*
+  StringConversion: A couple of type safe conversion utilities between
+  Python types and C++ strings.
+*/
+
+#ifndef VAMPYHOST_STRING_CONVERSION_H
+#define VAMPYHOST_STRING_CONVERSION_H
+
+#include <Python.h>
+#include <string>
+
+class StringConversion
+{
+public:
+    StringConversion() {}
+    ~StringConversion() {}
+
+    PyObject *string2py(const std::string &s) {
+#if PY_MAJOR_VERSION < 3
+	return PyString_FromString(s.c_str());
+#else
+	return PyUnicode_FromString(s.c_str());
+#endif
+    }
+
+    std::string py2string(PyObject *obj) {
+#if PY_MAJOR_VERSION < 3
+	char *cstr = PyString_AsString(obj);
+	if (!cstr) return std::string();
+	else return cstr;
+#else
+	PyObject *uobj = PyUnicode_AsUTF8String(obj);
+	if (!uobj) return std::string();
+	char *cstr = PyBytes_AsString(uobj);
+	if (!cstr) return std::string();
+	else return cstr;
+#endif
+    }
+};
+
+#endif
+
+
--- a/native/VectorConversion.cpp	Wed Jun 17 11:04:15 2015 +0100
+++ b/native/VectorConversion.cpp	Wed Jun 17 12:35:41 2015 +0100
@@ -37,8 +37,9 @@
 
 #include <Python.h>
 
+#include "VectorConversion.h"
 #include "FloatConversion.h"
-#include "VectorConversion.h"
+#include "StringConversion.h"
 
 #include <math.h>
 #include <float.h>
@@ -237,7 +238,7 @@
 {
     PyObject *pyList = PyList_New(v.size());
     for (size_t i = 0; i < v.size(); ++i) {
-        PyObject *pyStr = PyString_FromString(v[i].c_str());
+        PyObject *pyStr = StringConversion().string2py(v[i].c_str());
         PyList_SET_ITEM(pyList, i, pyStr);
     }
     return pyList;
@@ -289,23 +290,20 @@
 VectorConversion::PyValue_Get_TypeName(PyObject* pyValue) const
 {
     PyObject *pyType = PyObject_Type(pyValue);
-    if (!pyType) 
-    {
+    if (!pyType) {
         cerr << "Warning: Object type name could not be found." << endl;
         if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
         return string ("< unknown type >");
     }
     PyObject *pyString = PyObject_Str(pyType);
-    if (!pyString)
-    {
+    if (!pyString) {
         cerr << "Warning: Object type name could not be found." << endl;
         if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
         Py_CLEAR(pyType);
         return string ("< unknown type >");
     }
-    char *cstr = PyString_AS_STRING(pyString);
-    if (!cstr)
-    {
+    string str = StringConversion().py2string(pyString);
+    if (str == "") {
         cerr << "Warning: Object type name could not be found." << endl;
         if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
         Py_DECREF(pyType);
@@ -314,5 +312,5 @@
     }
     Py_DECREF(pyType);
     Py_DECREF(pyString);
-    return string(cstr);
+    return str;
 }
--- a/native/vampyhost.cpp	Wed Jun 17 11:04:15 2015 +0100
+++ b/native/vampyhost.cpp	Wed Jun 17 12:35:41 2015 +0100
@@ -52,6 +52,7 @@
 #include "vamp-hostsdk/PluginLoader.h"
 
 #include "VectorConversion.h"
+#include "StringConversion.h"
 #include "PyRealTime.h"
 
 #include <iostream>
@@ -83,7 +84,7 @@
 static string toPluginKey(PyObject *pyPluginKey)
 {
     // convert to stl string
-    string pluginKey(PyString_AS_STRING(pyPluginKey));
+    string pluginKey(StringConversion().py2string(pyPluginKey));
 
     // check pluginKey validity
     string::size_type ki = pluginKey.find(':');
@@ -101,7 +102,13 @@
 {
     PyObject *pyPluginKey;
 
-    if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
+    if (!PyArg_ParseTuple(args,
+#if (PY_MAJOR_VERSION >= 3)
+                          "U",
+#else
+                          "S",
+#endif
+                          &pyPluginKey)) {
         PyErr_SetString(PyExc_TypeError,
                         "get_library_for() takes plugin key (string) argument");
         return 0; }
@@ -111,7 +118,7 @@
     
     PluginLoader *loader = PluginLoader::getInstance();
     string path = loader->getLibraryPathForPlugin(pluginKey);
-    PyObject *pyPath = PyString_FromString(path.c_str());
+    PyObject *pyPath = StringConversion().string2py(path.c_str());
     return pyPath;
 }
 
@@ -120,7 +127,13 @@
 {
     PyObject *pyPluginKey;
 
-    if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
+    if (!PyArg_ParseTuple(args,
+#if (PY_MAJOR_VERSION >= 3)
+                          "U",
+#else
+                          "S",
+#endif
+                          &pyPluginKey)) {
         PyErr_SetString(PyExc_TypeError,
                         "get_category_of() takes plugin key (string) argument");
         return 0; }
@@ -141,7 +154,13 @@
 {
     PyObject *pyPluginKey;
 
-    if (!PyArg_ParseTuple(args, "S", &pyPluginKey)) {
+    if (!PyArg_ParseTuple(args,
+#if (PY_MAJOR_VERSION >= 3)
+                          "U",
+#else
+                          "S",
+#endif
+                          &pyPluginKey)) {
         PyErr_SetString(PyExc_TypeError,
                         "get_outputs_of() takes plugin key (string) argument");
         return 0; }
@@ -166,7 +185,7 @@
 
     for (size_t i = 0; i < outputs.size(); ++i) {
         PyObject *pyOutputId =
-            PyString_FromString(outputs[i].identifier.c_str());
+            StringConversion().string2py(outputs[i].identifier.c_str());
         PyList_SET_ITEM(pyList, i, pyOutputId);
     }
 
@@ -178,9 +197,14 @@
 {
     PyObject *pyPluginKey;
     float inputSampleRate;
-    int adapterFlags;
+    ssize_t adapterFlags;
 
-    if (!PyArg_ParseTuple(args, "Sfn",
+    if (!PyArg_ParseTuple(args,
+#if (PY_MAJOR_VERSION >= 3)
+                          "Ufn",
+#else
+                          "Sfn",
+#endif
                           &pyPluginKey,
                           &inputSampleRate,
                           &adapterFlags)) {
@@ -208,14 +232,14 @@
 static PyObject *
 frame_to_realtime(PyObject *self, PyObject *args)
 {
-    int frame;
-    int rate;
+    ssize_t frame;
+    float rate;
 
-    if (!PyArg_ParseTuple(args, "nn",
+    if (!PyArg_ParseTuple(args, "nf",
                           &frame,
                           &rate)) {
         PyErr_SetString(PyExc_TypeError,
-                        "frame_to_realtime() takes frame (int) and sample rate (int) arguments");
+                        "frame_to_realtime() takes frame (int) and sample rate (float) arguments");
         return 0; }
 
     RealTime rt = RealTime::frame2RealTime(frame, rate);
@@ -249,14 +273,16 @@
     {0, 0}              /* sentinel */
 };
 
-PyDoc_STRVAR(module_doc, "Load and run Vamp audio analysis plugins.");
-
 static int
 setint(PyObject *d, const char *name, int value)
 {
     PyObject *v;
     int err;
+#if (PY_MAJOR_VERSION >= 3)
+    v = PyLong_FromLong((long)value);
+#else
     v = PyInt_FromLong((long)value);
+#endif
     err = PyDict_SetItemString(d, name, v);
     Py_XDECREF(v);
     return err;
@@ -264,19 +290,49 @@
 
 /* Initialization function for the module (*must* be called initxx) */
 
+#if (PY_MAJOR_VERSION >= 3)
+static struct PyModuleDef vampyhostdef = {
+    PyModuleDef_HEAD_INIT,
+    "vampyhost",
+    "Load and run Vamp audio analysis plugins.",
+    -1,
+    vampyhost_methods,
+    0, 0, 0, 0
+};
+#else
+PyDoc_STRVAR(module_doc, "Load and run Vamp audio analysis plugins.");
+#endif
+
 // module initialization (includes extern C {...} as necessary)
 PyMODINIT_FUNC
+#if (PY_MAJOR_VERSION >= 3)
+PyInit_vampyhost(void)
+#else
 initvampyhost(void)
+#endif
 {
     PyObject *m;
 
-    if (PyType_Ready(&RealTime_Type) < 0) return;
-    if (PyType_Ready(&Plugin_Type) < 0) return;
+#if (PY_MAJOR_VERSION >= 3)
+#define GOOD_RETURN m
+#define BAD_RETURN 0
+#else
+#define GOOD_RETURN
+#define BAD_RETURN
+#endif
+    
+    if (PyType_Ready(&RealTime_Type) < 0) return BAD_RETURN;
+    if (PyType_Ready(&Plugin_Type) < 0) return BAD_RETURN;
 
+#if (PY_MAJOR_VERSION >= 3)
+    m = PyModule_Create(&vampyhostdef);
+#else
     m = Py_InitModule3("vampyhost", vampyhost_methods, module_doc);
+#endif
+    
     if (!m) {
         cerr << "ERROR: initvampyhost: Failed to initialise module" << endl;
-        return;
+        return BAD_RETURN;
     }
 
     import_array();
@@ -288,7 +344,7 @@
     PyObject *dict = PyModule_GetDict(m);
     if (!dict) {
         cerr << "ERROR: initvampyhost: Failed to obtain module dictionary" << endl;
-        return;
+        return BAD_RETURN;
     }
 
     if (setint(dict, "ONE_SAMPLE_PER_STEP",
@@ -314,6 +370,8 @@
         setint(dict, "ADAPT_ALL",
                PluginLoader::ADAPT_ALL) < 0) {
         cerr << "ERROR: initvampyhost: Failed to add enums to module dictionary" << endl;
-        return;
+        return BAD_RETURN;
     }
+
+    return GOOD_RETURN;
 }
--- a/test/test_collect.py	Wed Jun 17 11:04:15 2015 +0100
+++ b/test/test_collect.py	Wed Jun 17 12:35:41 2015 +0100
@@ -6,7 +6,7 @@
 plugin_key = "vamp-test-plugin:vamp-test-plugin"
 plugin_key_freq = "vamp-test-plugin:vamp-test-plugin-freq"
 
-rate = 44100
+rate = 44100.0
 
 # Throughout this file we have the assumption that the plugin gets run with a
 # blocksize of 1024, and with a step of 1024 for the time-domain version or 512
--- a/test/test_process.py	Wed Jun 17 11:04:15 2015 +0100
+++ b/test/test_process.py	Wed Jun 17 12:35:41 2015 +0100
@@ -22,18 +22,18 @@
 
 def test_process_n():
     buf = input_data(blocksize)
-    results = list(vamp.process(buf, rate, plugin_key, "input-summary"))
+    results = list(vamp.process_audio(buf, rate, plugin_key, "input-summary"))
     assert len(results) == 1
 
 def test_process_freq_n():
     buf = input_data(blocksize)
-    results = list(vamp.process(buf, rate, plugin_key_freq, "input-summary", {}))
+    results = list(vamp.process_audio(buf, rate, plugin_key_freq, "input-summary", {}))
     assert len(results) == 2 # one complete block starting at zero, one half-full
 
 def test_process_default_output():
     # If no output is specified, we should get the first one (instants)
     buf = input_data(blocksize)
-    results = list(vamp.process(buf, rate, plugin_key, "", {}))
+    results = list(vamp.process_audio(buf, rate, plugin_key, "", {}))
     assert len(results) == 10
     for i in range(len(results)):
         expectedTime = vamp.vampyhost.RealTime('seconds', i * 1.5)
@@ -42,7 +42,7 @@
 
 def test_process_summary_param():
     buf = input_data(blocksize * 10)
-    results = list(vamp.process(buf, rate, plugin_key, "input-summary", { "produce_output": 0 }))
+    results = list(vamp.process_audio(buf, rate, plugin_key, "input-summary", { "produce_output": 0 }))
     assert len(results) == 0
 
 def test_process_multi_summary_param():
@@ -52,7 +52,7 @@
 
 def test_process_summary_param_bool():
     buf = input_data(blocksize * 10)
-    results = list(vamp.process(buf, rate, plugin_key, "input-summary", { "produce_output": False }))
+    results = list(vamp.process_audio(buf, rate, plugin_key, "input-summary", { "produce_output": False }))
     assert len(results) == 0
 
 def test_process_multi_summary_param_bool():
@@ -62,7 +62,7 @@
 
 def test_process_summary():
     buf = input_data(blocksize * 10)
-    results = list(vamp.process(buf, rate, plugin_key, "input-summary", {}))
+    results = list(vamp.process_audio(buf, rate, plugin_key, "input-summary", {}))
     assert len(results) == 10
     for i in range(len(results)):
         #
@@ -120,7 +120,7 @@
 
 def test_process_freq_summary():
     buf = input_data(blocksize * 10)
-    results = list(vamp.process(buf, rate, plugin_key_freq, "input-summary", {}))
+    results = list(vamp.process_audio(buf, rate, plugin_key_freq, "input-summary", {}))
     assert len(results) == 20
     for i in range(len(results)):
         #
@@ -171,7 +171,7 @@
 
 def test_process_timestamps():
     buf = input_data(blocksize * 10)
-    results = list(vamp.process(buf, rate, plugin_key, "input-timestamp", {}))
+    results = list(vamp.process_audio(buf, rate, plugin_key, "input-timestamp", {}))
     assert len(results) == 10
     for i in range(len(results)):
         # The timestamp should be the frame number of the first frame in the
@@ -193,7 +193,7 @@
 
 def test_process_freq_timestamps():
     buf = input_data(blocksize * 10)
-    results = list(vamp.process(buf, rate, plugin_key_freq, "input-timestamp", {}))
+    results = list(vamp.process_audio(buf, rate, plugin_key_freq, "input-timestamp", {}))
     assert len(results) == 20
     for i in range(len(results)):
         # The timestamp should be the frame number of the frame just beyond
--- a/vamp/__init__.py	Wed Jun 17 11:04:15 2015 +0100
+++ b/vamp/__init__.py	Wed Jun 17 12:35:41 2015 +0100
@@ -2,7 +2,7 @@
 
 import vampyhost
 
-from load import list_plugins, get_outputs_of, get_category_of
-from process import process, process_frames, process_multiple_outputs, process_frames_multiple_outputs
-from collect import collect
+from vamp.load import list_plugins, get_outputs_of, get_category_of
+from vamp.process import process_audio, process_frames, process_multiple_outputs, process_frames_multiple_outputs
+from vamp.collect import collect
 
--- a/vamp/collect.py	Wed Jun 17 11:04:15 2015 +0100
+++ b/vamp/collect.py	Wed Jun 17 12:35:41 2015 +0100
@@ -1,9 +1,9 @@
 '''A high-level interface to the vampyhost extension module, for quickly and easily running Vamp audio analysis plugins on audio files and buffers.'''
 
 import vampyhost
-import load
-import process
-import frames
+import vamp.load
+import vamp.process
+import vamp.frames
 
 import numpy as np
 
@@ -124,7 +124,7 @@
     vamp.process() instead.
     """
 
-    plugin, step_size, block_size = load.load_and_configure(data, sample_rate, key, parameters)
+    plugin, step_size, block_size = vamp.load.load_and_configure(data, sample_rate, key, parameters)
 
     if output == "":
         output_desc = plugin.get_output(0)
@@ -132,9 +132,9 @@
     else:
         output_desc = plugin.get_output(output)
 
-    ff = frames.frames_from_array(data, step_size, block_size)
+    ff = vamp.frames.frames_from_array(data, step_size, block_size)
 
-    results = process.process_with_initialised_plugin(ff, sample_rate, step_size, plugin, [output])
+    results = vamp.process.process_with_initialised_plugin(ff, sample_rate, step_size, plugin, [output])
 
     rv = reshape(results, sample_rate, step_size, output_desc)
 
--- a/vamp/process.py	Wed Jun 17 11:04:15 2015 +0100
+++ b/vamp/process.py	Wed Jun 17 12:35:41 2015 +0100
@@ -1,8 +1,8 @@
 '''A high-level interface to the vampyhost extension module, for quickly and easily running Vamp audio analysis plugins on audio files and buffers.'''
 
 import vampyhost
-import frames
-import load
+import vamp.frames
+import vamp.load
 
 def process_with_initialised_plugin(ff, sample_rate, step_size, plugin, outputs):
 
@@ -30,7 +30,7 @@
                 yield { o: r }
 
 
-def process(data, sample_rate, key, output = "", parameters = {}):
+def process_audio(data, sample_rate, key, output = "", parameters = {}):
     """Process audio data with a Vamp plugin, and make the results from a
     single plugin output available as a generator.
 
@@ -57,12 +57,12 @@
     structure, consider using vamp.collect() instead.
     """
 
-    plugin, step_size, block_size = load.load_and_configure(data, sample_rate, key, parameters)
+    plugin, step_size, block_size = vamp.load.load_and_configure(data, sample_rate, key, parameters)
 
     if output == "":
         output = plugin.get_output(0)["identifier"]
 
-    ff = frames.frames_from_array(data, step_size, block_size)
+    ff = vamp.frames.frames_from_array(data, step_size, block_size)
 
     for r in process_with_initialised_plugin(ff, sample_rate, step_size, plugin, [output]):
         yield r[output]
@@ -164,9 +164,9 @@
     array of float values.
     """
 
-    plugin, step_size, block_size = load.load_and_configure(data, sample_rate, key, parameters)
+    plugin, step_size, block_size = vamp.load.load_and_configure(data, sample_rate, key, parameters)
 
-    ff = frames.frames_from_array(data, step_size, block_size)
+    ff = vamp.frames.frames_from_array(data, step_size, block_size)
 
     for r in process_with_initialised_plugin(ff, sample_rate, step_size, plugin, outputs):
         yield r