File: python3-ftbfs-1008637

package info (click to toggle)
crossfire 1.75.0-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 24,168 kB
  • sloc: ansic: 83,169; sh: 4,659; perl: 1,736; lex: 1,443; makefile: 1,199; python: 43
file content (78 lines) | stat: -rw-r--r-- 3,431 bytes parent folder | download | duplicates (3)
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);