diff --git a/examples/python/funcs.py b/examples/python/funcs.py index 2f4830676e..f38aca53f2 100644 --- a/examples/python/funcs.py +++ b/examples/python/funcs.py @@ -1,9 +1,10 @@ # Python function that implements a loop of short runs # calls back to LAMMPS via "lmp" instance # lammps() must be called with ptr=lmpptr for this to work +from __future__ import print_function def loop(N,cut0,thresh,lmpptr): - print "LOOP ARGS",N,cut0,thresh,lmpptr + print("LOOP ARGS",N,cut0,thresh,lmpptr) from lammps import lammps lmp = lammps(ptr=lmpptr) natoms = lmp.get_natoms() @@ -12,11 +13,12 @@ def loop(N,cut0,thresh,lmpptr): cut = cut0 + i*0.1 lmp.set_variable("cut",cut) # set a variable in LAMMPS + lmp.command("pair_style lj/cut ${cut}") # LAMMPS command #lmp.command("pair_style lj/cut %d" % cut) # LAMMPS command option lmp.command("pair_coeff * * 1.0 1.0") # ditto lmp.command("run 10") # ditto pe = lmp.extract_compute("thermo_pe",0,0) # extract total PE from LAMMPS - print "PE",pe/natoms,thresh + print("PE",pe/natoms,thresh) if pe/natoms < thresh: return diff --git a/examples/python/in.python b/examples/python/in.python index cb2013fdd3..c5aa504d43 100644 --- a/examples/python/in.python +++ b/examples/python/in.python @@ -25,13 +25,14 @@ run 10 # example of catching a syntax error python simple here """ +from __future__ import print_function + def simple(): - import exceptions - print "Inside simple function" + print("Inside simple function") try: foo += 1 - except Exception, e: - print "FOO error:",e + except Exception as e: + print("FOO error:", e) """ python simple invoke diff --git a/lib/python/Makefile.lammps.python3 b/lib/python/Makefile.lammps.python3 new file mode 100644 index 0000000000..1d5d5f2d81 --- /dev/null +++ b/lib/python/Makefile.lammps.python3 @@ -0,0 +1,6 @@ +# Settings that the LAMMPS build will import when this package library is used +# See the README file for more explanation + +python_SYSINC = $(shell which python3-config > /dev/null 2>&1 && python3-config --includes || python-config --includes ) +python_SYSLIB = $(shell which python3-config > /dev/null 2>&1 && python3-config --ldflags || python-config --ldflags) +python_SYSPATH = diff --git a/lib/python/README b/lib/python/README index ddccc1a21a..077c2547d2 100644 --- a/lib/python/README +++ b/lib/python/README @@ -6,10 +6,6 @@ and your version of Python, and copy it to Makefile.lammps before building LAMMPS itself. You may need to edit one of the provided files to match your system. -Note that is not currently possible to use the PYTHON package with -Python 3, only with Python 2. The C API changed from Python 2 to 3 -and the LAMMPS code is not compatible with both. - If you create a new Makefile.lammps file suitable for some version of Python on some system, that is not a match to one of the provided Makefile.lammps.* files, you can send it to the developers, and we can diff --git a/python/lammps.py b/python/lammps.py index a36abb87e8..d428a097a8 100644 --- a/python/lammps.py +++ b/python/lammps.py @@ -30,7 +30,7 @@ from collections import namedtuple import os import select import re - +import sys class MPIAbortException(Exception): def __init__(self, message): @@ -151,9 +151,16 @@ class lammps(object): else: # magic to convert ptr to ctypes ptr - pythonapi.PyCObject_AsVoidPtr.restype = c_void_p - pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object] - self.lmp = c_void_p(pythonapi.PyCObject_AsVoidPtr(ptr)) + if sys.version_info >= (3, 0): + # Python 3 (uses PyCapsule API) + pythonapi.PyCapsule_GetPointer.restype = c_void_p + pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p] + self.lmp = c_void_p(pythonapi.PyCapsule_GetPointer(ptr, None)) + else: + # Python 2 (uses PyCObject API) + pythonapi.PyCObject_AsVoidPtr.restype = c_void_p + pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object] + self.lmp = c_void_p(pythonapi.PyCObject_AsVoidPtr(ptr)) def __del__(self): if self.lmp and self.opened: @@ -305,7 +312,7 @@ class lammps(object): def set_variable(self,name,value): if name: name = name.encode() if value: value = str(value).encode() - return self.lib.lammps_set_variable(self.lmp,name,str(value)) + return self.lib.lammps_set_variable(self.lmp,name,value) # return current value of thermo keyword diff --git a/src/PYTHON/python.cpp b/src/PYTHON/python.cpp index 74591c1c16..c465086f63 100644 --- a/src/PYTHON/python.cpp +++ b/src/PYTHON/python.cpp @@ -25,6 +25,22 @@ enum{NONE,INT,DOUBLE,STRING,PTR}; #define VALUELENGTH 64 // also in variable.cpp +// Wrap API changes between Python 2 and 3 using macros +#if PY_MAJOR_VERSION == 2 +#define PY_INT_FROM_LONG(X) PyInt_FromLong(X) +#define PY_INT_AS_LONG(X) PyInt_AsLong(X) +#define PY_STRING_FROM_STRING(X) PyString_FromString(X) +#define PY_VOID_POINTER(X) PyCObject_FromVoidPtr((void *) X, NULL) +#define PY_STRING_AS_STRING(X) PyString_AsString(X) + +#elif PY_MAJOR_VERSION == 3 +#define PY_INT_FROM_LONG(X) PyLong_FromLong(X) +#define PY_INT_AS_LONG(X) PyLong_AsLong(X) +#define PY_STRING_FROM_STRING(X) PyUnicode_FromString(X) +#define PY_VOID_POINTER(X) PyCapsule_New((void *) X, NULL, NULL) +#define PY_STRING_AS_STRING(X) PyUnicode_AsUTF8(X) +#endif + /* ---------------------------------------------------------------------- */ Python::Python(LAMMPS *lmp) : Pointers(lmp) @@ -257,8 +273,10 @@ void Python::invoke_function(int ifunc, char *result) error->all(FLERR,"Could not evaluate Python function input variable"); } - pValue = PyInt_FromLong(atoi(str)); - } else pValue = PyInt_FromLong(pfuncs[ifunc].ivalue[i]); + pValue = PY_INT_FROM_LONG(atoi(str)); + } else { + pValue = PY_INT_FROM_LONG(pfuncs[ifunc].ivalue[i]); + } } else if (itype == DOUBLE) { if (pfuncs[ifunc].ivarflag[i]) { str = input->variable->retrieve(pfuncs[ifunc].svalue[i]); @@ -269,7 +287,9 @@ void Python::invoke_function(int ifunc, char *result) } pValue = PyFloat_FromDouble(atof(str)); - } else pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]); + } else { + pValue = PyFloat_FromDouble(pfuncs[ifunc].dvalue[i]); + } } else if (itype == STRING) { if (pfuncs[ifunc].ivarflag[i]) { str = input->variable->retrieve(pfuncs[ifunc].svalue[i]); @@ -277,10 +297,13 @@ void Python::invoke_function(int ifunc, char *result) PyGILState_Release(gstate); error->all(FLERR,"Could not evaluate Python function input variable"); } - pValue = PyString_FromString(str); - } else pValue = PyString_FromString(pfuncs[ifunc].svalue[i]); + + pValue = PY_STRING_FROM_STRING(str); + } else { + pValue = PY_STRING_FROM_STRING(pfuncs[ifunc].svalue[i]); + } } else if (itype == PTR) { - pValue = PyCObject_FromVoidPtr((void *) lmp,NULL); + pValue = PY_VOID_POINTER(lmp); } PyTuple_SetItem(pArgs,i,pValue); } @@ -304,11 +327,11 @@ void Python::invoke_function(int ifunc, char *result) if (pfuncs[ifunc].noutput) { int otype = pfuncs[ifunc].otype; if (otype == INT) { - sprintf(result,"%ld",PyInt_AsLong(pValue)); + sprintf(result,"%ld",PY_INT_AS_LONG(pValue)); } else if (otype == DOUBLE) { sprintf(result,"%.15g",PyFloat_AsDouble(pValue)); } else if (otype == STRING) { - char *pystr = PyString_AsString(pValue); + char *pystr = PY_STRING_AS_STRING(pValue); if (pfuncs[ifunc].longstr) strncpy(pfuncs[ifunc].longstr,pystr,pfuncs[ifunc].length_longstr); else strncpy(result,pystr,VALUELENGTH-1);