SourceXtractorPlusPlus 1.0.3
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
PythonInterpreter.cpp
Go to the documentation of this file.
1/*
2 * Copyright © 2019-2022 Université de Genève, LMU Munich - Faculty of Physics, IAP-CNRS/Sorbonne Université
3 *
4 * This library is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License as published by the Free
6 * Software Foundation; either version 3.0 of the License, or (at your option)
7 * any later version.
8 *
9 * This library is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 * details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18/*
19 * @file PythonInterpreter.cpp
20 * @author Nikolaos Apostolakos <nikoapos@gmail.com>
21 */
22
23#include <Pyston/Exceptions.h>
24#include <Pyston/GIL.h>
25#include <Pyston/Module.h>
26#include <Python.h>
29#include <boost/python/dict.hpp>
30#include <boost/python/exec.hpp>
31#include <boost/python/extract.hpp>
32#include <boost/python/import.hpp>
33#include <boost/python/object.hpp>
34#include <fstream>
35#include <signal.h>
36#include <system_error>
37#include <utility>
38
39namespace py = boost::python;
40
44
45namespace SourceXtractor {
46
48 static PythonInterpreter singleton{};
49 return singleton;
50}
51
53 // Python sets its own signal handler for SIGINT (Ctrl+C), so it can throw a KeyboardInterrupt
54 // Here we are not interested on this behaviour, so we get whatever handler we've got (normally
55 // the default one) and restore it after initializing the interpreter
56 struct sigaction sigint_handler;
57 sigaction(SIGINT, nullptr, &sigint_handler);
58
59 PyImport_AppendInittab("pyston", PYSTON_MODULE_INIT);
60 Py_Initialize();
61#if PY_VERSION_HEX < 3090000
62 PyEval_InitThreads();
63#endif
64 PyEval_SaveThread();
65
66 sigaction(SIGINT, &sigint_handler, nullptr);
67}
68
70 logger.info() << "Python GIL acquired " << Pyston::GILLocker::getLockCount() << " times";
71}
72
74 Pyston::GILLocker locker;
75
76 try {
77 // Setup argv
78 // Python expects to have the ownership!
79#if PY_MAJOR_VERSION == 2
80 using py_argv_char_t = char;
81#define py_argv_assign(d, s, l) d = strndup(s, l)
82#else
83 using py_argv_char_t = wchar_t;
84#define py_argv_assign(d, s, l) d = Py_DecodeLocale(s, &(l))
85#endif
86
87 py_argv_char_t** py_argv = static_cast<py_argv_char_t**>(PyMem_MALLOC((argv.size() + 1) * sizeof(py_argv_char_t*)));
88 size_t wlen = filename.size();
89 py_argv_assign(py_argv[0], filename.c_str(), wlen);
90 for (size_t i = 0; i < argv.size(); ++i) {
91 wlen = argv[i].size();
92 py_argv_assign(py_argv[i + 1], argv[i].c_str(), wlen);
93 }
94 PySys_SetArgv(argv.size() + 1, py_argv);
95
96 // Import ourselves so the conversions are registered
97 py::import("_SourceXtractorPy");
98
99 // Setup stdout and stderr
100 PySys_SetObject("stdout", py::object(boost::ref(m_out_wrapper)).ptr());
101 PySys_SetObject("stderr", py::object(boost::ref(m_err_wrapper)).ptr());
102
103 // Run the file
104 py::object main_module = py::import("__main__");
105 py::setattr(main_module, "__file__", py::object(filename));
106 py::object main_namespace = main_module.attr("__dict__");
107
108 // boost 1.75 up to 1.77 has a bug that trashes the heap
109 // See https://github.com/boostorg/python/issues/371
110 // So we read the file ourselves as a workaround
111 std::ifstream fs(filename);
112 if (fs.fail()) {
113 throw std::system_error(errno, std::system_category(), filename);
114 }
116 py::exec(pycode.c_str(), main_namespace);
117 } catch (const py::error_already_set& e) {
118 PyErr_Print();
119 throw Elements::Exception() << "An error occured while running the Python configuration script";
120 } catch (const std::system_error& e) {
121 throw Elements::Exception() << e.what() << ": " << e.code().message();
122 }
123}
124
126 Pyston::GILLocker locker;
127 try {
128 m_measurement_config = py::import("sourcextractor.config.measurement_config").attr("global_measurement_config");
129 } catch (const py::error_already_set& e) {
130 PyErr_Print();
131 throw Elements::Exception() << "An error occured while setting up the Python interpreter";
132 }
133}
134
136 Pyston::GILLocker locker;
137
138 try {
139 py::dict images = py::extract<py::dict>(m_measurement_config.attr("measurement_images"));
140 py::list ids = images.keys();
142 for (int i = 0; i < py::len(ids); ++i) {
143 int id = py::extract<int>(ids[i]);
144 PyMeasurementImage im = py::extract<PyMeasurementImage>(images[ids[i]]);
145 result.emplace(std::make_pair(id, im));
146 }
147 return result;
148 } catch (const py::error_already_set& e) {
149 PyErr_Print();
150 throw Elements::Exception() << "An error occurred while retrieving measurement images";
151 }
152}
153
155 Pyston::GILLocker locker;
156
157 try {
158 py::dict apertures = py::extract<py::dict>(m_measurement_config.attr("apertures_for_image"));
159 py::list ids = apertures.keys();
161 for (int i = 0; i < py::len(ids); ++i) {
162 int id = py::extract<int>(ids[i]);
163 PyAperture ap = py::extract<PyAperture>(apertures[ids[i]]);
164 result.emplace(std::make_pair(id, ap));
165 }
166 return result;
167 } catch (const py::error_already_set& e) {
168 PyErr_Print();
169 throw Elements::Exception() << "An error occurred while retrieving apertures";
170 }
171}
172
174 Pyston::GILLocker locker;
175
176 try {
177 py::list output = py::extract<py::list>(m_measurement_config.attr("model_fitting_parameter_columns"));
179 for (int i = 0; i < py::len(output); ++i) {
180 py::tuple t = py::extract<py::tuple>(output[i]);
181 std::string name = py::extract<std::string>(t[0]);
182 auto extract_list = py::extract<py::list>(t[1]);
183
184 std::vector<int> ids{};
185 if (extract_list.check()) {
186 py::list cs = extract_list;
187 for (int j = 0; j < py::len(cs); ++j) {
188 int c = py::extract<int>(cs[j].attr("id"));
189 ids.push_back(c);
190 }
191 } else {
192 int c = py::extract<int>(t[1]);
193 ids.push_back(c);
194 }
195 result.emplace_back(name, std::move(ids));
196 }
197 return result;
198 } catch (const py::error_already_set& e) {
199 PyErr_Print();
200 throw Elements::Exception() << "An error occurred while retrieving model fitting output columns";
201 }
202}
203
205 Pyston::GILLocker locker;
206
207 try {
208 py::list output = py::extract<py::list>(m_measurement_config.attr("aperture_columns"));
210 for (int i = 0; i < py::len(output); ++i) {
211 py::tuple t = py::extract<py::tuple>(output[i]);
212 std::string name = py::extract<std::string>(t[0]);
213 auto extract_list = py::extract<py::list>(t[1]);
214
215 if (extract_list.check()) {
216 py::list cs = extract_list;
217 for (int j = 0; j < py::len(cs); ++j) {
218 int c = py::extract<int>(cs[j].attr("id"));
219 result[name].push_back(c);
220 }
221 } else {
222 int c = py::extract<int>(t[1]);
223 result[name].push_back(c);
224 }
225 }
226 return result;
227 } catch (const py::error_already_set& e) {
228 PyErr_Print();
229 throw Elements::Exception() << "An error occurred while retrieving aperture output columns";
230 }
231}
232
234 const char* dict_name) {
235 Pyston::GILLocker locker;
236
237 try {
238 py::object obj = m_measurement_config.attr(object_name);
239 py::dict parameters = py::extract<py::dict>(obj.attr(dict_name));
240 py::list ids = parameters.keys();
241
243 for (int i = 0; i < py::len(ids); ++i) {
244 int id = py::extract<int>(ids[i]);
245 auto par = parameters[ids[i]];
246 result.emplace(std::make_pair(id, par));
247 }
248 return result;
249 } catch (const py::error_already_set& e) {
250 PyErr_Print();
251 throw Elements::Exception() << "An error occurred while retrieving map from dict";
252 }
253}
254
256 return getMapFromDict("model_fitting", "constant_parameter_dict");
257}
258
260 return getMapFromDict("model_fitting", "free_parameter_dict");
261}
262
264 return getMapFromDict("model_fitting", "dependent_parameter_dict");
265}
266
268 return getMapFromDict("model_fitting", "prior_dict");
269}
270
272 return getMapFromDict("model_fitting", "constant_model_dict");
273}
274
276 return getMapFromDict("model_fitting", "point_source_model_dict");
277}
278
280 return getMapFromDict("model_fitting", "sersic_model_dict");
281}
282
284 return getMapFromDict("model_fitting", "exponential_model_dict");
285}
286
288 return getMapFromDict("model_fitting", "de_vaucouleurs_model_dict");
289}
290
292 return getMapFromDict("model_fitting", "onnx_model_dict");
293}
294
296 Pyston::GILLocker locker;
297 try {
299 py::object model_fitting = m_measurement_config.attr("model_fitting");
300 py::dict frame_dict = py::extract<py::dict>(model_fitting.attr("frame_models_dict"));
301 py::list frame_ids = frame_dict.keys();
302 for (int i = 0; i < py::len(frame_ids); ++i) {
303 int frame_id = py::extract<int>(frame_ids[i]);
304 py::list model_ids = py::extract<py::list>(frame_dict[frame_ids[i]]);
305 for (int j = 0; j < py::len(model_ids); ++j) {
306 int model_id = py::extract<int>(model_ids[j]);
307 result[frame_id].push_back(model_id);
308 }
309 }
310 return result;
311 } catch (const py::error_already_set& e) {
312 PyErr_Print();
313 throw Elements::Exception() << "An error occurred while retrieving frame models map";
314 }
315}
316
318 Pyston::GILLocker locker;
319
320 py::object model_fitting = m_measurement_config.attr("model_fitting");
321 py::dict parameters = py::extract<py::dict>(model_fitting.attr("params_dict"));
322 py::list ids = parameters.keys();
323
325 for (int i = 0; i < py::len(ids); ++i) {
326 std::string id = py::extract<std::string>(ids[i]);
327 auto par = parameters[ids[i]];
328 result.emplace(std::make_pair(id, par));
329 }
330 return result;
331}
332
333} // namespace SourceXtractor
static Elements::Logging logger
#define PYSTON_MODULE_INIT
static Elements::Logging stdout_logger
#define py_argv_assign(d, s, l)
static Elements::Logging stderr_logger
T c_str(T... args)
static Logging getLogger(const std::string &name="")
static size_t getLockCount()
std::map< int, boost::python::object > getDeVaucouleursModels()
std::map< int, PyAperture > getApertures()
std::map< int, boost::python::object > getMapFromDict(const char *object_name, const char *dict_name)
std::map< std::string, std::vector< int > > getApertureOutputColumns()
std::map< int, boost::python::object > getDependentParameters()
std::vector< std::pair< std::string, std::vector< int > > > getModelFittingOutputColumns()
std::map< int, boost::python::object > getSersicModels()
std::map< int, boost::python::object > getPriors()
std::map< int, boost::python::object > getFreeParameters()
static PythonInterpreter & getSingleton()
std::map< int, boost::python::object > getOnnxModels()
std::map< int, boost::python::object > getExponentialModels()
std::map< int, boost::python::object > getConstantParameters()
void runFile(const std::string &filename, const std::vector< std::string > &argv)
boost::python::object m_measurement_config
std::map< int, boost::python::object > getConstantModels()
std::map< int, std::vector< int > > getFrameModelsMap()
std::map< int, PyMeasurementImage > getMeasurementImages()
std::map< int, boost::python::object > getPointSourceModels()
std::map< std::string, boost::python::object > getModelFittingParams()
T emplace_back(T... args)
T emplace(T... args)
T make_pair(T... args)
T move(T... args)
static Elements::Logging logger
T push_back(T... args)
T size(T... args)
T system_category(T... args)