Ticket 735: Add python3 support jenkins-bRopo-49 jenkins-bRopo-50
authorAnders Henja <anders@henjab.se>
Fri, 30 Mar 2018 12:19:31 +0000 (14:19 +0200)
committerAnders Henja <anders@henjab.se>
Fri, 30 Mar 2018 12:19:31 +0000 (14:19 +0200)
pyropo/pyfmiimage.c
pyropo/pyfmiimage.h
pyropo/pyropo_compat.h [new file with mode: 0644]
pyropo/pyropogenerator.c
pyropo/pyropogenerator.h
pyropo/ropo_realtime.py
test/pytest/FmiImageTest.py
test/pytest/PyRopoGeneratorTest.py
test/pytest/ropo_quality_plugin_test.py
tools/run_python_script.sh
tools/test_ropo.sh

index afaffba..cb551a8 100644 (file)
@@ -22,6 +22,7 @@ along with bRopo.  If not, see <http://www.gnu.org/licenses/>.
  * @author Anders Henja (Swedish Meteorological and Hydrological Institute, SMHI)
  * @date 2011-08-31
  */
+#include "pyropo_compat.h"
 #include "Python.h"
 #include <math.h>
 #include <stdio.h>
@@ -456,35 +457,27 @@ static struct PyMethodDef _pyfmiimage_methods[] =
 /**
  * Returns the specified attribute in the fmi image
  */
-static PyObject* _pyfmiimage_getattr(PyFmiImage* self, char* name)
+static PyObject* _pyfmiimage_getattro(PyFmiImage* self, PyObject* name)
 {
-  PyObject* res = NULL;
-  if (strcmp("offset", name) == 0) {
+  if (PY_COMPARE_STRING_WITH_ATTRO_NAME("offset", name) == 0) {
     return PyFloat_FromDouble(RaveFmiImage_getOffset(self->image));
-  } else if (strcmp("gain", name) == 0) {
+  } else if (PY_COMPARE_STRING_WITH_ATTRO_NAME("gain", name) == 0) {
     return PyFloat_FromDouble(RaveFmiImage_getGain(self->image));
   }
-
-  res = Py_FindMethod(_pyfmiimage_methods, (PyObject*) self, name);
-  if (res)
-    return res;
-
-  PyErr_Clear();
-  PyErr_SetString(PyExc_AttributeError, name);
-  return NULL;
+  return PyObject_GenericGetAttr((PyObject*)self, name);
 }
 
 /**
  * Returns the specified attribute in the polar volume
  */
-static int _pyfmiimage_setattr(PyFmiImage* self, char* name, PyObject* val)
+static int _pyfmiimage_setattro(PyFmiImage* self, PyObject* name, PyObject* val)
 {
   int result = -1;
   if (name == NULL) {
     goto done;
   }
 
-  if (strcmp("offset", name) == 0) {
+  if (PY_COMPARE_STRING_WITH_ATTRO_NAME("offset", name) == 0) {
     double v = 0.0;
     if (PyFloat_Check(val)) {
       v = PyFloat_AsDouble(val);
@@ -494,7 +487,7 @@ static int _pyfmiimage_setattr(PyFmiImage* self, char* name, PyObject* val)
       raiseException_gotoTag(done, PyExc_TypeError, "offset is a number");
     }
     RaveFmiImage_setOffset(self->image, v);
-  } else if (strcmp("gain", name) == 0) {
+  } else if (PY_COMPARE_STRING_WITH_ATTRO_NAME("gain", name) == 0) {
     double v = 0.0;
     if (PyFloat_Check(val)) {
       v = PyFloat_AsDouble(val);
@@ -505,7 +498,7 @@ static int _pyfmiimage_setattr(PyFmiImage* self, char* name, PyObject* val)
     }
     RaveFmiImage_setGain(self->image, v);
   } else {
-    raiseException_gotoTag(done, PyExc_AttributeError, name);
+    raiseException_gotoTag(done, PyExc_AttributeError, PY_RAVE_ATTRO_NAME_TO_STRING(name));
   }
 
   result = 0;
@@ -521,22 +514,49 @@ done:
 /*@{ Type definitions */
 PyTypeObject PyFmiImage_Type =
 {
-  PyObject_HEAD_INIT(NULL)0, /*ob_size*/
+  PyVarObject_HEAD_INIT(NULL, 0) /*ob_size*/
   "FmiImageCore", /*tp_name*/
   sizeof(PyFmiImage), /*tp_size*/
   0, /*tp_itemsize*/
   /* methods */
   (destructor)_pyfmiimage_dealloc, /*tp_dealloc*/
   0, /*tp_print*/
-  (getattrfunc)_pyfmiimage_getattr, /*tp_getattr*/
-  (setattrfunc)_pyfmiimage_setattr, /*tp_setattr*/
-  0, /*tp_compare*/
-  0, /*tp_repr*/
-  0, /*tp_as_number */
+  (getattrfunc)0,               /*tp_getattr*/
+  (setattrfunc)0,               /*tp_setattr*/
+  0,                            /*tp_compare*/
+  0,                            /*tp_repr*/
+  0,                            /*tp_as_number */
   0,
-  0, /*tp_as_mapping */
-  0 /*tp_hash*/
+  0,                            /*tp_as_mapping */
+  0,                            /*tp_hash*/
+  (ternaryfunc)0,               /*tp_call*/
+  (reprfunc)0,                  /*tp_str*/
+  (getattrofunc)_pyfmiimage_getattro, /*tp_getattro*/
+  (setattrofunc)_pyfmiimage_setattro, /*tp_setattro*/
+  0,                            /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT, /*tp_flags*/
+  0,                            /*tp_doc*/
+  (traverseproc)0,              /*tp_traverse*/
+  (inquiry)0,                   /*tp_clear*/
+  0,                            /*tp_richcompare*/
+  0,                            /*tp_weaklistoffset*/
+  0,                            /*tp_iter*/
+  0,                            /*tp_iternext*/
+  _pyfmiimage_methods,              /*tp_methods*/
+  0,                            /*tp_members*/
+  0,                            /*tp_getset*/
+  0,                            /*tp_base*/
+  0,                            /*tp_dict*/
+  0,                            /*tp_descr_get*/
+  0,                            /*tp_descr_set*/
+  0,                            /*tp_dictoffset*/
+  0,                            /*tp_init*/
+  0,                            /*tp_alloc*/
+  0,                            /*tp_new*/
+  0,                            /*tp_free*/
+  0,                            /*tp_is_gc*/
 };
+
 /*@} End of Type definitions */
 
 /// --------------------------------------------------------------------
@@ -553,31 +573,33 @@ static PyMethodDef functions[] = {
 /**
  * Initializes polar volume.
  */
-void init_fmiimage(void)
+MOD_INIT(_fmiimage)
 {
   PyObject *module=NULL,*dictionary=NULL;
   static void *PyFmiImage_API[PyFmiImage_API_pointers];
   PyObject *c_api_object = NULL;
-  PyFmiImage_Type.ob_type = &PyType_Type;
 
-  module = Py_InitModule("_fmiimage", functions);
+  MOD_INIT_SETUP_TYPE(PyFmiImage_Type, &PyType_Type);
+
+  MOD_INIT_VERIFY_TYPE_READY(&PyFmiImage_Type);
+
+  MOD_INIT_DEF(module, "_fmiimage", NULL/*doc*/, functions);
   if (module == NULL) {
-    return;
+    return MOD_INIT_ERROR;
   }
+
   PyFmiImage_API[PyFmiImage_Type_NUM] = (void*)&PyFmiImage_Type;
   PyFmiImage_API[PyFmiImage_GetNative_NUM] = (void *)PyFmiImage_GetNative;
   PyFmiImage_API[PyFmiImage_New_NUM] = (void*)PyFmiImage_New;
 
-  c_api_object = PyCObject_FromVoidPtr((void *)PyFmiImage_API, NULL);
-
-  if (c_api_object != NULL) {
-    PyModule_AddObject(module, "_C_API", c_api_object);
-  }
-
+  c_api_object = PyCapsule_New(PyFmiImage_API, PyFmiImage_CAPSULE_NAME, NULL);
   dictionary = PyModule_GetDict(module);
-  ErrorObject = PyString_FromString("_fmiimage.error");
+  PyDict_SetItemString(dictionary, "_C_API", c_api_object);
+
+  ErrorObject = PyErr_NewException("_fmiimage.error", NULL, NULL);
   if (ErrorObject == NULL || PyDict_SetItemString(dictionary, "error", ErrorObject) != 0) {
     Py_FatalError("Can't define _fmiimage.error");
+    return MOD_INIT_ERROR;
   }
 
   import_array();
@@ -585,5 +607,6 @@ void init_fmiimage(void)
   import_pypolarvolume();
   import_pyravefield();
   PYRAVE_DEBUG_INITIALIZE;
+  return MOD_INIT_SUCCESS(module);
 }
 /*@} End of Module setup */
index 7755093..9594eee 100644 (file)
@@ -26,6 +26,7 @@ along with bRopo.  If not, see <http://www.gnu.org/licenses/>.
 #define PYFMIIMAGE_H
 #include "rave_fmi_image.h"
 
+
 /**
  * The fmi image object
  */
@@ -46,7 +47,10 @@ typedef struct {
 
 #define PyFmiImage_API_pointers 3                          /**< number of type and function pointers */
 
+#define PyFmiImage_CAPSULE_NAME "_fmiimage._C_API"
+
 #ifdef PYFMIIMAGE_MODULE
+
 /** Forward declaration of type */
 extern PyTypeObject PyFmiImage_Type;
 
@@ -64,7 +68,7 @@ static PyFmiImage_New_RETURN PyFmiImage_New PyFmiImage_New_PROTO;
 static void **PyFmiImage_API;
 
 /**
- * Returns a pointer to the internal fmi image, remember to release the reference
+ * Returns a pointer to the internal composite, remember to release the reference
  * when done with the object. (RAVE_OBJECT_RELEASE).
  */
 #define PyFmiImage_GetNative \
@@ -86,34 +90,16 @@ static void **PyFmiImage_API;
  * Checks if the object is a python fmi image.
  */
 #define PyFmiImage_Check(op) \
-   ((op)->ob_type == (PyTypeObject *)PyFmiImage_API[PyFmiImage_Type_NUM])
+   (Py_TYPE(op) == &PyFmiImage_Type)
+
+
+#define PyFmiImage_Type (*(PyTypeObject*)PyFmiImage_API[PyFmiImage_Type_NUM])
 
 /**
- * Imports the PyFmiImage module (like import _fmiimage in python).
+ * Imports the PyArea module (like import _area in python).
  */
-static int
-import_fmiimage(void)
-{
-  PyObject *module;
-  PyObject *c_api_object;
-
-  module = PyImport_ImportModule("_fmiimage");
-  if (module == NULL) {
-    return -1;
-  }
-
-  c_api_object = PyObject_GetAttrString(module, "_C_API");
-  if (c_api_object == NULL) {
-    Py_DECREF(module);
-    return -1;
-  }
-  if (PyCObject_Check(c_api_object)) {
-    PyFmiImage_API = (void **)PyCObject_AsVoidPtr(c_api_object);
-  }
-  Py_DECREF(c_api_object);
-  Py_DECREF(module);
-  return 0;
-}
+#define import_fmiimage() \
+    PyFmiImage_API = (void **)PyCapsule_Import(PyFmiImage_CAPSULE_NAME, 1);
 
 #endif
 
diff --git a/pyropo/pyropo_compat.h b/pyropo/pyropo_compat.h
new file mode 100644 (file)
index 0000000..9fdd586
--- /dev/null
@@ -0,0 +1,164 @@
+/* --------------------------------------------------------------------
+Copyright (C) 2016 Swedish Meteorological and Hydrological Institute, SMHI,
+
+This file is part of RAVE.
+
+RAVE is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+RAVE is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with RAVE.  If not, see <http://www.gnu.org/licenses/>.
+------------------------------------------------------------------------*/
+/**
+ * Python compability file for making it possible to compile bropo for both python >2.6 and python3
+ * @file
+ * @author Anders Henja (Swedish Meteorological and Hydrological Institute, SMHI)
+ * @date 2010-03-30
+ */
+
+/* Define this to ensure that we are not using old API:s from Numpy. However, for now we are more interested in getting the
+ * code to build on both 2.7 and 3.x
+ */
+/*
+ * #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+ */
+#include "Python.h"
+
+#ifndef PYROPOCOMPAT_H
+#define PYROPOCOMPAT_H
+
+
+#ifndef PyInt_Check
+#define PyInt_Check             PyLong_Check
+#define PyInt_FromLong          PyLong_FromLong
+#define PyInt_AsLong            PyLong_AsLong
+#define PyInt_Type              PyLong_Type
+#endif
+
+#ifndef PyString_Check
+#define PyString_Check          PyUnicode_Check
+#define PyString_AsString       PyUnicode_AsUTF8
+#define PyString_FromString     PyUnicode_FromString
+#define PyString_FromFormat     PyUnicode_FromFormat
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+#define PY_RAVE_ATTRO_NAME_TO_STRING PyUnicode_AsUTF8
+
+#else
+#define PY_RAVE_ATTRO_NAME_TO_STRING PyString_AsString
+
+#endif
+
+#define PY_COMPARE_ATTRO_NAME_WITH_STRING(ptr, name) PyRopoAPI_CompareWithASCIIString(ptr, name)
+
+#define PY_COMPARE_STRING_WITH_ATTRO_NAME(name, ptr) PyRopoAPI_CompareWithASCIIString(ptr, name)
+
+#if PY_MAJOR_VERSION >= 3
+#define MOD_INIT_ERROR        NULL
+#define MOD_INIT_SUCCESS(val) val
+#define MOD_INIT(name)        PyMODINIT_FUNC PyInit_##name(void)
+#define MOD_INIT_DEF(ob, name, doc, methods) \
+  static struct PyModuleDef moduledef = { \
+    PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \
+    ob = PyModule_Create(&moduledef);
+
+#define MOD_INIT_CREATE_CAPI(ptr, name) PyCapsule_New(ptr, name, NULL)
+#define MOD_INIT_IS_CAPI(ptr) PyCapsule_CheckExact(ptr)
+#define MOD_INIT_GET_CAPI(ptr, name) PyCapsule_GetPointer(ptr, name)
+#define MOD_INIT_SETUP_TYPE(itype, otype) Py_TYPE(&itype) = otype
+#define MOD_INIT_VERIFY_TYPE_READY(type) if (PyType_Ready(type) < 0) return MOD_INIT_ERROR
+
+#else
+#define MOD_INIT_ERROR
+#define MOD_INIT_SUCCESS(val)
+#define MOD_INIT(name) void init##name(void)
+#define MOD_INIT_DEF(ob, name, doc, methods) \
+  ob = Py_InitModule3(name, methods, doc);
+#define MOD_INIT_CREATE_CAPI(ptr, name) PyCObject_FromVoidPtr(ptr, name)
+#define MOD_INIT_IS_CAPI(ptr) PyCObject_Check(ptr)
+#define MOD_INIT_GET_CAPI(ptr, name) PyCObject_AsVoidPtr(ptr)
+#define MOD_INIT_SETUP_TYPE(itype, otype) itype.ob_type = otype
+#define MOD_INIT_VERIFY_TYPE_READY(type)
+#endif
+
+/**
+ * Macros that can be used to simplify way of generating __dir__ content that is necessary for executing dir() on an object
+ */
+#define MOD_DIR_FORWARD_DECLARE(name) static PyObject* _##name##__dir__(name *self)
+
+#define MOD_DIR_REFERENCE(name) _##name##__dir__
+
+#define MOD_DIR_APPEND(list, str)                               \
+  do {                                            \
+    PyObject *o = PyUnicode_FromString(str);        \
+    if (o != NULL)                          \
+      PyList_Append(list, o);         \
+    Py_XDECREF(o);                          \
+  } while (0)
+
+#define MOD_DIR_FUNCTION(name, method_listing) static PyObject * _##name##__dir__(name *self) { \
+  int i=0; \
+  PyObject* rc = PyList_New(0); \
+  if (!rc) \
+    return NULL; \
+\
+  while (method_listing[i].ml_name != NULL) { \
+    MOD_DIR_APPEND(rc, method_listing[i++].ml_name); \
+  } \
+\
+  return rc; \
+}
+
+/**
+ * Tests if a python object string (or unicode) is equal to name.
+ * @param[in] ptr - the python string or unicode
+ * @param[in] name - the string to compare with
+ * @return 0, -1 or 1
+ */
+static int PyRopoAPI_CompareWithASCIIString(PyObject* ptr, const char* name) {
+  int result = -1;
+  if (!PyString_Check(ptr)){
+#ifdef Py_USING_UNICODE
+    if (PyUnicode_Check(ptr)) {
+      PyObject* tmp = PyUnicode_AsEncodedString(ptr, NULL, NULL);
+      if (tmp != NULL) {
+        result = strcmp(PyString_AsString(tmp), name);
+        Py_DecRef(tmp);
+      }
+    }
+#endif
+  } else {
+       result = strcmp(PyString_AsString(ptr), name);
+  }
+  return result;
+}
+
+/**
+ * Creates a string compatible with the current python interpreeter.
+ */
+/*
+static PyObject* PyRopoAPI_StringOrUnicode_FromASCII(const char *buffer) {
+#if PY_MAJOR_VERSION >= 3
+  Py_ssize_t size = (Py_ssize_t)strlen(buffer);
+  const unsigned char *s = (const unsigned char *)buffer;
+  PyObject *unicode = NULL;
+  unicode = PyUnicode_New(size, 127);
+  if (!unicode) {
+    return NULL;
+  }
+  memcpy(PyUnicode_1BYTE_DATA(unicode), s, size);
+  return unicode;
+#else
+  return PyString_FromString(buffer);
+#endif
+}
+*/
+#endif
index c91cb29..fe29658 100644 (file)
@@ -22,6 +22,7 @@ along with bRopo.  If not, see <http://www.gnu.org/licenses/>.
  * @author Anders Henja (Swedish Meteorological and Hydrological Institute, SMHI)
  * @date 2011-08-31
  */
+#include "pyropo_compat.h"
 #include "Python.h"
 #include <math.h>
 #include <stdio.h>
@@ -586,11 +587,11 @@ static struct PyMethodDef _pyropogenerator_methods[] =
 /**
  * Returns the specified attribute in the fmi image
  */
-static PyObject* _pyropogenerator_getattr(PyRopoGenerator* self, char* name)
+static PyObject* _pyropogenerator_getattro(PyRopoGenerator* self, PyObject* name)
 {
   PyObject* res = NULL;
 
-  if (strcmp("classification", name) == 0) {
+  if (PY_COMPARE_STRING_WITH_ATTRO_NAME("classification", name) == 0) {
        RaveFmiImage_t* image = NULL;
     image = RaveRopoGenerator_getClassification(self->generator);
     if (image == NULL) {
@@ -599,7 +600,7 @@ static PyObject* _pyropogenerator_getattr(PyRopoGenerator* self, char* name)
     res = (PyObject*)PyFmiImage_New(image,0,0);
     RAVE_OBJECT_RELEASE(image);
     return res;
-  } else if (strcmp("markers", name) == 0) {
+  } else if (PY_COMPARE_STRING_WITH_ATTRO_NAME("markers", name) == 0) {
        RaveFmiImage_t* image = NULL;
        image = RaveRopoGenerator_getMarkers(self->generator);
        if (image == NULL) {
@@ -609,27 +610,20 @@ static PyObject* _pyropogenerator_getattr(PyRopoGenerator* self, char* name)
        RAVE_OBJECT_RELEASE(image);
        return res;
   }
-
-  res = Py_FindMethod(_pyropogenerator_methods, (PyObject*) self, name);
-  if (res)
-    return res;
-
-  PyErr_Clear();
-  PyErr_SetString(PyExc_AttributeError, name);
-  return NULL;
+  return PyObject_GenericGetAttr((PyObject*)self, name);
 }
 
 /**
  * Returns the specified attribute in the polar volume
  */
-static int _pyropogenerator_setattr(PyRopoGenerator* self, char* name, PyObject* val)
+static int _pyropogenerator_setattro(PyRopoGenerator* self, PyObject* name, PyObject* val)
 {
   int result = -1;
   if (name == NULL) {
     goto done;
   }
 
-  raiseException_gotoTag(done, PyExc_AttributeError, name);
+  raiseException_gotoTag(done, PyExc_AttributeError, PY_RAVE_ATTRO_NAME_TO_STRING(name));
 
   result = 0;
 done:
@@ -644,21 +638,47 @@ done:
 /*@{ Type definitions */
 PyTypeObject PyRopoGenerator_Type =
 {
-  PyObject_HEAD_INIT(NULL)0, /*ob_size*/
+  PyVarObject_HEAD_INIT(NULL, 0) /*ob_size*/
   "RopoGeneratorCore", /*tp_name*/
   sizeof(PyRopoGenerator), /*tp_size*/
   0, /*tp_itemsize*/
   /* methods */
   (destructor)_pyropogenerator_dealloc, /*tp_dealloc*/
   0, /*tp_print*/
-  (getattrfunc)_pyropogenerator_getattr, /*tp_getattr*/
-  (setattrfunc)_pyropogenerator_setattr, /*tp_setattr*/
-  0, /*tp_compare*/
-  0, /*tp_repr*/
-  0, /*tp_as_number */
+  (getattrfunc)0,               /*tp_getattr*/
+  (setattrfunc)0,               /*tp_setattr*/
+  0,                            /*tp_compare*/
+  0,                            /*tp_repr*/
+  0,                            /*tp_as_number */
   0,
-  0, /*tp_as_mapping */
-  0 /*tp_hash*/
+  0,                            /*tp_as_mapping */
+  0,                            /*tp_hash*/
+  (ternaryfunc)0,               /*tp_call*/
+  (reprfunc)0,                  /*tp_str*/
+  (getattrofunc)_pyropogenerator_getattro, /*tp_getattro*/
+  (setattrofunc)_pyropogenerator_setattro, /*tp_setattro*/
+  0,                            /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT, /*tp_flags*/
+  0,                            /*tp_doc*/
+  (traverseproc)0,              /*tp_traverse*/
+  (inquiry)0,                   /*tp_clear*/
+  0,                            /*tp_richcompare*/
+  0,                            /*tp_weaklistoffset*/
+  0,                            /*tp_iter*/
+  0,                            /*tp_iternext*/
+  _pyropogenerator_methods,              /*tp_methods*/
+  0,                            /*tp_members*/
+  0,                            /*tp_getset*/
+  0,                            /*tp_base*/
+  0,                            /*tp_dict*/
+  0,                            /*tp_descr_get*/
+  0,                            /*tp_descr_set*/
+  0,                            /*tp_dictoffset*/
+  0,                            /*tp_init*/
+  0,                            /*tp_alloc*/
+  0,                            /*tp_new*/
+  0,                            /*tp_free*/
+  0,                            /*tp_is_gc*/
 };
 /*@} End of Type definitions */
 
@@ -890,35 +910,38 @@ static PyMethodDef functions[] = {
   {NULL,NULL} /*Sentinel*/
 };
 
-PyMODINIT_FUNC
-init_ropogenerator(void)
+MOD_INIT(_ropogenerator)
 {
   PyObject *module=NULL,*dictionary=NULL;
   static void *PyRopoGenerator_API[PyRopoGenerator_API_pointers];
   PyObject *c_api_object = NULL;
-  PyRopoGenerator_Type.ob_type = &PyType_Type;
 
-  module = Py_InitModule("_ropogenerator", functions);
+  MOD_INIT_SETUP_TYPE(PyRopoGenerator_Type, &PyType_Type);
+
+  MOD_INIT_VERIFY_TYPE_READY(&PyRopoGenerator_Type);
+
+  MOD_INIT_DEF(module, "_ropogenerator", NULL/*doc*/, functions);
   if (module == NULL) {
-    return;
+    return MOD_INIT_ERROR;
   }
+
   PyRopoGenerator_API[PyRopoGenerator_Type_NUM] = (void*)&PyRopoGenerator_Type;
   PyRopoGenerator_API[PyRopoGenerator_GetNative_NUM] = (void *)PyRopoGenerator_GetNative;
   PyRopoGenerator_API[PyRopoGenerator_New_NUM] = (void*)PyRopoGenerator_New;
 
-  c_api_object = PyCObject_FromVoidPtr((void *)PyRopoGenerator_API, NULL);
-
-  if (c_api_object != NULL) {
-    PyModule_AddObject(module, "_C_API", c_api_object);
-  }
 
+  c_api_object = PyCapsule_New(PyRopoGenerator_API, PyRopoGenerator_CAPSULE_NAME, NULL);
   dictionary = PyModule_GetDict(module);
-  ErrorObject = PyString_FromString("_ropogenerator.error");
+  PyDict_SetItemString(dictionary, "_C_API", c_api_object);
 
+  ErrorObject = PyErr_NewException("_ropogenerator.error", NULL, NULL);
   if (ErrorObject == NULL || PyDict_SetItemString(dictionary, "error", ErrorObject) != 0) {
     Py_FatalError("Can't define _ropogenerator.error");
+    return MOD_INIT_ERROR;
   }
+
   import_fmiimage();
   PYRAVE_DEBUG_INITIALIZE;
+  return MOD_INIT_SUCCESS(module);
 }
 /*@} End of Module setup */
index fb7c233..30ede8a 100644 (file)
@@ -46,6 +46,8 @@ typedef struct {
 
 #define PyRopoGenerator_API_pointers 3                          /**< number of type and function pointers */
 
+#define PyRopoGenerator_CAPSULE_NAME "_fmiimage._C_API"
+
 #ifdef PYROPOGENERATOR_MODULE
 /** Forward declaration of type */
 extern PyTypeObject PyRopoGenerator_Type;
@@ -85,34 +87,15 @@ static void **PyRopoGenerator_API;
  * Checks if the object is a python fmi image.
  */
 #define PyRopoGenerator_Check(op) \
-   ((op)->ob_type == (PyTypeObject *)PyRopoGenerator_API[PyRopoGenerator_Type_NUM])
+  (Py_TYPE(op) == &PyRopoGenerator_Type)
+
+#define PyRopoGenerator_Type (*(PyTypeObject*)PyRopoGenerator_API[PyRopoGenerator_Type_NUM])
 
 /**
  * Imports the PyRopoGenerator module (like import _ropogenerator in python).
  */
-static int
-import_ropogenerator(void)
-{
-  PyObject *module;
-  PyObject *c_api_object;
-
-  module = PyImport_ImportModule("_ropogenerator");
-  if (module == NULL) {
-    return -1;
-  }
-
-  c_api_object = PyObject_GetAttrString(module, "_C_API");
-  if (c_api_object == NULL) {
-    Py_DECREF(module);
-    return -1;
-  }
-  if (PyCObject_Check(c_api_object)) {
-    PyRopoGenerator_API = (void **)PyCObject_AsVoidPtr(c_api_object);
-  }
-  Py_DECREF(c_api_object);
-  Py_DECREF(module);
-  return 0;
-}
+#define import_ropogenerator() \
+               PyRopoGenerator_API = (void **)PyCapsule_Import(PyRopoGenerator_CAPSULE_NAME, 1);
 
 #endif
 
index d06e145..a2a02c9 100644 (file)
@@ -217,7 +217,7 @@ def process_pvol(pvol, options, quality_control_mode=QUALITY_CONTROL_MODE_ANALYZ
 # @return SCAN or PVOL object, with anomalies hopefully identified and removed
 def generate(inobj, reprocess_quality_flag=True, quality_control_mode=QUALITY_CONTROL_MODE_ANALYZE_AND_APPLY):
     if _polarscan.isPolarScan(inobj) == False and _polarvolume.isPolarVolume(inobj) == False:
-      raise IOError, "Input file must be either polar scan or volume."
+      raise IOError("Input file must be either polar scan or volume.")
 
     if reprocess_quality_flag == False:
       if _polarscan.isPolarScan(inobj) and inobj.findQualityFieldByHowTask("fi.fmi.ropo.detector.classification"):
index ac7d727..4dccfc4 100644 (file)
@@ -33,45 +33,45 @@ class FmiImageTest(unittest.TestCase):
   def testAttributes(self):
     a = _fmiimage.new()
     names = a.getAttributeNames()
-    self.assertEquals(0, len(names))
+    self.assertEqual(0, len(names))
     a.addAttribute("how/slask", 10)
-    self.assertEquals(10, a.getAttribute("how/slask"))
+    self.assertEqual(10, a.getAttribute("how/slask"))
     names = a.getAttributeNames()
-    self.assertEquals(1, len(names))
+    self.assertEqual(1, len(names))
     self.assertTrue("how/slask" in names)
 
   def testFromRave_scan(self):
     a = _raveio.open(self.PVOL_TESTFILE)
     scan = a.object.getScan(0)
     b = _fmiimage.fromRave(scan)
-    self.assertNotEqual(-1, string.find(`type(b)`, "FmiImageCore"))
+    self.assertNotEqual(-1, str(type(b)).find("FmiImageCore"))
     self.assertAlmostEqual(scan.getParameter("DBZH").offset, b.offset, 4)
     self.assertAlmostEqual(scan.getParameter("DBZH").gain, b.gain, 4)
 
   def testFromRave_withQuantity_scan(self):
     a = _raveio.open(self.PVOL_TESTFILE)
     b = _fmiimage.fromRave(a.object.getScan(0), "DBZH")
-    self.assertNotEqual(-1, string.find(`type(b)`, "FmiImageCore"))
+    self.assertNotEqual(-1, str(type(b)).find("FmiImageCore"))
 
   def testFromRave_volume(self):
     a = _raveio.open(self.PVOL_TESTFILE)
     b = _fmiimage.fromRave(a.object)
-    self.assertNotEqual(-1, string.find(`type(b)`, "FmiImageCore"))
+    self.assertNotEqual(-1, str(type(b)).find("FmiImageCore"))
 
   def testFromRave_withQuantity_volume(self):
     a = _raveio.open(self.PVOL_TESTFILE)
     b = _fmiimage.fromRave(a.object, "DBZH")
-    self.assertNotEqual(-1, string.find(`type(b)`, "FmiImageCore"))
+    self.assertNotEqual(-1, str(type(b)).find("FmiImageCore"))
 
   def testFromRaveVolume(self):
     a = _raveio.open(self.PVOL_TESTFILE)
     b = _fmiimage.fromRaveVolume(a.object, 0)
-    self.assertNotEqual(-1, string.find(`type(b)`, "FmiImageCore"))
+    self.assertNotEqual(-1, str(type(b)).find("FmiImageCore"))
 
   def testFromRaveVolume_withQuantity(self):
     a = _raveio.open(self.PVOL_TESTFILE)
     b = _fmiimage.fromRaveVolume(a.object, 0, "DBZH")
-    self.assertNotEqual(-1, string.find(`type(b)`, "FmiImageCore"))
+    self.assertNotEqual(-1, str(type(b)).find("FmiImageCore"))
 
   def testToPolarScan(self):
     a = _raveio.open(self.PVOL_TESTFILE).object
@@ -84,7 +84,7 @@ class FmiImageTest(unittest.TestCase):
     a = _raveio.open(self.PVOL_TESTFILE).object
     b = _fmiimage.fromRave(a.getScan(0), "DBZH")
     c = b.toRaveField()
-    self.assertNotEqual(-1, string.find(`type(c)`, "RaveFieldCore"))
+    self.assertNotEqual(-1, str(type(c)).find("RaveFieldCore"))
     
 if __name__ == "__main__":
   #import sys;sys.argv = ['', 'Test.testName']
index 425414e..4dd4fb2 100644 (file)
@@ -37,13 +37,13 @@ class PyRopoGeneratorTest(unittest.TestCase):
   
   def testNew(self):
     a = _ropogenerator.new()
-    self.assertNotEqual(-1, string.find(`type(a)`, "RopoGeneratorCore"))
+    self.assertNotEqual(-1, str(type(a)).find("RopoGeneratorCore"))
     self.assertTrue(None==a.getImage())
 
   def testNew_withImage(self):
     image = _fmiimage.fromRave(_raveio.open(self.PVOL_TESTFILE).object.getScan(0), "DBZH")
     a = _ropogenerator.new(image)
-    self.assertNotEqual(-1, string.find(`type(a)`, "RopoGeneratorCore"))
+    self.assertNotEqual(-1, str(type(a)).find("RopoGeneratorCore"))
     self.assertTrue(image==a.getImage())
   
   def testSetImage(self):
@@ -131,46 +131,46 @@ class PyRopoGeneratorTest(unittest.TestCase):
     b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
     b.speck(-20, 5).emitter(3,6).classify()
     
-    self.assertEquals("fi.fmi.ropo.detector.classification", b.classification.getAttribute("how/task"))
-    self.assertTrue(string.find(b.classification.getAttribute("how/task_args"), "SPECK:") >= 0)
-    self.assertTrue(string.find(b.classification.getAttribute("how/task_args"), "EMITTER:") >= 0)
+    self.assertEqual("fi.fmi.ropo.detector.classification", b.classification.getAttribute("how/task"))
+    self.assertTrue(b.classification.getAttribute("how/task_args").find("SPECK:") >= 0)
+    self.assertTrue(b.classification.getAttribute("how/task_args").find("EMITTER:") >= 0)
 
-    self.assertEquals("fi.fmi.ropo.detector.classification_marker", b.markers.getAttribute("how/task"))
-    self.assertTrue(string.find(b.markers.getAttribute("how/task_args"), "SPECK:") >= 0)
-    self.assertTrue(string.find(b.markers.getAttribute("how/task_args"), "EMITTER:") >= 0)
+    self.assertEqual("fi.fmi.ropo.detector.classification_marker", b.markers.getAttribute("how/task"))
+    self.assertTrue(b.markers.getAttribute("how/task_args").find("SPECK:") >= 0)
+    self.assertTrue(b.markers.getAttribute("how/task_args").find("EMITTER:") >= 0)
 
   def testClassify_reclassification(self):
     a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
     b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
     b.speck(-20, 5).emitter(3,6).classify()
     
-    self.assertEquals("fi.fmi.ropo.detector.classification", b.classification.getAttribute("how/task"))
-    self.assertTrue(string.find(b.classification.getAttribute("how/task_args"), "SPECK:") >= 0)
-    self.assertTrue(string.find(b.classification.getAttribute("how/task_args"), "EMITTER:") >= 0)
+    self.assertEqual("fi.fmi.ropo.detector.classification", b.classification.getAttribute("how/task"))
+    self.assertTrue(b.classification.getAttribute("how/task_args").find("SPECK:") >= 0)
+    self.assertTrue(b.classification.getAttribute("how/task_args").find("EMITTER:") >= 0)
 
-    self.assertEquals("fi.fmi.ropo.detector.classification_marker", b.markers.getAttribute("how/task"))
-    self.assertTrue(string.find(b.markers.getAttribute("how/task_args"), "SPECK:") >= 0)
-    self.assertTrue(string.find(b.markers.getAttribute("how/task_args"), "EMITTER:") >= 0)
+    self.assertEqual("fi.fmi.ropo.detector.classification_marker", b.markers.getAttribute("how/task"))
+    self.assertTrue(b.markers.getAttribute("how/task_args").find("SPECK:") >= 0)
+    self.assertTrue(b.markers.getAttribute("how/task_args").find("EMITTER:") >= 0)
 
     b.clutter(-5, 5).classify()
-    self.assertEquals("fi.fmi.ropo.detector.classification", b.classification.getAttribute("how/task"))
-    self.assertTrue(string.find(b.classification.getAttribute("how/task_args"), "SPECK:") >= 0)
-    self.assertTrue(string.find(b.classification.getAttribute("how/task_args"), "EMITTER:") >= 0)
-    self.assertTrue(string.find(b.classification.getAttribute("how/task_args"), "CLUTTER:") >= 0)
+    self.assertEqual("fi.fmi.ropo.detector.classification", b.classification.getAttribute("how/task"))
+    self.assertTrue(b.classification.getAttribute("how/task_args").find("SPECK:") >= 0)
+    self.assertTrue(b.classification.getAttribute("how/task_args").find("EMITTER:") >= 0)
+    self.assertTrue(b.classification.getAttribute("how/task_args").find("CLUTTER:") >= 0)
 
   def testDeclassify(self):
     a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
     b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
     b.speck(-20, 5).emitter(3,6).classify().declassify()
     
-    self.assertEquals(0, b.getProbabilityFieldCount())
-    self.assertEquals("fi.fmi.ropo.detector.classification", b.classification.getAttribute("how/task"))
-    self.assertTrue(string.find(b.classification.getAttribute("how/task_args"), "SPECK:") == -1)
-    self.assertTrue(string.find(b.classification.getAttribute("how/task_args"), "EMITTER:") == -1)
+    self.assertEqual(0, b.getProbabilityFieldCount())
+    self.assertEqual("fi.fmi.ropo.detector.classification", b.classification.getAttribute("how/task"))
+    self.assertTrue(b.classification.getAttribute("how/task_args").find("SPECK:") == -1)
+    self.assertTrue(b.classification.getAttribute("how/task_args").find("EMITTER:") == -1)
 
-    self.assertEquals("fi.fmi.ropo.detector.classification_marker", b.markers.getAttribute("how/task"))
-    self.assertTrue(string.find(b.markers.getAttribute("how/task_args"), "SPECK:") == -1)
-    self.assertTrue(string.find(b.markers.getAttribute("how/task_args"), "EMITTER:") == -1)
+    self.assertEqual("fi.fmi.ropo.detector.classification_marker", b.markers.getAttribute("how/task"))
+    self.assertTrue(b.markers.getAttribute("how/task_args").find("SPECK:") == -1)
+    self.assertTrue(b.markers.getAttribute("how/task_args").find("EMITTER:") == -1)
 
   def testGenerator_generateClassification(self):
     a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
@@ -222,22 +222,22 @@ class PyRopoGeneratorTest(unittest.TestCase):
     b.speck(-20, 5).restoreSelf(50)
     result = b.getImage()
     self.assertTrue(result != oldimg)
-    self.assertEquals("fi.fmi.ropo.restore", result.getAttribute("how/task"))
-    self.assertTrue(string.find(result.getAttribute("how/task_args"), "SPECK:") >= 0)
+    self.assertEqual("fi.fmi.ropo.restore", result.getAttribute("how/task"))
+    self.assertTrue(result.getAttribute("how/task_args").find("SPECK:") >= 0)
 
   def testGetProbabilityFieldCount(self):
     a = _raveio.open(self.PVOL_RIX_TESTFILE).object.getScan(0)
     b = _ropogenerator.new(_fmiimage.fromRave(a, "DBZH"))
 
-    self.assertEquals(0, b.getProbabilityFieldCount())
+    self.assertEqual(0, b.getProbabilityFieldCount())
     b.speck(-20, 5).emitter(-20, 4)
-    self.assertEquals(2, b.getProbabilityFieldCount())
+    self.assertEqual(2, b.getProbabilityFieldCount())
     c = b.getProbabilityField(0)
     d = b.getProbabilityField(1)
-    self.assertTrue(string.find(c.getAttribute("how/task_args"), "SPECK:") >= 0)
-    self.assertTrue(string.find(c.getAttribute("how/task_args"), "EMITTER:") == -1)
-    self.assertTrue(string.find(d.getAttribute("how/task_args"), "EMITTER:") >= 0)
-    self.assertTrue(string.find(d.getAttribute("how/task_args"), "SPECK:") == -1)
+    self.assertTrue(c.getAttribute("how/task_args").find("SPECK:") >= 0)
+    self.assertTrue(c.getAttribute("how/task_args").find("EMITTER:") == -1)
+    self.assertTrue(d.getAttribute("how/task_args").find("EMITTER:") >= 0)
+    self.assertTrue(d.getAttribute("how/task_args").find("SPECK:") == -1)
 
   def testPadding(self):
       import numpy
@@ -262,15 +262,15 @@ class PyRopoGeneratorTest(unittest.TestCase):
       newdata = newscan.getParameter("DBZH").getData()
 
       # Test data wrapping       
-      self.assertEquals(newdata[:4].tolist(), data[356:,].tolist())
-      self.assertEquals(newdata[364:,].tolist(), data[:4,].tolist())
+      self.assertEqual(newdata[:4].tolist(), data[356:,].tolist())
+      self.assertEqual(newdata[364:,].tolist(), data[:4,].tolist())
 
       classification = _ravefield.new()
       classification.setData(newdata)    # bogus probability of anomaly array
       unwrapped, classification = ropo_realtime.UnpadNrays(newscan, classification, gates)
 
       # Test data unwrapping
-      self.assertEquals(unwrapped.getParameter("DBZH").getData().tolist(), 
+      self.assertEqual(unwrapped.getParameter("DBZH").getData().tolist(), 
                         data.tolist())
 
   # Simple way to ensure that a file is exported properly
index 09f4a5f..9e83b26 100644 (file)
@@ -42,8 +42,8 @@ class ropo_quality_plugin_test(unittest.TestCase):
 
   def test_getQualityFields(self):
     result = self.classUnderTest.getQualityFields()
-    self.assertEquals(1, len(result))
-    self.assertEquals("fi.fmi.ropo.detector.classification", result[0])
+    self.assertEqual(1, len(result))
+    self.assertEqual("fi.fmi.ropo.detector.classification", result[0])
   
   def test_process_scan(self):
     scan = _raveio.open(self.SCAN_FIXTURE).object
@@ -81,8 +81,8 @@ class ropo_quality_plugin_test(unittest.TestCase):
     for i in range(result.getNumberOfScans()):
       scan = result.getScan(i)
       self.assertTrue(scan.getQualityFieldByHowTask("fi.fmi.ropo.detector.classification") != None)
-      self.assertEquals(result.getScanWithMaxDistance().getParameter('DBZH').getData().shape[0], 361, "Wrong size of data field")
-      self.assertEquals(result.getScanWithMaxDistance().getParameter('DBZH').getData().shape[1], 500, "Wrong size of data field")
+      self.assertEqual(result.getScanWithMaxDistance().getParameter('DBZH').getData().shape[0], 361, "Wrong size of data field")
+      self.assertEqual(result.getScanWithMaxDistance().getParameter('DBZH').getData().shape[1], 500, "Wrong size of data field")
 
   def test_process_volume_reprocess_true(self):
     volume = _raveio.open(self.VOLUME_FIXTURE).object
@@ -100,7 +100,7 @@ class ropo_quality_plugin_test(unittest.TestCase):
       fields2.append(result.getScan(i).getQualityFieldByHowTask("fi.fmi.ropo.detector.classification"))
     
     self.assertTrue(len(fields) != 0)
-    self.assertEquals(len(fields), len(fields2))
+    self.assertEqual(len(fields), len(fields2))
     for i in range(len(fields)):
       self.assertTrue(fields[i] != fields2[i])
 
@@ -120,7 +120,7 @@ class ropo_quality_plugin_test(unittest.TestCase):
       fields2.append(result.getScan(i).getQualityFieldByHowTask("fi.fmi.ropo.detector.classification"))
     
     self.assertTrue(len(fields) != 0)
-    self.assertEquals(len(fields), len(fields2))
+    self.assertEqual(len(fields), len(fields2))
     for i in range(len(fields)):
       self.assertTrue(fields[i] == fields2[i])
       
@@ -132,6 +132,6 @@ class ropo_quality_plugin_test(unittest.TestCase):
     for i in range(result.getNumberOfScans()):
       scan = result.getScan(i)
       self.assertTrue(scan.getQualityFieldByHowTask("fi.fmi.ropo.detector.classification") != None)
-      self.assertEquals(result.getScanWithMaxDistance().getParameter('DBZH').getData().shape[0], 361, "Wrong size of data field")
-      self.assertEquals(result.getScanWithMaxDistance().getParameter('DBZH').getData().shape[1], 500, "Wrong size of data field")
+      self.assertEqual(result.getScanWithMaxDistance().getParameter('DBZH').getData().shape[0], 361, "Wrong size of data field")
+      self.assertEqual(result.getScanWithMaxDistance().getParameter('DBZH').getData().shape[1], 500, "Wrong size of data field")
 
index 83369c1..90f769e 100755 (executable)
@@ -25,6 +25,12 @@ RESULT=0
 RAVE_ROOT_DIR=`fgrep RAVE_ROOT_DIR "${DEF_MK_FILE}" | sed -e"s/\(RAVE_ROOT_DIR=[ \t]*\)//"`
 RAVE_ROOT_MKFILE="$RAVE_ROOT_DIR/mkf/def.mk"
 
+# Identify python version
+PYTHON_BIN=`fgrep PYTHON_BIN "${RAVE_ROOT_MKFILE}" | sed -e "s/\(PYTHON_BIN=[ \t]*\)//"`
+if [ "$PYTHON_BIN" = "" ]; then
+  PYTHON_BIN=python
+fi
+
 # HLHDFS MKF FILE
 HLHDF_MKFFILE=`fgrep HLHDF_HLDEF_MK_FILE "${RAVE_ROOT_MKFILE}" | sed -e"s/\(HLHDF_HLDEF_MK_FILE=[ \t]*\)//"`
 
@@ -34,7 +40,7 @@ HDF5_LDPATH=`fgrep HDF5_LIBDIR "${HLHDF_MKFFILE}" | sed -e"s/\(HDF5_LIBDIR=[ \t]
 # Get HLHDFs libpath from raves mkf file
 HLHDF_LDPATH=`fgrep HLHDF_LIB_DIR "${DEF_MK_FILE}" | sed -e"s/\(HLHDF_LIB_DIR=[ \t]*\)//"`
 
-BNAME=`python -c 'from distutils import util; import sys; print "lib.%s-%s" % (util.get_platform(), sys.version[0:3])'`
+BNAME=`$PYTHON_BIN -c 'from distutils import util; import sys; print("lib.%s-%s" % (util.get_platform(), sys.version[0:3]))'`
 
 RBPATH="${SCRIPTPATH}/../pyropo"
 RAVE_LDPATH="${RAVE_ROOT_DIR}/lib"
@@ -80,9 +86,9 @@ NARGS=$#
 PYSCRIPT=
 DIRNAME=
 if [ $NARGS -eq 1 ]; then
-  PYSCRIPT=`python -c "import os;print os.path.abspath(\"$1\")"`
+  PYSCRIPT=`$PYTHON_BIN -c "import os;print(os.path.abspath(\"$1\"))"`
 elif [ $NARGS -eq 2 ]; then
-  PYSCRIPT=`python -c "import os;print os.path.abspath(\"$1\")"`
+  PYSCRIPT=`$PYTHON_BIN -c "import os;print(os.path.abspath(\"$1\"))"`
   DIRNAME="$2"
 elif [ $NARGS -eq 0 ]; then
   # Do nothing
@@ -99,9 +105,9 @@ fi
 
 if [ "$PYSCRIPT" != "" ]; then
   #valgrind --leak-check=full --show-reachable=yes 
-  python "$PYSCRIPT"
+  $PYTHON_BIN "$PYSCRIPT"
 else
-  python
+  $PYTHON_BIN
 fi
 
 VAL=$?
index 9486413..cf7cf09 100755 (executable)
@@ -9,7 +9,7 @@
 #
 # History:  2009-06-15 Created by Anders Henja
 ############################################################
-SCRFILE=`python -c "import os;print os.path.abspath(\"$0\")"`
+SCRFILE=`python -c "import os;print(os.path.abspath(\"$0\"))"`
 SCRIPTPATH=`dirname "$SCRFILE"`
 
 RES=255