0

First a short background:

(1) When running this program in Python3.6 I need to wait until it ends for the result to be displayed:

import time

for i in range(5):
        print(i, end=" ")
        time.sleep(1)

(2) This program on the other hand works as I would like it to, each value printed every second:

import time

import functools
print = functools.partial(print, flush=True)

for i in range(5):
        print(i, end=" ")
        time.sleep(1)

And now to my question:

How can I add

import functools
print = functools.partial(print, flush=True)

in C code so that I can run program 1 using PyRun_File, but have the results as if I was running program 2?

I have had trouble finding documentation and examples that is straight forward and easy to understand, so I'm hoping for some help.

This is what I have tried (error checking omitted and old code already in the program I'm trying to improve is labeled "//existing"):

Py_Initialize(); //existing

PyObject* functools = PyImport_ImportModule("functools");
PyObject* partial = PyObject_GetAttrString(functools, "partial");

PyObject* builtins = PyImport_ImportModule("builtins");
PyObject* print = PyObject_GetAttrString(builtins, "print");

PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, print);

PyObject* kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "flush", Py_True);

PyObject_Call(partial, args, kwargs);

PyObject* main_module = PyImport_ImportModule("__main__"); //existing
PyObject* pdict = PyModule_GetDict(main_module); //existing

FILE* fp = fopen("prg_1.py", "r"); //existing
PyObject* pval = PyRun_File(fp, "prg_1.py", Py_file_input, pdict, pdict); //existing

Py_Finalize(); //existing

Unfortunately this doesn't work as I hoped it would, so can anyone spot any obvious errors, or maybe not so obvious ones?

Thank you in advance for your help!

palle
  • 11
  • 1
  • For **every** function that returns a `PyObject*` you should check if the return value is `NULL`. If it is then your need something like `PyErr_Print` to investigate why. This'll help you figure our what's gone wrong. It's probably an issue with the module search path (but that'll be a lot clearer if you knew where it's failing) – DavidW Jun 07 '21 at 18:17
  • Thank you for your reply, @DavidW. As I wrote, I have deliberately omitted error handling in the posted code to make it clearer. In reality I have of course checked for NULL and the code "work" in that sense, but it doesn't work. – palle Jun 07 '21 at 18:30
  • `PyObject_Call` has a return value? – DavidW Jun 07 '21 at 18:43
  • Yes. tp_name is "functools.partial", and there are other tp_-values as well which all seem to be ok. And as a sidenote: PyRun_SimpleString("import functools"); PyRun_SimpleString("print = functools.partial(print, flush=True)"); and then calling PyRun_File seems to work. – palle Jun 07 '21 at 18:54
  • The point I was making is that just calling `functools.partial(print, flash=True)` doesn't replace `print` - instead it returns a function that you can call instead. – DavidW Jun 07 '21 at 19:51
  • That actually sounds very reasonable, @DavidW. Thank you! :o) So in the code from my original question, if I save the return value from the PyObject_Call, how does one make this returned print function to be used when executing Python code in the PyRun_File? – palle Jun 07 '21 at 20:23
  • I think if you add it to `pdict` (as `print`) it should be found instead of `print`. Although that probably affects all subsequent uses of the `__main__` module – DavidW Jun 07 '21 at 20:27
  • `PyObject* newPrint = PyObject_Call(partial, args, kwargs);` and then `PyDict_SetItemString(pdict, "print", newPrint);` seems to work, but will do some more testing tomorrow. Thank you very much for your help, @DavidW! – palle Jun 07 '21 at 20:55

1 Answers1

0

Thanks to the valuable input from DavidW it now works! Thank you!

(I did although encounter another problem which I will address in a new question.)

With additions the code now looks like this (error checking is still omitted):

Py_Initialize(); //existing

PyObject* functools = PyImport_ImportModule("functools");
PyObject* partial = PyObject_GetAttrString(functools, "partial");

PyObject* builtins = PyImport_ImportModule("builtins");
PyObject* print = PyObject_GetAttrString(builtins, "print");

PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, print);

PyObject* kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "flush", Py_True);

PyObject* newPrint = PyObject_Call(partial, args, kwargs);

PyObject* main_module = PyImport_ImportModule("__main__"); //existing
PyObject* pdict = PyModule_GetDict(main_module); //existing

PyDict_SetItemString(pdict, "print", newPrint);

FILE* fp = fopen("prg_1.py", "r"); //existing
PyObject* pval = PyRun_File(fp, "prg_1.py", Py_file_input, pdict, pdict); //existing

Py_Finalize(); //existing
palle
  • 11
  • 1