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