[despammed] Re: redirect sys.stdout to C++ ?

Bengt Richter bokr at oz.net
Sun Dec 29 06:09:25 EST 2002


On Sat, 28 Dec 2002 19:43:21 +0100, =?ISO-8859-1?Q?Andr=E9_Jonsson?= <tatsujin at despammed.com> wrote:

>Bengt Richter wrote:
>
>>>I should probably make clear that I don't use an actual "interactive shell", I just 
>>>feed an entered string into the PyRun_SimpleString() API call, thus the normal 
>>         ^^^^^^^^^^^^^^ entered where & how? If it is in the same interpreter,
>> perhaps a similar thing could happen.
>> 
>> Need more context to understand your problem. Can you reduce it to a minimal example
>> showing how "entered strings" are entered and wind up being passed to PyRunSimpleString()?
>
>Ok, I'm developing this C++ application that embeds Python. In this application there 
                         ^^^^^^^^^^^^^^^-- do you refer to this C++ every time you say
"application" below? 

>is a prompt, completely self-made, that is meant to accept one-liners of Python 
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                   ^^^^^^^^^^^^^^^^^
??  printf("Enter one-liner: ");                     fgets(line, 128, stdin);  // or such ??

>"code" as input. The strings input there are passed directly to PyRun_SimpleString().
>
>To this embedded Python interpreter I've also added a module of my own that makes 
                                                     ^^^^^^^^^^^^^^^^^^ written in C++ or Python?
I'm guessing python.

>calls to some static functions in the application. One of these are a logging 
                                      ^-C++?
                  
>facility (or merely a 'print'-command inside my application).
                        ^-sounds like python.    ^-C++??
>
>Now, the thing is that I want all stdout/err output from any script that is run by 
>the python interpreter, including -but not limited to- the prompt, to be output 
If you are using printf/fgets, those are determining the prompt/input line, so I assume
you will have to log those from C++ before calling PyRun_SimpleString.
>through the logging-function in the imported module (python code can also be executed 
>from anywhere inside the application code (using PyRun_SimpleString()). To accomplish
                          ^--this must be C++ ;-)
>this I've made a redirection of sys.stdout and sys.stderr to call my own callback on
                                                                          ^-C++?
>any output to make the output visible inside my application.
                                                 ^--C++?h
>
>Without this all output from the code the interpreter runs will be output to the 
>application's stdout, which is of course not what I want.
>
>Maybe that clears it up... Thanks for trying to help!
>
Yes, though I forgot to ask what platform & what version of Python. Going by your
header, it would be some Linux variant and Python version.

The following is on NT3 with Python 2.2.2 and MSVC++6.0, guessing what you've done:

====< andrejonsson.c >==============================================================
/* andrejonsson.c */
#include <stdio.h>
#include <Python.h>
#include <string.h>

FILE * logfile = NULL;
int
logwrite(char* tag, char* line){
    int ntag, nline;
    if (logfile==NULL) return 0;
    ntag = strlen(tag); nline = strlen(line);
    if (ntag  != fwrite(tag, 1, ntag, logfile) ||
         2  != fwrite(": ", 1, 2, logfile) ||
         nline != fwrite(line, 1, nline, logfile)
    ){
        return 1;   /*error*/
    }
    fflush(logfile);
    return 0;
}

static PyObject *
andre_log(PyObject *self, PyObject *args)
{
    char *s_tag, *s_line;
    int n_tag, n_line, err;
    if (!PyArg_ParseTuple(args, "ss:andrelog", &s_tag, &s_line))
        return NULL;
    Py_BEGIN_ALLOW_THREADS
    err = logwrite(s_tag, s_line);
    Py_END_ALLOW_THREADS
    if (err) {
        PyErr_SetFromErrno(PyExc_IOError);
        clearerr(logfile);
        return NULL;
    }
    Py_INCREF(Py_None);
    return Py_None;
}

static PyMethodDef andreMethods[] = {
          {"andre_log", (PyCFunction)andre_log, METH_VARARGS,
           "andre_log(tag, line) writes same to log file if specified as -log lfile"},
          {NULL, NULL, 0, NULL}
      };

int
main(int argc, char *argv[])
{
    char *prompt = "Enter Python line: ";
    char line[128];
    
    if (argc>2 && !strcmp(argv[1],"-log")){
        if (!strcmp(argv[2], "stdout")){
            logfile = stdout;
        } else if (!strcmp(argv[2], "stderr")){
            logfile = stderr;
        } else {
            logfile =  fopen(argv[2],"a");
        }
    }
    Py_Initialize();
    Py_InitModule("andreApp", andreMethods);

    for (line[0]='\0'; strcmp(line,"q\n");){
        printf(prompt);
        if (fgets(line, 128, stdin) == NULL){
            printf( "fgets error\n" );
            break;
        } else {
            logwrite("prompt",prompt); logwrite("entered",line);
            if(!strcmp(line,"q\n")) break;
            PyRun_SimpleString(line);
        }
    }
    Py_Finalize();
    if (logfile) fclose(logfile);
    return 0;
}
====================================================================================
compiled thus:
--
[ 2:40] C:\pywk\embed>cl -ID:\Python22\include andrejonsson.c -link /LIBPATH:D:\Python22\libs
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

andrejonsson.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

/out:andrejonsson.exe
/LIBPATH:D:\Python22\libs
andrejonsson.obj
--
The above program prompts "on its own" and reads a line, and feeds it to
PyRun_SimpleString as I think you meant.

It also accepts a log file as a command line argument after -log, and it
presents an importable module interface called andreApp, which has a andre_log method
that takes two string args (a prefix and data).

You said there was a separate module (I thought python) which did overriding of stdout
and stderr. I modified your code a tiny bit and called it redirect.py:

====< redirect.py >==================================================================
# redirect.py
import andreApp     # a module interface created by C application
class Logger:
     def __init__(self, source):
         self.source=source
         self.buf = []
     def write(self, data):
         self.buf.append(data)
         if data.endswith('\n'):
            andreApp.andre_log(self.source, ''.join(self.buf))
            self.buf = []

import sys
sys.stdout = Logger('stdout')
sys.stderr = Logger('stderr')
=====================================================================================

This just does the redirection on import. Not nicely reversible or anything, but this
is just test stuff.

Ok, now we will run andrejonsson.exe:

--
[ 2:57] C:\pywk\embed>dir log.txt
 Volume in drive C is System
 Volume Serial Number is 14CF-C4B9

 Directory of C:\pywk\embed

File Not Found

[ 2:58] C:\pywk\embed>andrejonsson -log log.txt
Enter Python line: import andreApp
Enter Python line: import redirect
Enter Python line: print dir()
Enter Python line: 1/0
Enter Python line: import sys
Enter Python line: print sys.modules.keys()
Enter Python line: for k,v in sys.modules.items(): print '%15s %s' %(k,v)
Enter Python line: '----------------------------------'
Enter Python line: andreApp.andre_log('prefix', 'message fragment w/o newline, ')
Enter Python line: andreApp.andre_log('prefix', 'and same prefix with newline.\n')
Enter Python line: print 'should be stdout'
Enter Python line: print >> sys.stderr, 'should be stderr'
Enter Python line: print >> sys.stdout, 'should be stdout again'
Enter Python line: raise ValueError, 'A value error message'
Enter Python line: print '1'
Enter Python line: print '1'
Enter Python line: q
--

And the resulting log.txt was:
--
prompt: Enter Python line: entered: import andreApp
prompt: Enter Python line: entered: import redirect
prompt: Enter Python line: entered: print dir()
stdout: ['__builtins__', '__doc__', '__name__', 'andreApp', 'redirect']
prompt: Enter Python line: entered: 1/0
stderr: Traceback (most recent call last):
stderr:   File "<string>", line 1, in ?
stderr: ZeroDivisionError: integer division or modulo by zero
prompt: Enter Python line: entered: import sys
prompt: Enter Python line: entered: print sys.modules.keys()
stdout: ['redirect', 'stat', '__future__', 'copy_reg', 'os', 'signal', 'site', '__builtin__', 'UserDict', 'sys', 'ntpath', 'andreApp', '__main__', 'exceptions', 'types', 'nt', 'os.path']
prompt: Enter Python line: entered: for k,v in sys.modules.items(): print '%15s %s' %(k,v)
stdout:        redirect <module 'redirect' from 'C:\pywk\embed\redirect.pyc'>
stdout:            stat <module 'stat' from 'D:\Python22\Lib\stat.pyc'>
stdout:      __future__ <module '__future__' from 'D:\Python22\Lib\__future__.pyc'>
stdout:        copy_reg <module 'copy_reg' from 'D:\Python22\Lib\copy_reg.pyc'>
stdout:              os <module 'os' from 'D:\Python22\Lib\os.pyc'>
stdout:          signal <module 'signal' (built-in)>
stdout:            site <module 'site' from 'D:\Python22\Lib\site.pyc'>
stdout:     __builtin__ <module '__builtin__' (built-in)>
stdout:        UserDict <module 'UserDict' from 'D:\Python22\Lib\UserDict.pyc'>
stdout:             sys <module 'sys' (built-in)>
stdout:          ntpath <module 'ntpath' from 'D:\Python22\Lib\ntpath.pyc'>
stdout:        andreApp <module 'andreApp' (built-in)>
stdout:        __main__ <module '__main__' (built-in)>
stdout:      exceptions <module 'exceptions' (built-in)>
stdout:           types <module 'types' from 'D:\Python22\Lib\types.pyc'>
stdout:              nt <module 'nt' (built-in)>
stdout:         os.path <module 'ntpath' from 'D:\Python22\Lib\ntpath.pyc'>
prompt: Enter Python line: entered: '----------------------------------'
prompt: Enter Python line: entered: andreApp.andre_log('prefix', 'message fragment w/o newline, ')
prefix: message fragment w/o newline, prompt: Enter Python line: entered: andreApp.andre_log('prefix', 'and same prefix with newline.\n')
prefix: and same prefix with newline.
prompt: Enter Python line: entered: print 'should be stdout'
stdout: should be stdout
prompt: Enter Python line: entered: print >> sys.stderr, 'should be stderr'
stderr: should be stderr
prompt: Enter Python line: entered: print >> sys.stdout, 'should be stdout again'
stdout: should be stdout again
prompt: Enter Python line: entered: raise ValueError, 'A value error message'
stderr: Traceback (most recent call last):
stderr:   File "<string>", line 1, in ?
stderr: ValueError: A value error message
prompt: Enter Python line: entered: print '1'
stdout: 1
prompt: Enter Python line: entered: print '1'
stdout: 1
prompt: Enter Python line: entered: q
--

It seems to be working. So what did I do differently from what you did?
Maybe the fflush in logwrite?

Regards,
Bengt Richter



More information about the Python-list mailing list