Skip to content

Commit c344b90

Browse files
committed
Do not capture python stdout and stderr
When calling sourcextractor++ from Python
1 parent a308bc7 commit c344b90

File tree

5 files changed

+70
-48
lines changed

5 files changed

+70
-48
lines changed

SEImplementation/SEImplementation/Configuration/PythonConfig.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/**
22
* Copyright © 2019-2022 Université de Genève, LMU Munich - Faculty of Physics, IAP-CNRS/Sorbonne Université
33
*
44
* This library is free software; you can redistribute it and/or modify it under
@@ -15,7 +15,7 @@
1515
* along with this library; if not, write to the Free Software Foundation, Inc.,
1616
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1717
*/
18-
/*
18+
/*
1919
* @file PythonConfig.h
2020
* @author Nikolaos Apostolakos <[email protected]>
2121
*/
@@ -29,20 +29,21 @@
2929
namespace SourceXtractor {
3030

3131
class PythonConfig : public Euclid::Configuration::Configuration {
32-
32+
3333
public:
34-
34+
3535
explicit PythonConfig(long manager_id);
36-
36+
3737
std::map<std::string, Configuration::OptionDescriptionList> getProgramOptions() override;
3838

3939
void preInitialize(const UserValues& args) override;
4040

4141
void initialize(const UserValues& args) override;
42-
42+
4343
PythonInterpreter& getInterpreter() const;
4444

4545
private:
46+
bool m_capture_output = true;
4647
boost::python::object m_measurement_config;
4748
};
4849

SEImplementation/SEImplementation/PythonConfig/PythonInterpreter.h

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/**
22
* Copyright © 2019-2022 Université de Genève, LMU Munich - Faculty of Physics, IAP-CNRS/Sorbonne Université
33
*
44
* This library is free software; you can redistribute it and/or modify it under
@@ -15,7 +15,7 @@
1515
* along with this library; if not, write to the Free Software Foundation, Inc.,
1616
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1717
*/
18-
/*
18+
/*
1919
* @file PythonInterpreter.h
2020
* @author Nikolaos Apostolakos <[email protected]>
2121
*/
@@ -34,17 +34,20 @@
3434
namespace SourceXtractor {
3535

3636
class PythonInterpreter {
37-
37+
3838
public:
39-
39+
4040
static PythonInterpreter& getSingleton();
41-
41+
4242
void runFile(const std::string& filename, const std::vector<std::string>& argv);
4343

44+
/// Capture Python's stdout and stderr and pass the output through the logging
45+
void captureOutput();
46+
4447
void setupContext(boost::python::object config = {});
4548

4649
virtual ~PythonInterpreter();
47-
50+
4851
std::map<int, PyMeasurementImage> getMeasurementImages();
4952

5053
std::map<int, PyAperture> getApertures();
@@ -54,31 +57,31 @@ class PythonInterpreter {
5457
std::map<std::string, std::vector<int>> getApertureOutputColumns();
5558

5659
std::map<int, boost::python::object> getConstantParameters();
57-
60+
5861
std::map<int, boost::python::object> getFreeParameters();
59-
62+
6063
std::map<int, boost::python::object> getDependentParameters();
61-
64+
6265
std::map<int, boost::python::object> getPriors();
63-
66+
6467
std::map<int, boost::python::object> getConstantModels();
6568

6669
std::map<int, boost::python::object> getPointSourceModels();
67-
70+
6871
std::map<int, boost::python::object> getSersicModels();
69-
72+
7073
std::map<int, boost::python::object> getExponentialModels();
71-
74+
7275
std::map<int, boost::python::object> getDeVaucouleursModels();
73-
76+
7477
std::map<int, boost::python::object> getOnnxModels();
7578

7679
std::map<int, std::vector<int>> getFrameModelsMap();
7780

7881
std::map<std::string, boost::python::object> getModelFittingParams();
7982

8083
private:
81-
84+
8285
PythonInterpreter();
8386

8487
std::map<int, boost::python::object> getMapFromDict(const char* object_name,

SEImplementation/src/lib/Configuration/PythonConfig.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/**
22
* Copyright © 2019-2022 Université de Genève, LMU Munich - Faculty of Physics, IAP-CNRS/Sorbonne Université
33
*
44
* This library is free software; you can redistribute it and/or modify it under
@@ -31,7 +31,9 @@ namespace {
3131

3232
const std::string PYTHON_CONFIG_FILE{"python-config-file"};
3333
const std::string PYTHON_ARGV{"python-arg"};
34+
// These are internal and not exposed to the configuration manager
3435
const std::string PYTHON_CONFIG_OBJ{"python-config-object"};
36+
const std::string PYTHON_CAPTURE_OUTPUT{"python-capture-output"};
3537

3638
} // namespace
3739

@@ -58,13 +60,19 @@ void PythonConfig::preInitialize(const UserValues& args) {
5860
} else if (!filename.empty() && !fs::exists(filename)) {
5961
throw Elements::Exception() << "Python configuration file " << filename << " does not exist";
6062
}
63+
64+
auto py_capture_output = args.find(PYTHON_CAPTURE_OUTPUT);
65+
if (py_capture_output != args.end()) {
66+
m_capture_output = py_capture_output->second.as<bool>();
67+
}
6168
}
6269

6370
void PythonConfig::initialize(const UserValues& args) {
6471
auto& singleton = PythonInterpreter::getSingleton();
65-
if (m_measurement_config) {
66-
singleton.setupContext(m_measurement_config);
67-
} else {
72+
if (m_capture_output) {
73+
singleton.captureOutput();
74+
}
75+
if (!m_measurement_config) {
6876
auto filename = args.find(PYTHON_CONFIG_FILE)->second.as<std::string>();
6977
if (!filename.empty()) {
7078
std::vector<std::string> argv;

SEImplementation/src/lib/PythonConfig/PythonInterpreter.cpp

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,31 @@ PythonInterpreter& PythonInterpreter::getSingleton() {
5151

5252
PythonInterpreter::PythonInterpreter() : m_out_wrapper(stdout_logger), m_err_wrapper(stderr_logger) {
5353
// We may be called *from* Python!
54-
if (Py_IsInitialized()) {
55-
return;
56-
}
57-
// Python sets its own signal handler for SIGINT (Ctrl+C), so it can throw a KeyboardInterrupt
58-
// Here we are not interested on this behaviour, so we get whatever handler we've got (normally
59-
// the default one) and restore it after initializing the interpreter
60-
struct sigaction sigint_handler;
61-
sigaction(SIGINT, nullptr, &sigint_handler);
62-
63-
PyImport_AppendInittab("pyston", PYSTON_MODULE_INIT);
64-
Py_Initialize();
54+
if (!Py_IsInitialized()) {
55+
struct sigaction sigint_handler;
56+
57+
// Python sets its own signal handler for SIGINT (Ctrl+C), so it can throw a KeyboardInterrupt
58+
// Here we are not interested on this behaviour, so we get whatever handler we've got (normally
59+
// the default one) and restore it after initializing the interpreter
60+
sigaction(SIGINT, nullptr, &sigint_handler);
61+
62+
// Make pyston importable
63+
PyImport_AppendInittab("pyston", PYSTON_MODULE_INIT);
64+
65+
// Initialize Python
66+
Py_Initialize();
6567
#if PY_VERSION_HEX < 3090000
66-
PyEval_InitThreads();
68+
PyEval_InitThreads();
6769
#endif
68-
PyEval_SaveThread();
70+
PyEval_SaveThread();
71+
72+
// Restore SIGINT handler
73+
sigaction(SIGINT, &sigint_handler, nullptr);
74+
}
6975

70-
sigaction(SIGINT, &sigint_handler, nullptr);
76+
// Import ourselves so the conversions are registered
77+
Pyston::GILLocker locker;
78+
py::import("_SEPythonConfig");
7179
}
7280

7381
PythonInterpreter::~PythonInterpreter() {
@@ -97,13 +105,6 @@ void PythonInterpreter::runFile(const std::string& filename, const std::vector<s
97105
}
98106
PySys_SetArgv(argv.size() + 1, py_argv);
99107

100-
// Import ourselves so the conversions are registered
101-
py::import("_SEPythonConfig");
102-
103-
// Setup stdout and stderr
104-
PySys_SetObject("stdout", py::object(boost::ref(m_out_wrapper)).ptr());
105-
PySys_SetObject("stderr", py::object(boost::ref(m_err_wrapper)).ptr());
106-
107108
// Run the file
108109
py::object main_module = py::import("__main__");
109110
py::setattr(main_module, "__file__", py::object(filename));
@@ -125,6 +126,12 @@ void PythonInterpreter::runFile(const std::string& filename, const std::vector<s
125126
}
126127
}
127128

129+
void PythonInterpreter::captureOutput() {
130+
Pyston::GILLocker locker;
131+
PySys_SetObject("stdout", py::object(boost::ref(m_out_wrapper)).ptr());
132+
PySys_SetObject("stderr", py::object(boost::ref(m_err_wrapper)).ptr());
133+
}
134+
128135
void PythonInterpreter::setupContext(boost::python::object config) {
129136
Pyston::GILLocker locker;
130137
try {

SEPythonModule/src/lib/Context.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "SEImplementation/Configuration/DetectionFrameConfig.h"
2727
#include "SEImplementation/Configuration/MultiThreadingConfig.h"
2828
#include "SEImplementation/Configuration/OutputConfig.h"
29+
#include "SEImplementation/Configuration/PythonConfig.h"
2930
#include "SEImplementation/Deblending/DeblendingFactory.h"
3031
#include "SEImplementation/Grouping/GroupingFactory.h"
3132
#include "SEImplementation/Measurement/MeasurementFactory.h"
@@ -88,8 +89,10 @@ Context::Context(const py::dict& global_config, const py::object& measurement_co
8889
config_wrapper.fromPython(global_config);
8990
auto options = config_wrapper.getOptions();
9091

91-
// Override the python object to use for the measurement configuration
92-
options["python-config-object"].value() = boost::any(measurement_config);
92+
// Override the python object to use for the measurement configuration and disable output capture
93+
options["python-config-object"].value() = boost::any(measurement_config);
94+
options["python-capture-output"].value() = boost::any(false);
95+
9396
m_config_manager->initialize(options);
9497

9598
m_segmentation_factory->configure(*m_config_manager);

0 commit comments

Comments
 (0)