Skip to content

Commit c8c6a72

Browse files
committed
Allow disabling printing stats to stdout
Correct failing Python API tests Re ECFLOW-1944
1 parent 6140d81 commit c8c6a72

File tree

4 files changed

+81
-20
lines changed

4 files changed

+81
-20
lines changed

Pyext/src/ecflow/python/ClientDoc.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -680,13 +680,18 @@ const char* ClientDoc::ping() {
680680
}
681681

682682
const char* ClientDoc::stats() {
683-
return "Returns the `ecflow_server`_ statistics as a string\n::\n\n"
684-
" string stats()\n"
683+
return "Returns the `ecflow_server`_ statistics as a string\n\n"
684+
".. warning::\n\n"
685+
" When called without arguments, this function will print the statistics to :code:`stdout`,"
686+
" before returning the information as a string.\n"
687+
" To avoid printing the output, set the boolean flag :code:`to_stdout` to :code:`False`.\n"
685688
"\nUsage:\n\n"
686689
".. code-block:: python\n\n"
687690
" try:\n"
688691
" ci = Client() # use default host(ECF_HOST) & port(ECF_PORT)\n"
689-
" stats = ci.stats()\n"
692+
" stats = ci.stats() # prints to stdout\n"
693+
" stats = ci.stats(True) # prints to stdout\n"
694+
" stats = ci.stats(False) # does not print to stdout\n"
690695
" print(stats)\n"
691696
" except RuntimeError, e:\n"
692697
" print(str(e))\n";

Pyext/src/ecflow/python/ExportClient.cpp

+8-5
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,16 @@ class CliSetter {
130130
private:
131131
ClientInvoker* _self;
132132
};
133-
const std::string& stats(ClientInvoker* self) {
133+
134+
const std::string& stats(ClientInvoker* self, bool to_stdout = true) {
134135
self->stats();
135-
// The statistics are printed to stdout for backward compatibility with previous python client versions
136-
// TODO: remove printing to stdout in the next release
137-
std::cout << self->server_reply().get_string() << std::endl;
136+
if (to_stdout) {
137+
std::cout << self->server_reply().get_string() << std::endl;
138+
}
138139
return self->server_reply().get_string();
139140
}
141+
BOOST_PYTHON_FUNCTION_OVERLOADS(stats_overloads, stats, 1, 2)
142+
140143
void stats_reset(ClientInvoker* self) {
141144
self->stats_reset();
142145
}
@@ -577,7 +580,7 @@ void export_Client() {
577580
.def("free_all_dep", &free_all_dep, ClientDoc::free_all_dep())
578581
.def("free_all_dep", &free_all_dep1)
579582
.def("ping", &ClientInvoker::pingServer, ClientDoc::ping())
580-
.def("stats", &stats, return_value_policy<copy_const_reference>(), ClientDoc::stats())
583+
.def("stats", &stats, stats_overloads(args("to_stdout"), ClientDoc::stats())[return_value_policy<copy_const_reference>()])
581584
.def("stats_reset", &stats_reset, ClientDoc::stats_reset())
582585
.def("get_file",
583586
&get_file,

Pyext/test/py_s_TestClientApi.py

+57-8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import time
1515
import os
1616
import pwd
17+
import select
1718
from datetime import datetime
1819
import shutil # used to remove directory tree
1920

@@ -529,20 +530,66 @@ def test_client_free_dep(ci):
529530
time.sleep(3)
530531

531532
dir_to_remove = Test.ecf_home(the_port) + "/" + "test_client_free_dep"
532-
shutil.rmtree(dir_to_remove, ignore_errors=True)
533+
shutil.rmtree(dir_to_remove, ignore_errors=True)
534+
535+
536+
class CustomStdOut:
537+
def __enter__(self):
538+
# create a pipe to store some content temporarily
539+
self.read_pipe, self.write_pipe = os.pipe()
540+
# save the original ouput fd
541+
self.stdout = os.dup(1)
542+
# redirect stdout into the write pipe
543+
fd = os.dup2(self.write_pipe, 1)
544+
return self
545+
546+
def __exit__(self, exc_type, exc_value, exc_traceback):
547+
# restore the original output fd
548+
os.dup2(self.stdout, 1)
549+
550+
def _more_data(self):
551+
# check if there is more data in the read pipe
552+
r, _, _ = select.select([self.read_pipe], [], [], 0)
553+
return bool(r)
554+
555+
def value(self):
556+
out = ''
557+
while self._more_data():
558+
out += os.read(self.read_pipe, 1024).decode('utf-8')
559+
return out
533560

534561

535562
def test_client_stats(ci):
536563
print_test(ci,"test_client_stats")
537-
stats = ci.stats()
538-
assert "statistics" in s, "Expected statistics in the response"
539-
564+
with CustomStdOut() as out:
565+
stats = ci.stats()
566+
assert "statistics" in stats, "Expected 'statistics' in the response"
567+
assert "statistics" in out.value(), "Expected 'statistics' in the captured output"
568+
569+
570+
def test_client_stats_with_stdout(ci):
571+
print_test(ci,"test_client_stats_with_stdout")
572+
with CustomStdOut() as out:
573+
stats = ci.stats(True)
574+
assert "statistics" in stats, "Expected 'statistics' in the response"
575+
assert "statistics" in out.value(), "Expected 'statistics' in the captured output"
576+
577+
578+
def test_client_stats_without_stdout(ci):
579+
print_test(ci,"test_client_stats_without_stdout")
580+
with CustomStdOut() as out:
581+
stats = ci.stats(False)
582+
assert "statistics" in stats, "Expected 'statistics' in the response"
583+
assert not out.value() , "No captured output expected"
584+
585+
540586
def test_client_stats_reset(ci):
541587
print_test(ci,"test_client_stats_reset")
542588
ci.stats_reset()
543-
statis = ci.stats()
544-
assert "statistics" in s, "Expected statistics in the response"
545-
589+
stats = ci.stats()
590+
assert "statistics" in stats, "Expected 'statistics' in the response"
591+
592+
546593
def test_client_debug_server_on_off(ci):
547594
print_test(ci,"test_client_debug_server_on_off")
548595
ci.debug_server_on() # writes to standard out
@@ -2058,7 +2105,9 @@ def do_tests(ci,the_port):
20582105
test_client_check(ci)
20592106
test_client_check_defstatus(ci)
20602107

2061-
test_client_stats(ci)
2108+
test_client_stats(ci)
2109+
test_client_stats_with_stdout(ci)
2110+
test_client_stats_without_stdout(ci)
20622111
test_client_stats_reset(ci)
20632112
test_client_debug_server_on_off(ci)
20642113

docs/python_api/Client.rst

+8-4
Original file line numberDiff line numberDiff line change
@@ -2259,21 +2259,25 @@ Usage:
22592259
sort_attributes( (Client)arg1, (list)paths, (str)attribute_name [, (bool)recursive=True]) -> None
22602260
22612261
2262-
.. py:method:: Client.stats( (Client)arg1) -> str :
2262+
.. py:method:: Client.stats( (Client)arg1 [, (bool)to_stdout]) -> str :
22632263
:module: ecflow
22642264
22652265
Returns the :term:`ecflow_server` statistics as a string
2266-
::
22672266
2268-
string stats()
2267+
.. warning::
2268+
2269+
When called without arguments, this function will print the statistics to :code:`stdout`, before returning the information as a string.
2270+
To avoid printing the output, set the boolean flag :code:`to_stdout` to :code:`False`.
22692271
22702272
Usage:
22712273
22722274
.. code-block:: python
22732275
22742276
try:
22752277
ci = Client() # use default host(ECF_HOST) & port(ECF_PORT)
2276-
stats = ci.stats()
2278+
stats = ci.stats() # prints to stdout
2279+
stats = ci.stats(True) # prints to stdout
2280+
stats = ci.stats(False) # does not print to stdout
22772281
print(stats)
22782282
except RuntimeError, e:
22792283
print(str(e))

0 commit comments

Comments
 (0)