Skip to content

Commit 2ac13f2

Browse files
committed
Support for Python3.
Tested on linux (lubuntu 16.04) with Python 3.5 and Python 2.7 using 'ctest' and 'make test_python_psc'.
1 parent 58dc757 commit 2ac13f2

27 files changed

Lines changed: 840 additions & 801 deletions

bindings/python/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ if(ENABLE_python)
7878

7979
swig_link_libraries(plplotcmodule plplot ${PYTHON_LIBRARIES})
8080

81+
# This creates the _plplotc.so library that Python3 will look for. It
82+
# will not find the _plplotcmodule.so that Python2 would fine. Ideally
83+
# we'd probably just generate this library, but for now it is safer(?)
84+
# and easier just to make them both.
85+
set_target_properties(
86+
_plplotcmodule
87+
PROPERTIES
88+
SUFFIX ".so"
89+
OUTPUT_NAME "_plplotc"
90+
)
91+
8192
if(USE_RPATH)
8293
get_target_property(LIB_INSTALL_RPATH plplot INSTALL_RPATH)
8394
set_target_properties(

bindings/python/plplot.py

Lines changed: 328 additions & 340 deletions
Large diffs are not rendered by default.

bindings/python/plplotcmodule.i

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,7 @@ typedef void ( *label_func )( PLINT, PLFLT, char *, PLINT, PLPointer );
11231123

11241124
void do_label_callback( PLINT axis, PLFLT value, char *string, PLINT len, PLPointer data )
11251125
{
1126-
PyObject *pdata, *arglist, *result;
1126+
PyObject *pdata, *arglist, *result, *unicode_string;
11271127
char *pystring;
11281128

11291129
// the data argument is acutally a pointer to a python object
@@ -1152,16 +1152,24 @@ typedef void ( *label_func )( PLINT, PLFLT, char *, PLINT, PLPointer );
11521152
fprintf( stderr, "label callback failed with 3 arguments\n" );
11531153
PyErr_SetString( PyExc_RuntimeError, "label callback must take 3 arguments." );
11541154
}
1155-
else if ( !PyString_Check( result ) )
1156-
{
1157-
fprintf( stderr, "label callback must return a string\n" );
1158-
PyErr_SetString( PyExc_RuntimeError, "label callback must return a string." );
1159-
}
1160-
else
1161-
{
1155+
else if ( PyString_Check( result ) )
1156+
{
11621157
// should I test the type here?
11631158
pystring = PyString_AsString( result );
11641159
strncpy( string, pystring, len );
1160+
}
1161+
else if ( PyUnicode_Check( result ))
1162+
{
1163+
// unicode_string is never freed? memory leak here?
1164+
unicode_string = PyUnicode_AsEncodedString( result , "utf-8", "Error ~");
1165+
pystring = PyBytes_AS_STRING( unicode_string );
1166+
// len may be different then the byte string length w/ unicode?
1167+
strncpy( string, pystring, len );
1168+
}
1169+
else
1170+
{
1171+
fprintf( stderr, "label callback must return a string\n" );
1172+
PyErr_SetString( PyExc_RuntimeError, "label callback must return a string." );
11651173
}
11661174
// release the result
11671175
Py_CLEAR( result );
@@ -1258,7 +1266,15 @@ typedef void ( *label_func )( PLINT, PLFLT, char *, PLINT, PLPointer );
12581266
PyObject * rep = PyObject_Repr( input );
12591267
if ( rep )
12601268
{
1261-
char* str = PyString_AsString( rep );
1269+
// Memory leaks here? str and uni_str are not freed?
1270+
char* str;
1271+
if ( PyUnicode_Check( rep ) ){
1272+
PyObject *uni_str = PyUnicode_AsEncodedString( rep , "utf-8", "Error ~");
1273+
str = PyBytes_AS_STRING( uni_str );
1274+
}
1275+
else {
1276+
str = PyString_AsString( rep );
1277+
}
12621278
if ( strcmp( str, "<built-in function pltr0>" ) == 0 )
12631279
{
12641280
result = pltr0;
@@ -1558,6 +1574,8 @@ typedef void ( *label_func )( PLINT, PLFLT, char *, PLINT, PLPointer );
15581574
%typemap( in ) const char *legline[4] ( char** tmp = NULL )
15591575
{
15601576
int i;
1577+
PyObject *elt, *unicode_string;
1578+
15611579
if ( !PySequence_Check( $input ) || PySequence_Size( $input ) != 4 )
15621580
{
15631581
PyErr_SetString( PyExc_ValueError, "Requires a sequence of 4 strings." );
@@ -1574,7 +1592,15 @@ typedef void ( *label_func )( PLINT, PLFLT, char *, PLINT, PLPointer );
15741592
$1 = tmp;
15751593
for ( i = 0; i < 4; i++ )
15761594
{
1577-
$1[i] = PyString_AsString( PySequence_Fast_GET_ITEM( $input, i ) );
1595+
$1[i] = NULL;
1596+
elt = PySequence_Fast_GET_ITEM( $input, i );
1597+
if ( PyString_Check( elt ) ){
1598+
$1[i] = PyString_AsString( elt );
1599+
}
1600+
else if ( PyUnicode_Check( elt ) ){
1601+
unicode_string = PyUnicode_AsEncodedString( elt , "utf-8", "Error ~");
1602+
$1[i] = PyBytes_AS_STRING( unicode_string );
1603+
}
15781604
if ( $1[i] == NULL )
15791605
{
15801606
free( tmp );
@@ -1592,6 +1618,8 @@ typedef void ( *label_func )( PLINT, PLFLT, char *, PLINT, PLPointer );
15921618
%typemap ( in ) ( int *p_argc, char **argv ) ( int tmp )
15931619
{
15941620
int i;
1621+
PyObject *unicode_string;
1622+
15951623
if ( !PyList_Check( $input ) )
15961624
{
15971625
PyErr_SetString( PyExc_ValueError, "Expecting a list" );
@@ -1603,13 +1631,20 @@ typedef void ( *label_func )( PLINT, PLFLT, char *, PLINT, PLPointer );
16031631
for ( i = 0; i < tmp; i++ )
16041632
{
16051633
PyObject *s = PyList_GetItem( $input, i );
1606-
if ( !PyString_Check( s ) )
1607-
{
1634+
if ( PyString_Check( s ) ){
1635+
$2[i] = PyString_AsString( s );
1636+
}
1637+
else if ( PyUnicode_Check( s ) ){
1638+
// unicode_string is never freed? memory leak here?
1639+
unicode_string = PyUnicode_AsEncodedString( s , "utf-8", "Error ~");
1640+
$2[i] = PyBytes_AS_STRING( unicode_string );
1641+
}
1642+
else{
16081643
free( $2 );
16091644
PyErr_SetString( PyExc_ValueError, "List items must be strings" );
16101645
return NULL;
16111646
}
1612-
$2[i] = PyString_AsString( s );
1647+
16131648
}
16141649
$2[i] = 0;
16151650
}

cmake/modules/python.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ if(ENABLE_python)
7575
# Check for numpy installation.
7676
execute_process(
7777
COMMAND
78-
${PYTHON_EXECUTABLE} -c "import numpy; print numpy.get_include()"
78+
${PYTHON_EXECUTABLE} -c "import numpy; print(numpy.get_include())"
7979
OUTPUT_VARIABLE NUMPY_INCLUDE_PATH_PARENT
8080
RESULT_VARIABLE NUMPY_ERR
8181
OUTPUT_STRIP_TRAILING_WHITESPACE
@@ -123,7 +123,7 @@ if(ENABLE_python)
123123
# using distutils.
124124
execute_process(
125125
COMMAND
126-
${PYTHON_EXECUTABLE} -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,0,prefix='${PYTHON_INSTALL_TEMPLATE}')"
126+
${PYTHON_EXECUTABLE} -c "from distutils import sysconfig; print(sysconfig.get_python_lib(1,0,prefix='${PYTHON_INSTALL_TEMPLATE}'))"
127127
OUTPUT_VARIABLE PYTHON_INSTDIR
128128
OUTPUT_STRIP_TRAILING_WHITESPACE
129129
)

examples/python/x01

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import plplot as w
3030
# Parse and process command line arguments
3131
w.plparseopts(sys.argv, w.PL_PARSE_FULL)
3232
version = w.plgver()
33-
print "PLplot library version: " + version
33+
print("PLplot library version: " + version)
3434

3535
# Initialize plplot
3636
w.plinit()

examples/python/x01.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ def plot2(w):
116116

117117
x = (arange(100)-19)/6.0
118118
if 0.0 in x:
119-
#replace 0.0 by small value that gives the same sinc(x) result.
120-
x[list(x).index(0.0)] = 1.e-30
119+
#replace 0.0 by small value that gives the same sinc(x) result.
120+
x[list(x).index(0.0)] = 1.e-30
121121
y = sin(x)/x
122122

123123
# Draw the line

examples/python/x02.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,19 @@ def draw_windows(w, nw, cmap0_offset):
2828
w.plfont(4)
2929

3030
for i in range(nw):
31-
w.plcol0(i+cmap0_offset)
32-
w.pladv(0)
33-
vmin = 0.1
34-
vmax = 0.9
35-
for j in range(3):
36-
w.plwidth(j + 1)
37-
w.plvpor(vmin, vmax, vmin, vmax)
38-
w.plwind(0.0, 1.0, 0.0, 1.0)
39-
w.plbox("bc", 0.0, 0, "bc", 0.0, 0)
40-
vmin = vmin + 0.1
41-
vmax = vmax - 0.1
42-
w.plwidth(1)
43-
w.plptex(0.5, 0.5, 1.0, 0.0, 0.5, `i`)
31+
w.plcol0(i+cmap0_offset)
32+
w.pladv(0)
33+
vmin = 0.1
34+
vmax = 0.9
35+
for j in range(3):
36+
w.plwidth(j + 1)
37+
w.plvpor(vmin, vmax, vmin, vmax)
38+
w.plwind(0.0, 1.0, 0.0, 1.0)
39+
w.plbox("bc", 0.0, 0, "bc", 0.0, 0)
40+
vmin = vmin + 0.1
41+
vmax = vmax - 0.1
42+
w.plwidth(1)
43+
w.plptex(0.5, 0.5, 1.0, 0.0, 0.5, str(i))
4444

4545
# Demonstrate multiple windows and default color map 0 palette.
4646
def demo1(w):

examples/python/x03.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,32 @@ def main(w):
3737

3838
# Draw circles for polar grid
3939
for i in range(10):
40-
w.plarc(0.0, 0.0, 0.1*(i+1), 0.1*(i+1), 0.0, 360.0, 0.0, 0)
40+
w.plarc(0.0, 0.0, 0.1*(i+1), 0.1*(i+1), 0.0, 360.0, 0.0, 0)
4141

4242
w.plcol0(2)
4343
for i in range(12):
44-
theta = 30.0 * i
45-
dx = cos(dtr * theta)
46-
dy = sin(dtr * theta)
44+
theta = 30.0 * i
45+
dx = cos(dtr * theta)
46+
dy = sin(dtr * theta)
4747

48-
# Draw radial spokes for polar grid
48+
# Draw radial spokes for polar grid
4949

50-
w.pljoin(0.0, 0.0, dx, dy)
50+
w.pljoin(0.0, 0.0, dx, dy)
5151

52-
# Write labels for angle
52+
# Write labels for angle
5353

54-
text = `int(theta)`
54+
text = str(int(theta))
5555
if theta < 9.99:
5656
offset = 0.45
5757
elif theta < 99.9:
5858
offset = 0.30
5959
else:
6060
offset = 0.15
6161
#Slightly off zero to avoid floating point logic flips at 90 and 270 deg.
62-
if dx >= -0.00001:
63-
w.plptex(dx, dy, dx, dy, -offset, text)
64-
else:
65-
w.plptex(dx, dy, -dx, -dy, 1.+offset, text)
62+
if dx >= -0.00001:
63+
w.plptex(dx, dy, dx, dy, -offset, text)
64+
else:
65+
w.plptex(dx, dy, -dx, -dy, 1.+offset, text)
6666

6767
# Draw the graph
6868

examples/python/x04.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ def plot1(w, type):
5252
w.plwind(-2., 3.0, -80.0, 0.0)
5353
w.plcol0(1)
5454
if type == 0:
55-
w.plbox("bclnst", 0.0, 0, "bnstv", 0.0, 0)
55+
w.plbox("bclnst", 0.0, 0, "bnstv", 0.0, 0)
5656
elif type == 1:
57-
w.plbox("bcfghlnst", 0.0, 0, "bcghnstv", 0.0, 0)
57+
w.plbox("bcfghlnst", 0.0, 0, "bcghnstv", 0.0, 0)
5858
else:
59-
print "error: type must be either 0 or 1"
59+
print("error: type must be either 0 or 1")
6060
w.plcol0(2)
6161
w.plline(freql, ampl)
6262
w.plcol0(2)
@@ -72,13 +72,13 @@ def plot1(w, type):
7272
# For the gridless case, put phase vs freq on same plot
7373
if type == 0:
7474
w.plcol0(1)
75-
w.plwind(-2.0, 3.0, -100.0, 0.0)
76-
w.plbox("", 0.0, 0, "cmstv", 30.0, 3)
77-
w.plcol0(3)
78-
w.plline(freql, phase)
79-
w.plstring(freql, phase, "#(728)")
80-
w.plcol0(3)
81-
w.plmtex("r", 5.0, 0.5, 0.5, "Phase shift (degrees)")
75+
w.plwind(-2.0, 3.0, -100.0, 0.0)
76+
w.plbox("", 0.0, 0, "cmstv", 30.0, 3)
77+
w.plcol0(3)
78+
w.plline(freql, phase)
79+
w.plstring(freql, phase, "#(728)")
80+
w.plcol0(3)
81+
w.plmtex("r", 5.0, 0.5, 0.5, "Phase shift (degrees)")
8282
nlegend = 2
8383
else:
8484
nlegend = 1

examples/python/x05.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def main(w):
3838
w.plhist(data, -1.1, 1.1, 44, 0)
3939
w.plcol0(2)
4040
w.pllab("#frValue", "#frFrequency",
41-
"#frPLplot Example 5 - Probability function of Oscillator")
41+
"#frPLplot Example 5 - Probability function of Oscillator")
4242

4343
# Restore defaults
4444
# Must be done independently because otherwise this changes output files

0 commit comments

Comments
 (0)