1

I'm doing a functools.partial(print, flush=True) in the C-API before calling PyRun_File (please see Python C-API and PyRun_File: Using "import functools and partial(print, flush=True)").

The print works in the __main__ module, but isn't available in imported modules.

I'll try to create a simple example to describe the issue:

C-code

Please see full code example in https://stackoverflow.com/questions/67875182/
...
...
PyObject* newPrint = PyObject(partial, args, kwargs);

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

PyDict_SetItemString(pdict, "print", newPrint);

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

And the Python files:

prg_1_func.py

import time

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

prg_1.py

import prg_1_func

if __name__ == "__main__":
    print("Start...")
    prg_1_func.execute()
    print("...End")

The new print function is available in __main__ due to the call to PyDict_SetItemString(pdict, "print", newPrint).

But how can I get it to be "visible" in prg_1_func.py as well?

I can not/do not want to edit any of the Python files!

I came up with one idea but have no clue if it is even achievable, i.e. import everything and postpone the actual execution of the code?

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

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

Read all modules that will be imported, without executing the code!

for all modules as MOD do:
    PyDict_SetItemString(pdict, MODname + ".print", newPrint);

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

If anyone else has encountered the same or a similar problem and has managed to solve it, how did you do it? :o)

palle
  • 11
  • 1
  • If you want to overwrite ``print`` for the entire program, you have to do so in ``builtins``. That's not at all related to the C-API, though. Are you able to do this in pure Python without jumping through the hoops of the C-API? The error in your previous question would already have been the same in pure Python and the C-API, the C-API was just obfuscating things. – MisterMiyagi Jun 08 '21 at 11:34
  • Does this answer your question? [Python 3 - substituting functions for print()](https://stackoverflow.com/questions/65816585/python-3-substituting-functions-for-print) – MisterMiyagi Jun 08 '21 at 11:43
  • Thank you, @MisterMiyagi! I can not change any Python file at all (as I have no control over a vast majority of them) and the Python code is run from a C program with `PyRun_File`, which is why I tried to do the `functools.partial(print, flush=True)` before calling `PyRun_File`. With the help from DavidW it did work, until I encountered the problem I've stated in this question. The info and the link you provided might point me in the right direction, so I will have a look. – palle Jun 08 '21 at 12:13

1 Answers1

0

Thank you, MisterMiyagi, for pointing me in the right direction!

If you have a similar problem, please follow the link provided in the comments section in the first post, and hopefully it will help you as well.

The solution to have print = functools.partial(print, flush=True) act globally across modules seems to be (error checking omitted):

Py_Initialize();

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_SetAttrString(builtins, "print", newPrint); <---

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

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

Py_Finalize();
palle
  • 11
  • 1