|
| 1 | +import importlib |
| 2 | +import traceback |
| 3 | + |
| 4 | + |
1 | 5 | cdef object RESOURCE_TYPE_NAME = "Python" |
2 | 6 | cdef object RESOURCE_EXTENSIONS = ("py", "pyc", "pyo", "pyd") |
3 | 7 |
|
@@ -85,37 +89,81 @@ cdef class PythonResourceFormatLoader: |
85 | 89 | # godot_extension: method(virtual=True, const=True) |
86 | 90 | cdef gd_variant_t _load(self, gd_string_t path, gd_string_t original_path, gd_bool_t use_sub_threads, gd_int_t cache_mode): |
87 | 91 | cdef gd_variant_t ret = gd_variant_new() |
88 | | - cdef object py_path = gdapi.gd_string_to_pystr(&path) |
89 | | - cdef object py_original_path = gdapi.gd_string_to_pystr(&original_path) |
| 92 | + cdef str py_path = gdapi.gd_string_to_pystr(&path) |
| 93 | + cdef str py_original_path = gdapi.gd_string_to_pystr(&original_path) |
90 | 94 | gd_string_del(&path) |
91 | 95 | gd_string_del(&original_path) |
92 | 96 | spy_log(f"CALLED PythonResourceFormatLoader::_load(path={py_path!r}, original_path={py_original_path!r}, use_sub_threads={use_sub_threads}, cache_mode={cache_mode})") |
93 | 97 |
|
| 98 | + # 1) Check path and convert it to Python format (e.g. `res://foo/bar.py` -> `foo.bar`) |
| 99 | + |
| 100 | + if not ( |
| 101 | + py_path.startswith("res://") and |
| 102 | + ( |
| 103 | + py_path.endswith(".py") or |
| 104 | + py_path.endswith(".pyc") or |
| 105 | + py_path.endswith(".pyo") or |
| 106 | + py_path.endswith(".pyd") |
| 107 | + ) |
| 108 | + ): |
| 109 | + print( |
| 110 | + f"Bad python script path `{py_path}`, must starts by `res://` and ends with `.py/pyc/pyo/pyd`", flush=True |
| 111 | + ) |
| 112 | + return gdapi.gd_int_into_variant(Error.ERR_FILE_BAD_PATH) |
| 113 | + |
| 114 | + # TODO: possible bug if res:// is not part of PYTHONPATH |
| 115 | + # Remove `res://`, `.py` and replace / by . |
| 116 | + modname = py_path[len("res://"):].rsplit(".", 1)[0].replace("/", ".") |
| 117 | + |
| 118 | + try: |
| 119 | + importlib.import_module(modname) # Force lazy loading of the module |
| 120 | + klass = _get_exposed_class(modname) # `_get_exposed_class` defined in `_lang_tags.pxi` |
| 121 | + print('=========> new Godot-Python class', klass) |
| 122 | + |
| 123 | + except BaseException: |
| 124 | + # If we are here it could be because the file doesn't exists |
| 125 | + # or (more possibly) the file content is not valid python (or |
| 126 | + # doesn't provide an exposed class) |
| 127 | + print( |
| 128 | + f"Got exception loading `{py_path}` (aka `{modname}`): {traceback.format_exc()}", flush=True |
| 129 | + ) |
| 130 | + return gdapi.gd_int_into_variant(Error.ERR_PARSE_ERROR) |
| 131 | + |
| 132 | + if klass is None: |
| 133 | + print( |
| 134 | + f"Cannot load `{py_path}` (aka `{modname}`) because it doesn't expose any class to Godot", flush=True |
| 135 | + ) |
| 136 | + return gdapi.gd_int_into_variant(Error.ERR_PARSE_ERROR) |
| 137 | + |
| 138 | + |
| 139 | + |
94 | 140 | cdef PythonScript script |
95 | | - cdef gd_string_t gd_source |
96 | | - cdef gd_string_t gd_script_path |
97 | | - cdef GDString source_code |
98 | | - |
99 | | - # Load the source code from file |
100 | | - |
101 | | - from godot.classes import FileAccess |
102 | | - # TODO: use `path` directly ! |
103 | | - cdef object file = FileAccess.open(GDString(py_path), FileAccess.ModeFlags.READ.value) |
104 | | - if file is None: |
105 | | - spy_log(f"Failed to load Python script {py_original_path}: cannot open file {path}") |
106 | | - # If file loading fails, return the nil variant (already initialized) |
107 | | - return ret |
108 | | - # TODO: what happen if the text is not UTF8 ? |
109 | | - source_code = file.get_as_text() |
| 141 | + # cdef gd_string_t gd_source |
| 142 | + # cdef gd_string_t gd_script_path |
| 143 | + # cdef GDString source_code |
| 144 | + |
| 145 | + # # Load the source code from file |
| 146 | + |
| 147 | + # from godot.classes import FileAccess |
| 148 | + # # TODO: use `path` directly ! |
| 149 | + # cdef object file = FileAccess.open(GDString(py_path), FileAccess.ModeFlags.READ.value) |
| 150 | + # if file is None: |
| 151 | + # spy_log(f"Failed to load Python script {py_original_path}: cannot open file {path}") |
| 152 | + # # If file loading fails, return the nil variant (already initialized) |
| 153 | + # return ret |
| 154 | + # # TODO: what happen if the text is not UTF8 ? |
| 155 | + # source_code = file.get_as_text() |
110 | 156 |
|
111 | 157 | # Create a new script instance from the source code |
112 | 158 |
|
113 | | - script = PythonScript() |
114 | | - script._set_source_code(source_code.into_gd_data()) |
115 | | - # `into_gd_data()` steal the underlying Godot string, so `source_code` |
116 | | - # ends up containing nothing and we'd rather destroy it early to avoid |
117 | | - # confusions. |
118 | | - del source_code |
| 159 | + script = PythonScript() # `__cinit__()` initializes `script._gd_ptr` |
| 160 | + script._py_cls = klass |
| 161 | + script._script_instance_info = generate_instance_info() |
| 162 | + # script._set_source_code(source_code.into_gd_data()) |
| 163 | + # # `into_gd_data()` steal the underlying Godot string, so `source_code` |
| 164 | + # # ends up containing nothing and we'd rather destroy it early to avoid |
| 165 | + # # confusions. |
| 166 | + # del source_code |
119 | 167 |
|
120 | 168 | # Return the script as a variant |
121 | 169 |
|
|
0 commit comments