diff --git a/CMakeLists.txt b/CMakeLists.txt index fb9a509f..060f22b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -860,9 +860,9 @@ if(PYTHON OR PYTHON_MODULE) endif() else() if(NOT PYTHONVERSION) - set(PYTHONVERSION 2) + set(PYTHONVERSION 3) endif() - find_package(PythonInterp) + find_package(PythonInterp ${PYTHONVERSION}) find_package(PythonLibs ${PYTHONVERSION}) if (NOT PYTHON_VERSION_STRING VERSION_EQUAL PYTHONLIBS_VERSION_STRING) message(STATUS "PYTHON_EXECUTABLE: " ${PYTHON_EXECUTABLE}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4590be8..27f35ae4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -212,7 +212,7 @@ target_link_libraries(gdl ${LIBRARIES}) add_definitions(-DHAVE_CONFIG_H) if(PYTHON_MODULE) - find_package( PythonInterp REQUIRED ) + find_package( PythonInterp ${PYTHONVERSION} REQUIRED ) execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import site, sys; sys.stdout.write(site.PREFIXES[-1])" OUTPUT_VARIABLE PYTHON_PREFIX) diff --git a/src/gdlpython.cpp b/src/gdlpython.cpp index 981a3fbe..819acde3 100644 --- a/src/gdlpython.cpp +++ b/src/gdlpython.cpp @@ -34,18 +34,32 @@ using namespace std; +#if PY_MAJOR_VERSION >= 3 +int PythonInit() +{ + if( Py_IsInitialized()) return NULL; +#else void PythonInit() { if( Py_IsInitialized()) return; +#endif Py_Initialize(); // signal handlers? static int argc = 1; +#if PY_MAJOR_VERSION >= 3 + static wchar_t* arg0 = Py_DecodeLocale("./py/python.exe",NULL); + static wchar_t* argv[] = {arg0}; +#else static char* arg0 = (char*)"./py/python.exe"; static char* argv[] = {arg0}; +#endif PySys_SetArgv(argc, argv); // http://docs.scipy.org/doc/numpy/reference/c-api.array.html#miscellaneous import_array(); +#if PY_MAJOR_VERSION >= 3 + return NULL; +#endif } void PythonEnd() @@ -73,6 +87,12 @@ BaseGDL* FromPython( PyObject* pyObj) { if( !PyArray_Check( pyObj)) { +#if PY_MAJOR_VERSION >= 3 + if( PyUnicode_Check( pyObj)) + { + return new DStringGDL( PyUnicode_AsUTF8( pyObj)); + } +#else if( PyString_Check( pyObj)) { return new DStringGDL( PyString_AsString( pyObj)); @@ -81,6 +101,7 @@ BaseGDL* FromPython( PyObject* pyObj) { return new DLongGDL( PyInt_AsLong( pyObj)); } +#endif if( PyLong_Check( pyObj)) { return new DLongGDL( PyLong_AsLong( pyObj)); @@ -176,11 +197,19 @@ namespace lib { e->Throw( "ARGV keyword must be of type STRING."); int argc = argvS->N_Elements(); +#if PY_MAJOR_VERSION >= 3 + wchar_t** argv = new wchar_t*[ argc]; +#else char** argv = new char*[ argc]; +#endif - // pyhton copies the value -> threats it as const + // python copies the value -> treats it as const for( int i=0; i= 3 + argv[i] = Py_DecodeLocale(const_cast((*argvS)[ i].c_str()), NULL); +#else argv[i] = const_cast((*argvS)[ i].c_str()); +#endif PySys_SetArgv(argc, argv); delete[] argv; diff --git a/src/gdlpython.hpp b/src/gdlpython.hpp index 45ef4366..cb53bd74 100644 --- a/src/gdlpython.hpp +++ b/src/gdlpython.hpp @@ -18,7 +18,11 @@ #ifndef GDLPYTHON_HPP_ #define GDLPYTHON_HPP_ +#if PY_MAJOR_VERSION >= 3 +int PythonInit(); +#else void PythonInit(); +#endif void PythonEnd(); BaseGDL* FromPython( PyObject* pyObj); diff --git a/src/pythongdl.cpp b/src/pythongdl.cpp index d2496f78..4150e5d9 100644 --- a/src/pythongdl.cpp +++ b/src/pythongdl.cpp @@ -185,14 +185,22 @@ bool CopyArgFromPython( vector& parRef, for( SizeT k=0; k= 3 + int keyIsString = PyUnicode_Check( key); +#else int keyIsString = PyString_Check( key); +#endif if( !keyIsString) { PyErr_SetString( gdlError, "Keywords must be of type string"); return false; } +#if PY_MAJOR_VERSION >= 3 + const char* keyChar = PyUnicode_AsUTF8( key); +#else const char* keyChar = PyString_AsString( key); +#endif string keyString = StrUpCase( keyChar); int kwIx = e.GetPro()->FindKey( keyString); if( kwIx == -1) @@ -519,9 +527,42 @@ extern "C" { {NULL, NULL, 0, NULL} // Sentinel }; +#if PY_MAJOR_VERSION >= 3 + struct module_state { + PyObject *error; + }; + + #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) + + static int GDL_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; + } + + static int GDL_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; + } + + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "GDL", + NULL, + sizeof(struct module_state), + GDLMethods, + NULL, + GDL_traverse, + GDL_clear, + NULL + }; +#endif // python GDL module init function - PyMODINIT_FUNC initGDL() +#if PY_MAJOR_VERSION >= 3 + PyMODINIT_FUNC PyInit_GDL(void) +#else + PyMODINIT_FUNC initGDL(void) +#endif { // http://docs.scipy.org/doc/numpy/reference/c-api.array.html#miscellaneous import_array(); @@ -548,7 +589,11 @@ extern "C" { } SysVar::SetGDLPath( gdlPath); +#if PY_MAJOR_VERSION >= 3 + PyObject* m = PyModule_Create(&moduledef); +#else PyObject* m = Py_InitModule("GDL", GDLMethods); +#endif gdlError = PyErr_NewException((char*)"GDL.error", NULL, NULL); Py_INCREF(gdlError); @@ -557,6 +602,9 @@ extern "C" { // GDL event handling oldInputHook = PyOS_InputHook; PyOS_InputHook = GDLEventHandlerPy; +#if PY_MAJOR_VERSION >= 3 + return m; +#endif } } // extern "C" diff --git a/testsuite/python/test-GDL.py b/testsuite/python/test-GDL.py old mode 100644 new mode 100755 index fca6782b..dec80608 --- a/testsuite/python/test-GDL.py +++ b/testsuite/python/test-GDL.py @@ -1,9 +1,10 @@ -#!/usr/bin/py.test -vv +#!/usr/bin/env -S python3 -m pytest -vv import math import numpy import pytest import os +import tempfile import warnings if 'ADTTMP' in os.environ: @@ -37,11 +38,11 @@ class GDLFile (object): self.name = None with warnings.catch_warnings(): warnings.simplefilter("ignore") - self.fname = os.tmpnam() + self.fname = tempfile.mkstemp()[1] self.code = code def __enter__(self): - fp = file(self.fname, 'w') + fp = open(self.fname, 'w') fp.write(self.code) fp.close() return self.name or self.fname @@ -70,18 +71,17 @@ def test_function_user(): @pytest.mark.parametrize('arg', [ 'Hello, world', '', '\n', -# u'Hello, world', + u'Hello, world', -1.2, 1e-39, 0.0, 0.05, 1.0, 1e128, float('inf'), float('-inf'), -1, 0, 1, 1000, 2**31-1, -2**31, - -1L, 0L, 1L, 2L**31-1, -2L**31, -# 2**45-1, -# complex(1.,1.), complex(0,0), -# numpy.arange(0, 259, dtype=int), -# numpy.arange(0, 259, dtype=long), -# numpy.arange(-255, 255, dtype=numpy.int8), + # 2**45-1, # GDL returns int + # complex(1.,1.), # Aborts execution + # complex(0.,0.), # Aborts execution + # numpy.arange(0, 259, dtype=int) # GDL Error: unknown return array type" numpy.arange(0, 259, dtype=numpy.uint8), numpy.arange(-255, 259, dtype=numpy.int16), -# numpy.arange(0, 259, dtype=numpy.uint16), + # numpy.arange(-255, 255, dtype=numpy.int8), # GDL Error: unknown return array type + # numpy.arange(0, 259, dtype=numpy.uint16), # GDL Error: unknown return array type numpy.arange(-255, 259, dtype=numpy.int32), numpy.arange(0, 259, dtype=numpy.uint32), numpy.arange(-1, 1., 0.1, dtype=float), @@ -89,9 +89,9 @@ def test_function_user(): numpy.arange(-1, 1., 0.1, dtype=numpy.float64), numpy.arange(-1, 1., 0.1, dtype=numpy.complex64), numpy.arange(-1, 1., 0.1, dtype=numpy.complex128), -# [1,2,3,4,5], -# (1,2,3,4,5), -# {'1':2}, + # [1,2,3,4,5], # GDL Error: Cannot convert python scalar" + # (1,2,3,4,5), # Returns only the first number + # {'1':2}, # GDL Error: Cannot convert python scalar ]) def test_function_arg_pass_return(arg): '''Call a function that just returns its argument, with different data types''' @@ -128,12 +128,11 @@ def test_pro_user(): @pytest.mark.parametrize('arg', [ 'Hello, world', '', -# u'Hello, world', + u'Hello, world', -1.2, 1e-39, 0.0, 0.05, 1.0, 1e128, float('inf'), float('-inf'), -1, 0, 1, 1000, 2**31-1, -2**31, - -1L, 0L, 1L, 2L**31-1, -2L**31, -# 2**45-1, -# complex(1.,1.), + # 2**45-1, # GDL returns int + # complex(1.,1.), # Aborts execution ]) def test_pro_arg_pass(arg): '''Call a user defined procedure that stores the value for different @@ -149,7 +148,7 @@ def test_pro_arg_pass(arg): with GDLFile(code) as name: with warnings.catch_warnings(): warnings.simplefilter("ignore") - fname = os.tmpnam() + fname = tempfile.mkstemp()[1] GDL.pro(name, fname, arg) ret = open(fname).read().strip() os.unlink(fname) @@ -185,7 +184,8 @@ def test_script(): with warnings.catch_warnings(): warnings.simplefilter("ignore") - fname = os.tmpnam() + fname = tempfile.mkstemp()[1] + scriptname = tempfile.mkstemp()[1] arg = 'Hello, world!' code = '''openw, 5, '{0}' printf, 5, '{1}' @@ -198,7 +198,7 @@ def test_script(): assert arg == ret -@pytest.mark.skipif(True, reason='This will errornously abort the test suite') +@pytest.mark.xfail(reason='Does not raise a GDL.error') def test_invalid_code(): '''Call a function with some invalid GDL code'''