finish move to Python-handled imports
[Tsunagari.git] / src / script-python.cpp
blob9243ac4097959d4d452cd699d3237d1b310f627e
1 /***************************************
2 ** Tsunagari Tile Engine **
3 ** script-python.cpp **
4 ** Copyright 2011-2013 PariahSoft LLC **
5 ***************************************/
7 // **********
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to
10 // deal in the Software without restriction, including without limitation the
11 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 // sell copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 // IN THE SOFTWARE.
25 // **********
27 #include <Python.h>
29 #include "log.h"
30 #include "python.h"
31 #include "reader.h"
32 #include "script-python.h"
35 template<>
36 ScriptRef Script::create(std::string source)
38 std::string s_file, s_func;
39 size_t colon;
40 PyObject* module = NULL;
41 PythonScript* script;
43 colon = source.find(':');
44 if (colon != std::string::npos) {
45 s_file = source.substr(0, colon);
46 s_func = source.substr(colon + 1);
48 else {
49 s_file = source;
52 if ((module = PyImport_ImportModule(s_file.c_str())) == NULL) {
53 Log::err("Script", s_file + ": import failed");
54 goto err;
57 script = new PythonScript;
58 script->module = module;
59 script->function = s_func;
60 return ScriptRef(script);
62 err:
63 pythonErr();
65 Py_XDECREF(module);
66 return ScriptRef();
70 template<>
71 ScriptRef Script::create(const char* source)
73 return Script::create(std::string(source));
77 PythonScript::PythonScript()
78 : module(NULL)
83 PythonScript::~PythonScript()
85 Py_DECREF(module);
89 bool PythonScript::validate()
91 // Always valid...?
92 return true;
96 bool PythonScript::invoke()
98 assert(function.size());
100 PyObject* result = NULL;
102 result = PyObject_CallMethod(module, (char*)function.c_str(), (char*)"()");
103 if (result == NULL) {
104 pythonErr();
105 return false;
107 Py_DECREF(result);
108 return true;
112 /* The code below has support for being bound from inside of Python
115 ScriptInst::ScriptInst(const std::string& source)
116 : dataType(BYTECODE_REF), data(Reader::getBytecode(source))
118 if (!validate()) {
119 Log::err("ScriptInst", "Error loading " + source);
124 ScriptInst::ScriptInst(boost::python::object& callable)
125 : dataType(BOOST_PY_OBJ), data(callable)
130 ScriptInst::ScriptInst(const ScriptInst& s)
131 : dataType(s.dataType)
133 switch (dataType) {
134 case BYTECODE_REF:
135 data = s.data.bcr;
136 break;
137 case BOOST_PY_OBJ:
138 data = s.data.bpo;
139 break;
144 ScriptInst::~ScriptInst()
146 switch (dataType) {
147 case BYTECODE_REF:
148 data = s.data.bcr;
149 break;
150 case BOOST_PY_OBJ:
151 data = s.data.bpo;
152 break;
157 bool ScriptInst::validate()
159 BytecodeRef& bc = *(BytecodeRef*)data;
161 switch (dataType) {
162 case BYTECODE_REF:
163 if (!bc) {
164 Log::err("ScriptInst", "<null script>: script not valid");
165 return false;
167 if (!bc->valid()) {
168 Log::err("ScriptInst", bc->filename() +
169 ": script not valid");
170 return false;
172 return true;
173 case BOOST_PY_OBJ:
174 return true;
175 default:
176 Log::fatal("ScriptInstInternal", "validate(): unknown data type");
177 return false;
182 bool ScriptInst::invoke()
184 BytecodeRef& bc = *(BytecodeRef*)data;
185 boost::python::object& callable = *(boost::python::object*)data;
187 switch (dataType) {
188 case BYTECODE_REF:
189 return (bc && bc->valid()) ? bc->execute() : false;
190 case BOOST_PY_OBJ:
191 try {
192 inPythonScript++;
193 callable();
194 inPythonScript--;
195 return true;
196 } catch (boost::python::error_already_set) {
197 inPythonScript--;
198 // XXX: How does this interact with a C++/Python callstack?
199 //Log::err("Python", "Originating from " + source + ":");
200 pythonErr();
201 return false;
203 default:
204 Log::fatal("ScriptInstInternal", "invoke(): unknown data type");
205 return false;
210 struct scriptinst_to_python
212 static PyObject* convert(ScriptInst script)
214 BytecodeRef& bc = *(BytecodeRef*)data;
215 boost::python::object& callable = *(boost::python::object*)data;
217 switch (dataType) {
218 case BYTECODE_REF:
219 boost::python::object str;
220 if (bc)
221 str = boost::python::object(bc->filename());
222 else
223 str = boost::python::object("");
224 return boost::python::incref(str.ptr());
225 case BOOST_PY_OBJ:
226 return boost::python::incref(callable.ptr());
227 default:
228 Log::fatal("ScriptInstInternal", "to_python: convert(): unknown data type");
229 return false;
235 struct scriptinst_from_python
237 scriptinst_from_python()
239 boost::python::converter::registry::push_back(
240 &convertible,
241 &construct,
242 boost::python::type_id<ScriptInst>());
245 // Can this be converted to a ScriptInst?
246 static void* convertible(PyObject* obj)
248 //bool callable = obj->ob_type->tp_call != NULL;
249 //const char* tp_name = obj->ob_type->tp_name;
250 // XXX: Return non-NULL only if string or
251 // callable (fn or lambda?).
252 return obj;
255 // Convert. boost::python provides us with a chunch of memory that we
256 // have to construct in-place.
257 static void construct(
258 PyObject* obj,
259 boost::python::converter::rvalue_from_python_stage1_data* data)
261 // Prevent compilation name collisions with "object" by making
262 // it "bp::object".
263 namespace bp = boost::python;
265 void* storage =
266 ((bp::converter::rvalue_from_python_storage<ScriptInst>*)data)
267 ->storage.bytes;
269 if (PyString_Check(obj)) {
270 const char* value = PyString_AsString(obj);
271 new (storage) ScriptInst(value);
273 else {
274 // By default, the PyObject is a borrowed reference,
275 // which means it hasn't been incref'd.
276 bp::handle<> hndl(bp::borrowed(obj));
277 new (storage) ScriptInst(bp::object(hndl));
280 data->convertible = storage;
285 void exportScriptInst()
287 using namespace boost::python;
289 to_python_converter<ScriptInst, scriptinst_to_python>();
290 scriptinst_from_python();