Skip to content

Commit d94d226

Browse files
authored
Fix execution in silent mode (#407)
* Fix execution in silent mode
1 parent 58d6174 commit d94d226

2 files changed

Lines changed: 78 additions & 17 deletions

File tree

src/xinterpreter.cpp

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,45 @@ namespace xcpp
236236
return "unknown";
237237
}
238238

239+
class SilentStreamRedirectRAII
240+
{
241+
public:
242+
243+
explicit SilentStreamRedirectRAII(bool silent)
244+
: m_silent(silent)
245+
, m_old_cout(silent ? std::cout.rdbuf() : nullptr)
246+
, m_old_cerr(silent ? std::cerr.rdbuf() : nullptr)
247+
{
248+
if (m_silent)
249+
{
250+
std::cout.rdbuf(&m_null);
251+
std::cerr.rdbuf(&m_null);
252+
}
253+
}
254+
255+
~SilentStreamRedirectRAII() noexcept
256+
{
257+
if (m_silent)
258+
{
259+
std::cout.rdbuf(m_old_cout);
260+
std::cerr.rdbuf(m_old_cerr);
261+
}
262+
}
263+
264+
SilentStreamRedirectRAII(const SilentStreamRedirectRAII&) = delete;
265+
SilentStreamRedirectRAII& operator=(const SilentStreamRedirectRAII&) = delete;
266+
SilentStreamRedirectRAII(SilentStreamRedirectRAII&&) = delete;
267+
SilentStreamRedirectRAII& operator=(SilentStreamRedirectRAII&&) = delete;
268+
269+
private:
270+
271+
bool m_silent;
272+
xnull m_null;
273+
274+
std::streambuf* m_old_cout;
275+
std::streambuf* m_old_cerr;
276+
};
277+
239278
interpreter::interpreter(int argc, const char* const* argv) :
240279
xmagics()
241280
, p_cout_strbuf(nullptr)
@@ -288,16 +327,7 @@ namespace xcpp
288327

289328
// If silent is set to true, temporarily dismiss all std::cerr and
290329
// std::cout outputs resulting from `process_code`.
291-
292-
auto cout_strbuf = std::cout.rdbuf();
293-
auto cerr_strbuf = std::cerr.rdbuf();
294-
295-
if (config.silent)
296-
{
297-
auto null = xnull();
298-
std::cout.rdbuf(&null);
299-
std::cerr.rdbuf(&null);
300-
}
330+
SilentStreamRedirectRAII silent_guard(config.silent);
301331

302332
std::string err;
303333

@@ -331,13 +361,6 @@ namespace xcpp
331361
std::cout << std::flush;
332362
std::cerr << std::flush;
333363

334-
// Reset non-silent output buffers
335-
if (config.silent)
336-
{
337-
std::cout.rdbuf(cout_strbuf);
338-
std::cerr.rdbuf(cerr_strbuf);
339-
}
340-
341364
// Depending of error level, publish execution result or execution
342365
// error, and compose execute_reply message.
343366
if (errorlevel)

test/test_interpreter.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,3 +1229,41 @@ TEST_SUITE("mime_bundle_repr")
12291229
REQUIRE(res == expected);
12301230
}
12311231
}
1232+
1233+
#if !defined(__EMSCRIPTEN__)
1234+
// TODO: Currently any test added to this file will fail for the wasm build saying memory access out of bounds.
1235+
TEST_CASE("Silent mode restores std::cout and std::cerr buffers")
1236+
{
1237+
std::vector<const char*> Args = {};
1238+
xcpp::interpreter interpreter((int)Args.size(), Args.data());
1239+
1240+
auto* cout_before = std::cout.rdbuf();
1241+
auto* cerr_before = std::cerr.rdbuf();
1242+
1243+
xeus::execute_request_config config;
1244+
config.silent = true;
1245+
config.store_history = false;
1246+
config.allow_stdin = false;
1247+
1248+
nl::json header = nl::json::object();
1249+
xeus::xrequest_context::guid_list id = {};
1250+
xeus::xrequest_context context(header, id);
1251+
1252+
std::promise<nl::json> promise;
1253+
auto callback = [&promise](nl::json result) { promise.set_value(result); };
1254+
1255+
std::string code = R"(
1256+
#include <iostream>
1257+
std::cout << "hidden stdout\n";
1258+
std::cerr << "hidden stderr\n";
1259+
)";
1260+
1261+
interpreter.execute_request(context, callback, code, config, nl::json::object());
1262+
1263+
auto reply = promise.get_future().get();
1264+
REQUIRE(reply["status"] == "ok");
1265+
1266+
REQUIRE(std::cout.rdbuf() == cout_before);
1267+
REQUIRE(std::cerr.rdbuf() == cerr_before);
1268+
}
1269+
#endif

0 commit comments

Comments
 (0)