reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
PyExtensionBase.cxx
Go to the documentation of this file.
1
2// NOTE: Python.h must be included before any standard headers
3// See: https://bugzilla.redhat.com/show_bug.cgi?id=518385
4
5/* Using the PyCXX API for C++ Python integration
6 * It is extremely convenient and avoids the need to write boilerplate
7 * code for handling the Python reference counts.
8 * It is licensed under BSD terms compatible with reSIProcate */
9#include <Python.h>
10#include <CXX/Objects.hxx>
11#include <CXX/Extensions.hxx>
12
13#include <memory>
14#include <utility>
15
16#include "rutil/Logger.hxx"
17
18#include "PyExtensionBase.hxx"
19#include "PyThreadSupport.hxx"
20
21#define RESIPROCATE_SUBSYSTEM resip::Subsystem::PYTHON
22
23using namespace resip;
24
25// FIXME "resip"
27 : Py::ExtensionModule<PyExtensionBase>(exportModuleName.c_str()),
28 mExportModuleName(exportModuleName),
29 mThreadState(nullptr),
30 mLogPrefix(mExportModuleName)
31{
32}
33
35{
36 if(mThreadState)
37 {
38 PyEval_RestoreThread(mThreadState);
39 // this can take time as it de-initializes all Python state
40 DebugLog(<<"Calling Py_Finalize");
41 Py_Finalize();
42 DebugLog(<<"Py_Finalize is done");
43 }
44}
45
46bool
47PyExtensionBase::checkPyArgs(const resip::Data& method, const Py::Tuple &args, unsigned int minArgs, unsigned int maxArgs)
48{
49 DebugLog(<<"python invoked C method: " << method);
50 if(args.size() < minArgs)
51 {
52 ErrLog(<< method << " called with insufficient arguments");
53 return false;
54 }
55 if(args.size() > maxArgs)
56 {
57 ErrLog(<< method << " called with excess arguments, only using first argument(s)");
58 }
59 return true;
60}
61
62Py::Object
63PyExtensionBase::logStack(const Py::Tuple &args)
64{
65 if(!checkPyArgs("log_stack", args, 1, 1))
66 {
67 throw Py::TypeError("insufficient number of arguments");
68 }
69 const Py::String& text(args[0]);
70 StackLog(<< '[' << mLogPrefix << "] " << text);
71 return Py::None();
72}
73
74Py::Object
75PyExtensionBase::logDebug(const Py::Tuple &args)
76{
77 if(!checkPyArgs("log_debug", args, 1, 1))
78 {
79 throw Py::TypeError("insufficient number of arguments");
80 }
81 const Py::String& text(args[0]);
82 DebugLog(<< '[' << mLogPrefix << "] " << text);
83 return Py::None();
84}
85
86Py::Object
87PyExtensionBase::logInfo(const Py::Tuple &args)
88{
89 if(!checkPyArgs("log_info", args, 1, 1))
90 {
91 throw Py::TypeError("insufficient number of arguments");
92 }
93 const Py::String& text(args[0]);
94 InfoLog(<< '[' << mLogPrefix << "] " << text);
95 return Py::None();
96}
97
98Py::Object
99PyExtensionBase::logWarning(const Py::Tuple &args)
100{
101 if(!checkPyArgs("log_warning", args, 1, 1))
102 {
103 throw Py::TypeError("insufficient number of arguments");
104 }
105 const Py::String& text(args[0]);
106 WarningLog(<< '[' << mLogPrefix << "] " << text);
107 return Py::None();
108}
109
110Py::Object
111PyExtensionBase::logErr(const Py::Tuple &args)
112{
113 if(!checkPyArgs("log_err", args, 1, 1))
114 {
115 throw Py::TypeError("insufficient number of arguments");
116 }
117 const Py::String& text(args[0]);
118 ErrLog(<< '[' << mLogPrefix << "] " << text);
119 return Py::None();
120}
121
122Py::Object
123PyExtensionBase::logCrit(const Py::Tuple &args)
124{
125 if(!checkPyArgs("log_crit", args, 1, 1))
126 {
127 throw Py::TypeError("insufficient number of arguments");
128 }
129 const Py::String& text(args[0]);
130 CritLog(<< '[' << mLogPrefix << "] " << text);
131 return Py::None();
132}
133
134void
136{
137 // Initialize the ExtensionModule superclass
138 add_varargs_method("log_stack", &PyExtensionBase::logDebug, "log_stack(arglist) = log a stack message");
139 add_varargs_method("log_debug", &PyExtensionBase::logDebug, "log_debug(arglist) = log a debug message");
140 add_varargs_method("log_info", &PyExtensionBase::logDebug, "log_info(arglist) = log a info message");
141 add_varargs_method("log_warning", &PyExtensionBase::logWarning, "log_warning(arglist) = log a warning message");
142 add_varargs_method("log_err", &PyExtensionBase::logErr, "log_err(arglist) = log a debug message");
143 add_varargs_method("log_crit", &PyExtensionBase::logErr, "log_crit(arglist) = log a debug message");
144}
145
146void
148{
149 PyObject *sys_path = PySys_GetObject("path");
150 PyObject *addpath = PyUnicode_FromString(path.c_str());
151 PyList_Append(sys_path, addpath);
152}
153
154bool
156{
157 if (PyErr_Occurred()) {
158 Py::Exception ex;
159 ErrLog(<< "Python exception: " << Py::value(ex));
160 return true;
161 }
162 return false;
163}
164
165void
166PyExtensionBase::importModule(PyObject *pyModule, const resip::Data& moduleImport)
167{
168 PyObject *thisModule = module().ptr();
169 PyDict_SetItemString(PyModule_GetDict(pyModule), moduleImport.c_str(), thisModule);
170}
171
172std::unique_ptr<Py::Module>
174{
175 PyObject *_pyModule = PyImport_ImportModule(moduleName.c_str());
176 if(!_pyModule)
177 {
178 ErrLog(<<"Failed to load module "<< moduleName);
180 }
181
182 // Using the line
183 // import resip
184 // in a script fails with an error "No module named 'resip'".
185 // Therefore, we force it to be included in the dictionary for the
186 // script we are invoking.
187 importModule(_pyModule, "resip");
188
189 std::unique_ptr<Py::Module> pyModule(new Py::Module(_pyModule));
190
191 return pyModule;
192}
193
194std::unique_ptr<PyExternalUser>
196{
197 std::unique_ptr<PyExternalUser> p(new PyExternalUser(mInterpreterState));
198 return p;
199}
200
201bool
203{
204 // FIXME: what if there are other Python modules?
205 Py_Initialize();
206#if PY_VERSION_HEX < 0x03070000
207 // Since Python 3.7: GIL is initialzed in Py_Initialize()
208 // Since Python 3.9: PyEval_InitThreads() is deprecated
209 PyEval_InitThreads();
210#endif
211
212 initMethods();
213 initialize("reSIProcate SIP stack API callbacks");
214
215 appendPath(pyPath);
216
217 //mThreadState = PyGILState_GetThisThreadState();
218 mThreadState = PyThreadState_GET();
219
220#if PY_VERSION_HEX < 0x03090000
221 mInterpreterState = mThreadState->interp;
222#else
223 //mInterpreterState = PyThreadState_GetInterpreter(mThreadState);
224 mInterpreterState = PyInterpreterState_Get();
225#endif
226
227 if(!onStartup())
228 {
229 // FIXME - use PyThreadSupport or similar scoped lock mechanism
230 PyEval_ReleaseThread(mThreadState);
231 return false;
232 }
233
234 // FIXME - use PyThreadSupport or similar scoped lock mechanism
235 PyEval_ReleaseThread(mThreadState);
236
237 return true;
238}
239
240
241/* ====================================================================
242
243 Copyright (c) 2022, Software Freedom Institute https://softwarefreedom.institute
244 Copyright (c) 2013-2022, Daniel Pocock https://danielpocock.com
245 All rights reserved.
246
247 Redistribution and use in source and binary forms, with or without
248 modification, are permitted provided that the following conditions are
249 met:
250
251 1. Redistributions of source code must retain the above copyright
252 notice, this list of conditions and the following disclaimer.
253
254 2. Redistributions in binary form must reproduce the above copyright
255 notice, this list of conditions and the following disclaimer in the
256 documentation and/or other materials provided with the distribution.
257
258 3. Neither the name of Plantronics nor the names of its contributors
259 may be used to endorse or promote products derived from this
260 software without specific prior written permission.
261
262 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
263 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
264 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
265 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
266 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
267 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
268 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
269 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
270 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
271 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
272 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
273
274 ==================================================================== */
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define CritLog(args_)
Definition Logger.hxx:140
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
virtual bool onStartup()=0
virtual std::unique_ptr< Py::Module > loadModule(const resip::Data &moduleName)
virtual void importModule(PyObject *pyModule, const resip::Data &moduleImport)
virtual bool checkPyArgs(const resip::Data &method, const Py::Tuple &args, unsigned int minArgs, unsigned int maxArgs)
Py::Object logErr(const Py::Tuple &args)
Py::Object logStack(const Py::Tuple &args)
Py::Object logWarning(const Py::Tuple &args)
virtual bool logPythonErrorIfAny()
Py::Object logDebug(const Py::Tuple &args)
std::unique_ptr< PyExternalUser > createPyExternalUser()
Py::Object logCrit(const Py::Tuple &args)
virtual void appendPath(const resip::Data &path)
virtual bool init(const resip::Data &pyPath)
PyExtensionBase(const resip::Data &exportModuleName)
Py::Object logInfo(const Py::Tuple &args)
dcm! – add UnusedChecking(_enum) below;