Skip to content
Snippets Groups Projects
Commit a7873393 authored by Boman Romain's avatar Boman Romain
Browse files

capture streams to file

parent 1c86db43
No related branches found
No related tags found
No related merge requests found
......@@ -12,6 +12,7 @@ SET_PROPERTY(TARGET femi PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON)
# MACRO_DebugPostfix(femi)
TARGET_INCLUDE_DIRECTORIES(femi PRIVATE
${PROJECT_SOURCE_DIR}/src
${Python3_INCLUDE_DIRS})
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/_src
${Python3_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(femi PRIVATE fem ${Python3_LIBRARIES})
......@@ -18,6 +18,8 @@ threads="1"
#include "femMedium.h"
#include "femExtractor.h"
#include "wCppBuf2Py.h"
#include <string>
// #include <sstream>
......@@ -72,6 +74,10 @@ namespace std {
}
}
%warnfilter(401); //Nothing known about base class 'std::basic_streambuf< char >'
%include "wCppBuf2Py.h"
%warnfilter(+401);
// ----------- FEM CLASSES ---------------
%include "fem.h"
%include "femDirichlet.h"
......
/*
* Copyright 2020 University of Liège
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <Python.h>
#include "wCppBuf2Py.h"
#include <stdio.h>
#include <algorithm>
CppBuf2Py::CppBuf2Py(std::ostream &o1, bool err) : stream(o1), errstream(err)
{
oldbuf = stream.rdbuf();
stream.rdbuf(this);
verb = false;
}
CppBuf2Py::~CppBuf2Py()
{
stream.rdbuf(oldbuf); // restore the ostream
}
std::streamsize
CppBuf2Py::xsputn(const char *s, std::streamsize n)
{
if (verb)
printf("CppBuf2Py::xsputn(%ld)\n", static_cast<long>(n));
// PySys_WriteStdout truncates to 1000 chars
static const std::streamsize MAXSIZE = 1000;
std::streamsize written = std::min(n, MAXSIZE);
if (verb)
printf("written=%ld\n", static_cast<long>(written));
std::string str(s, n);
if (verb)
printf("string=%s\n", str.c_str());
PyGILState_STATE gstate = PyGILState_Ensure();
if (errstream)
PySys_WriteStderr("%s", str.c_str());
else
PySys_WriteStdout("%s", str.c_str());
PyGILState_Release(gstate);
return written;
}
int CppBuf2Py::overflow(int c)
{
if (verb)
printf("CppBuf2Py::overflow(%d)\n", c);
if (c != EOF)
{
PyGILState_STATE gstate = PyGILState_Ensure();
if (errstream)
PySys_WriteStderr("%c", c);
else
PySys_WriteStdout("%c", c);
PyGILState_Release(gstate);
}
return c;
}
void CppBuf2Py::test()
{
CppBuf2Py buf(std::cout);
for (int i = 0; i < 1000; ++i)
std::cout << "(" << i << ")"
<< " this is a test - ";
}
// =============================================================================
StdOutErr2Py::StdOutErr2Py() : out(std::cout), err(std::cerr, true)
{
}
void StdOutErr2Py::test()
{
StdOutErr2Py redirector;
for (int i = 0; i < 50; ++i)
{
std::cout << "(" << i << ")"
<< " this is a test - ";
std::cerr << "(" << i << ")"
<< " error - ";
}
std::cout << '\n';
std::cerr << '\n';
}
#ifndef CPPBUF2PY_H
#define CPPBUF2PY_H
#include "fem.h"
#include <iostream>
// http://bo-peng.blogspot.be/2004/10/how-to-re-direct-cout-to-python_05.html
// https://docs.python.org/2/c-api/sys.html#system-functions
/**
* @brief redirect a std::stream to python
*/
#ifndef SWIG
class CppBuf2Py : public std::basic_streambuf<char>
{
public:
typedef std::char_traits<char> traits_type;
typedef traits_type::int_type int_type;
private:
std::ostream &stream;
std::streambuf *oldbuf;
bool verb;
bool errstream;
public:
CppBuf2Py(std::ostream &o1, bool err = false);
~CppBuf2Py();
static void test();
protected:
virtual std::streamsize xsputn(const char *s, std::streamsize n);
virtual int_type overflow(int c);
};
#endif //SWIG
/**
* @brief redirect std::cout/std::cerr to python sys.stdout/sys.stderr
*/
class StdOutErr2Py
{
CppBuf2Py out;
CppBuf2Py err;
public:
StdOutErr2Py();
static void test();
};
#endif //CPPBUF2PY_H
......@@ -13,6 +13,50 @@ import os, platform
# for p in corr_path:
# print(p)
# Duplicate console output to file
class DupStream:
def __init__(self, stream1, stream2):
self.stream1 = stream1
self.stream2 = stream2
def write(self, data):
self.stream1.write(data)
self.stream2.write(data)
def flush(self):
self.stream1.flush()
self.stream2.flush()
class Tee:
def __init__(self, name):
import sys
self.file = open(name, 'w')
self.stdoutbak = sys.stdout
self.stderrbak = sys.stderr
sys.stdout = DupStream(sys.stdout, self.file)
sys.stderr = DupStream(sys.stderr, self.file)
def __del__(self):
import sys
sys.stdout = self.stdoutbak
sys.stderr = self.stderrbak
self.file.close()
def parseargs():
"""
parses command line arguments
"""
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verb", help="increase output verbosity", action="count", default=0)
parser.add_argument("--nogui", help="disable any graphical output", action="store_true")
parser.add_argument("-k", help="nb of threads", type=int, default=1)
parser.add_argument('file', help='python files')
args = parser.parse_args()
return args
if __name__ == "__main__":
import sys
import os
......@@ -30,6 +74,11 @@ if __name__ == "__main__":
sys.path.append(os.path.join(thisdir, 'cxxfem',
'build', 'bin', 'Release')) # msvc
args = parseargs()
os.environ['OMP_NUM_THREADS'] = str(args.k)
# display env variables
try:
print(f"OMP_NUM_THREADS={os.environ['OMP_NUM_THREADS']}")
......@@ -37,29 +86,35 @@ if __name__ == "__main__":
print(f"OMP_NUM_THREADS=[not set]")
# parse args
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
"-v", "--verb", help="increase output verbosity", action="count", default=0)
parser.add_argument("--nogui", help="disable any graphical output",
action="store_true")
parser.add_argument('file', nargs='*', help='python files')
args = parser.parse_args()
# import argparse
# parser = argparse.ArgumentParser()
# parser.add_argument(
# "-v", "--verb", help="increase output verbosity", action="count", default=0)
# parser.add_argument("--nogui", help="disable any graphical output",
# action="store_true")
# parser.add_argument('file', nargs='*', help='python files')
# args = parser.parse_args()
# run test file
testname = os.path.abspath(args.file)
testname = os.path.normcase(testname) # F:/ => f:/ on Windows
# create workspace
common = os.path.commonprefix((testname, thisdir + os.sep))
resdir = testname[len(common):].replace(os.sep, "_")
resdir, ext = os.path.splitext(resdir)
wdir = os.path.join('workspace', resdir)
print('workspace=', wdir)
if not os.path.isdir(wdir):
os.makedirs(wdir)
os.chdir(wdir)
# redirect C++ streams to Python
import cxxfem
redirect = cxxfem.StdOutErr2Py()
tee = Tee('stdout.txt') # split streams
# run all tests sequentially
for testname in args.file:
testname = os.path.abspath(testname)
testname = os.path.normcase(testname) # F:/ => f:/ on Windows
# create workspace
common = os.path.commonprefix((testname, thisdir + os.sep))
resdir = testname[len(common):].replace(os.sep, "_")
resdir, ext = os.path.splitext(resdir)
wdir = os.path.join('workspace', resdir)
print('workspace=', wdir)
if not os.path.isdir(wdir):
os.makedirs(wdir)
os.chdir(wdir)
if ext == '.py':
# python script
__file__ = testname
exec(open(testname, encoding='utf-8').read())
if ext == '.py':
# run python script
__file__ = testname
exec(open(testname, encoding='utf-8').read())
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment