1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24
25 #include "pxr/pxr.h"
26
27 #include "pxr/base/tf/pyError.h"
28 #include "pxr/base/tf/pyInterpreter.h"
29 #include "pxr/base/tf/pyLock.h"
30 #include "pxr/base/tf/pyUtils.h"
31 #include "pxr/base/tf/scriptModuleLoader.h"
32 #include "pxr/base/tf/stringUtils.h"
33
34 #include "pxr/base/arch/fileSystem.h"
35 #include "pxr/base/arch/symbols.h"
36 #include "pxr/base/arch/systemInfo.h"
37 #include "pxr/base/arch/threads.h"
38
39 #include <boost/python.hpp>
40 #include <boost/python/detail/api_placeholder.hpp>
41 #include <atomic>
42 #include <mutex>
43 #include <string>
44 #include "pxr/base/tf/pySafePython.h"
45 #include <signal.h>
46
47 using std::string;
48
49 using namespace boost::python;
50
51 PXR_NAMESPACE_OPEN_SCOPE
52
53 void
TfPyInitialize()54 TfPyInitialize()
55 {
56 static std::atomic<bool> initialized(false);
57 if (initialized)
58 return;
59
60 // This mutex is, sadly, recursive since the call to the scriptModuleLoader
61 // at the end of this function can end up reentering this function, while
62 // importing python modules. In this case we'll quickly return since
63 // Py_IsInitialized will return true, but we need to keep other threads from
64 // entering.
65 static std::recursive_mutex mutex;
66 std::lock_guard<std::recursive_mutex> lock(mutex);
67
68 if (!Py_IsInitialized()) {
69
70 if (!ArchIsMainThread() && !PyEval_ThreadsInitialized()) {
71 // Python claims that PyEval_InitThreads "should be called in the
72 // main thread before creating a second thread or engaging in any
73 // other thread operations." So we'll issue a warning here.
74 TF_WARN("Calling PyEval_InitThreads() for the first time outside "
75 "the 'main thread'. Python doc says not to do this.");
76 }
77
78 const std::string s = ArchGetExecutablePath();
79
80 #if PY_MAJOR_VERSION == 2
81 // In Python 2 it is safe to call this before Py_Initialize(), but this
82 // is no longer true in python 3.
83 //
84 // Initialize Python threading. This grabs the GIL. We'll release it
85 // at the end of this function.
86 PyEval_InitThreads();
87 #endif
88
89 // Setting the program name is necessary in order for python to
90 // find the correct built-in modules.
91 #if PY_MAJOR_VERSION == 2
92 static std::string programName(s.begin(), s.end());
93 Py_SetProgramName(const_cast<char*>(programName.c_str()));
94 #else
95 static std::wstring programName(s.begin(), s.end());
96 Py_SetProgramName(const_cast<wchar_t*>(programName.c_str()));
97 #endif
98
99 // We're here when this is a C++ program initializing python (i.e. this
100 // is a case of "embedding" a python interpreter, as opposed to
101 // "extending" python with extension modules).
102 //
103 // In this case we don't want python to change the sigint handler. Save
104 // it before calling Py_Initialize and restore it after.
105 #if !defined(ARCH_OS_WINDOWS)
106 struct sigaction origSigintHandler;
107 sigaction(SIGINT, NULL, &origSigintHandler);
108 #endif
109 Py_Initialize();
110
111 #if !defined(ARCH_OS_WINDOWS)
112 // Restore original sigint handler.
113 sigaction(SIGINT, &origSigintHandler, NULL);
114 #endif
115
116 #if PY_MAJOR_VERSION > 2
117 // In python 3 PyEval_InitThreads must be called after Py_Initialize()
118 // see https://docs.python.org/3/c-api/init.html
119 //
120 // Initialize Python threading. This grabs the GIL. We'll release it
121 // at the end of this function.
122 PyEval_InitThreads();
123 #endif
124
125 #if PY_MAJOR_VERSION == 2
126 char emptyArg[] = {'\0'};
127 char *empty[] = { emptyArg };
128 #else
129 wchar_t emptyArg[] = { '\0' };
130 wchar_t *empty[] = { emptyArg };
131 #endif
132 PySys_SetArgv(1, empty);
133
134 // Kick the module loading mechanism for any loaded libs that have
135 // corresponding python binding modules. We do this after we've
136 // published that we're done initializing as this may reenter
137 // TfPyInitialize().
138 TfScriptModuleLoader::GetInstance().LoadModules();
139
140 // Release the GIL and restore thread state.
141 // When TfPyInitialize returns, we expect GIL is released
142 // and python's internal PyThreadState is NULL
143 // Previously this only released the GIL without resetting the ThreadState
144 // This can lead to a situation where python executes without the GIL
145 // PyGILState_Ensure checks the current thread state and decides
146 // to take the lock based on that; so if the GIL is released but the
147 // current thread is valid it leads to cases where python executes
148 // without holding the GIL and mismatched lock/release calls in TfPyLock
149 // (See the discussion in 141041)
150
151 PyThreadState* currentState = PyGILState_GetThisThreadState();
152 PyEval_ReleaseThread(currentState);
153
154 // Say we're done initializing python.
155 initialized = true;
156 }
157 }
158
159 int
TfPyRunSimpleString(const std::string & cmd)160 TfPyRunSimpleString(const std::string & cmd)
161 {
162 TfPyInitialize();
163 TfPyLock pyLock;
164 return PyRun_SimpleString(cmd.c_str());
165 }
166
167 boost::python::handle<>
TfPyRunString(const std::string & cmd,int start,object const & globals,object const & locals)168 TfPyRunString(const std::string &cmd , int start,
169 object const &globals, object const &locals)
170 {
171 TfPyInitialize();
172 TfPyLock pyLock;
173 try {
174 handle<> mainModule(borrowed(PyImport_AddModule("__main__")));
175 handle<>
176 defaultGlobalsHandle(borrowed(PyModule_GetDict(mainModule.get())));
177
178 PyObject *pyGlobals =
179 TfPyIsNone(globals) ? defaultGlobalsHandle.get() : globals.ptr();
180 PyObject *pyLocals =
181 TfPyIsNone(locals) ? pyGlobals : locals.ptr();
182
183 // used passed-in objects for globals and locals, or default
184 // to globals from main module if no locals/globals passed in.
185 return handle<>(PyRun_String(cmd.c_str(), start, pyGlobals, pyLocals));
186 } catch (error_already_set const &) {
187 TfPyConvertPythonExceptionToTfErrors();
188 PyErr_Clear();
189 }
190 return handle<>();
191 }
192
193 boost::python::handle<>
TfPyRunFile(const std::string & filename,int start,object const & globals,object const & locals)194 TfPyRunFile(const std::string &filename, int start,
195 object const &globals, object const &locals)
196 {
197 FILE *f = ArchOpenFile(filename.c_str(), "r");
198 if (!f) {
199 TF_CODING_ERROR("Could not open file '%s'!", filename.c_str());
200 return handle<>();
201 }
202
203 TfPyInitialize();
204 TfPyLock pyLock;
205 try {
206 handle<> mainModule(borrowed(PyImport_AddModule("__main__")));
207 handle<>
208 defaultGlobalsHandle(borrowed(PyModule_GetDict(mainModule.get())));
209
210 // used passed-in objects for globals and locals, or default
211 // to globals from main module if no locals/globals passed in.
212 PyObject *pyGlobals =
213 TfPyIsNone(globals) ? defaultGlobalsHandle.get() : globals.ptr();
214 PyObject *pyLocals =
215 TfPyIsNone(locals) ? pyGlobals : locals.ptr();
216
217 return handle<>(PyRun_FileEx(f, filename.c_str(), start,
218 pyGlobals, pyLocals, 1 /* close file */));
219 } catch (error_already_set const &) {
220 TfPyConvertPythonExceptionToTfErrors();
221 PyErr_Clear();
222 }
223 return handle<>();
224 }
225
226 std::string
TfPyGetModulePath(const std::string & moduleName)227 TfPyGetModulePath(const std::string & moduleName)
228 {
229 TfPyInitialize();
230
231 // Make sure imp is imported.
232 static std::once_flag once;
233 std::call_once(once, [](){
234 TfPyRunSimpleString("import imp\n");
235 });
236
237 // XXX
238 // If the module name is hierarchical (e.g. Animal.Primate.Chimp), then
239 // we have to walk the hierarchy and import all of the containing modules
240 // down the module we want to find.
241 // vector< string > pkgHierarchy = TfStringTokenize(moduleName, ".");
242
243 // Do the find_module.
244 std::string cmd =
245 TfStringPrintf("imp.find_module('%s')[1]\n", moduleName.c_str());
246 handle<> result = TfPyRunString(cmd, Py_eval_input);
247 if (!result)
248 return std::string();
249
250 // XXX close file
251
252 // Extract result string
253 extract<string> getString(result.get());
254 return getString.check() ? getString() : string();
255 }
256
257 PXR_NAMESPACE_CLOSE_SCOPE
258