1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
--- a/plugins/cfpython/cfpython.c
+++ b/plugins/cfpython/cfpython.c
@@ -60,7 +60,6 @@
#include <cfpython.h>
#include <fcntl.h>
#include <stdarg.h>
-#include <node.h>
#include <svnversion.h>
CF_PLUGIN char SvnRevPlugin[] = SVN_REV;
@@ -790,6 +789,12 @@
static PyObject *catcher = NULL;
/**
+ * A Python object so that we only need to import the io module once.
+ * The recommended way to load a file to compile in 3.10 and later involves importing the io module, so we really just do that once.
+ */
+static PyObject *io_module = NULL;
+
+/**
* Trace a Python error to the Crossfire log.
* This uses code from:
* http://stackoverflow.com/questions/4307187/how-to-catch-python-stdout-in-c-code
@@ -877,28 +882,35 @@
replace->file = cf_add_string(sh_path);
}
- /* Load, parse and compile. Note: because Pyhon may have been built with a
- * different library than Crossfire, the FILE* it uses may be incompatible.
- * Therefore we use PyFile to open the file, then convert to FILE* and get
- * Python's own structure. Messy, but can't be helped... */
- if (!(scriptfile = cfpython_openpyfile(filename))) {
- cf_log(llevDebug, "cfpython - The Script file %s can't be opened\n", filename);
+ /* With the new parser in 3.10, we need to read the file contents into a buffer, and then pass that string to compile it.
+ * The new parser removes the PyNode functions as well as PyParser_SimpleParseFile,
+ * so the code needed to be completely rewritten to work.
+ *
+ * Python's solution to these changes is to import the io module and use Python's read method to read in the file,
+ * and then convert the bytes object into a c-string for Py_CompileString
+ *
+ * Though, if it is more performant than the previous code, Py_CompileString is
+ * available for all Python 3, so it is possible to simplify all of them to this if we need to.
+ */
+ if (!io_module)
+ io_module = PyImport_ImportModule("io");
+ scriptfile = PyObject_CallMethod(io_module, "open", "ss", filename, "rb");
+ if (!scriptfile) {
+ cf_log(llevDebug, "CFPython: script file %s can't be opened\n", filename);
cf_free_string(sh_path);
return NULL;
- } else {
- /* Note: FILE* being opaque, it works, but the actual structure may be different! */
- FILE* pyfile = cfpython_pyfile_asfile(scriptfile);
- if ((n = PyParser_SimpleParseFile(pyfile, filename, Py_file_input))) {
- replace->code = PyNode_Compile(n, filename);
- PyNode_Free(n);
- }
-
- if (PyErr_Occurred())
- log_python_error();
- else
- replace->cached_time = stat_buf.st_mtime;
- run = replace;
}
+ PyObject *source_bytes = PyObject_CallMethod(scriptfile, "read", "");
+ (void)PyObject_CallMethod(scriptfile, "close", "");
+ PyObject *code = Py_CompileString(PyBytes_AsString(source_bytes), filename, Py_file_input);
+ if (code) {
+ replace->code = (PyCodeObject *)code;
+ }
+ if (PyErr_Occurred())
+ log_python_error();
+ else
+ replace->cached_time = stat_buf.st_mtime;
+ run = replace;
}
cf_free_string(sh_path);
|