views:

120

answers:

1

This is a combination of my two recent questions:
[1] http://stackoverflow.com/questions/1954494/python-instance-method-in-c
[2] http://stackoverflow.com/questions/1956142/how-to-redirect-stderr-in-python

I would like to log the output of both stdout and stderr from a python script.

The thing I want to ask is, to create a new type according to [1] seems fairly complicated. Does it simplifies the things if there was no need to expose the new type to Python, i.e. it would only exist in C?

I mean, when Python prints something it goes to "Objects/fileobject.c" and there in "PyFile_WriteObject" it check whether it is possible to write to its argument:

writer = PyObject_GetAttrString(f, "write");
if (writer == NULL)
...

Also, it is possible to get stdout and stderr like this:

PyObject* out = PySys_GetObject("stdout");
PyObject* err = PySys_GetObject("stderr");

My question is then, is it somehow possible to construct necessary PyObject which satisfies the above 'PyObject_GetAttrString(f, "write")' and is callable so I can write:

PySys_SetObject("stdout", <my writer object / class / type / ?>);

http://docs.python.org/c-api/sys.html?highlight=pysys%5Fsetobject#PySys%5FSetObject

This way, there would be no need to expose the new "writer type" to the rest of Python script so I thought it might be a bit simpler to write the code...?

+1  A: 

Just make a module object (you're doing that anyway, if you're using the C API!-) and make it have a suitable write function -- that module object will be suitable as the second argument to PySys_SetObject.

In my answer to your other question I pointed you to xxmodule.c, an example file in Python's C sources, which is a module with a lot of examples including types and functions of various kinds -- you can work from there even if (mysteriously to me) you consider the "make a new type" part too difficult;-).

Edit: here's a trivial working example (aview.py):

#include "Python.h"
#include <stdio.h>

static PyObject *
aview_write(PyObject *self, PyObject *args)
{
    const char *what;
    if (!PyArg_ParseTuple(args, "s", &what))
        return NULL;
    printf("==%s==", what);
    return Py_BuildValue("");
}

static PyMethodDef a_methods[] = {
    {"write", aview_write, METH_VARARGS, "Write something."},
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
initaview(void)
{
    PyObject *m = Py_InitModule("aview", a_methods);
    if (m == NULL) return;
    PySys_SetObject("stdout", m);
}

Once this aview module is properly installed:

$ python
Python 2.5.4 (r254:67917, Dec 23 2008, 14:57:27) 
[GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import aview
>>> print 'ciao'
==ciao====
==>>>

...any string emitted to standard output is written with == signs around it (and this print calls .write twice: with 'ciao', and then again with a newline).

Alex Martelli
You mean: `static PyMethodDef LogMethods[] = {{"write", console_write, METH_VARARGS, "Writes into console."},...}`? And then: `Py_InitModule("log", LogMethods);` Because I tried that and it didn't work... But maybe I misunderstood you.Ad your `xxmodule.c` - I read it, I really tried, this is totally new to me.....
EcirH
@EcirH, OK, edited my answer to show a complete if trivial working example -- it works perfectly well (I've tried with Python 2.5 since you didn't mention what you're using, but it should work just about the same in any reasonably modern Python). So what's so complicated in these 20 lines or so of code, that's giving you such anguish?!
Alex Martelli
Yes, it works perfectly! Thanks a lot! I was missing that `PySys_SetObject("stdout", m);` line... And no, these 20 LOC are exactly what I wanted, this is much easier to understand than `xxmodule.c`. Thank you very much, once more!
EcirH