1

I want to define function that can be used globally

I made this decorator.

def toprint(func):
    def wrapper(*args, **kwargs):
        global print
        temp = print

        def print(*args, end="\n", sep=" "):
            args = [str(i) for i in args]
            target = sep.join(args)
            with open("output", "a") as file:
                file.write(target + end)

        func(*args, **kwargs)
        print = temp

    return wrapper

If I have this function defined in one file and use it in same file it would work perfectly, but if i use this on function from imported module if would print in console not write it in file like it should

Example #1


... toprint function ...

@toprint
def test():
    print("test")

It will output in file, but

Example #2

# test.py

def test():
    print("test")
# main.py

... toprint function ...

from test import test

toprint(test)()

It will print in console not in file

Hovercraft
  • 17
  • 5
  • I'm just adding a wild guess, because I haven't seen something like that yet. But in your wrapper-method, wouldn't you have to reassign the global print method first for this to work? So something like ```def _print(...)...;print = _print;func(*args, **kwargs);...``` (Sorry for bad formatting, but in the comments that's not really possible or I don't know how, so here, wherever I put a semicolon it's meant as a newline instead) – Christian Aug 21 '23 at 11:04
  • There isn't problem in which you stated, this work just fine. But the problem is with imports, [here](https://i.imgur.com/bVeX28y.png) is the image to code, result is in output file is "test", but if I import function which has print() in it, it would print in console and "output" file would even create – Hovercraft Aug 21 '23 at 11:16
  • Is your question specifically for `print`? Or is that a specific example and your question is more generic? Note that `main.print` is different from the builtin print (The scope specifically is to the file / module `main`) – Abdul Aziz Barkat Aug 21 '23 at 11:18
  • Yes it is mainly on builtin print function. The idea is that every logging from function will not print to console but in file. Of course I would need to do that with `sys.stdout.write()` too, but that rough idea – Hovercraft Aug 21 '23 at 11:21
  • The problem is that when `test` is imported it makes use of its module's namespace's `print` function. To do what you want you'd have to replace this; it's doable by importing `test`, then replacing `test.print` before calling the function. – Swifty Aug 21 '23 at 11:40
  • 1
    Does this answer your question? [Is there a way to override the print method in Python 3.x?](https://stackoverflow.com/questions/62059206/is-there-a-way-to-override-the-print-method-in-python-3-x) – Abdul Aziz Barkat Aug 21 '23 at 12:21
  • 1
    Also note that the need to do this feels weird to me, maybe what you should really look at is the [logging module](https://docs.python.org/3/library/logging.html) – Abdul Aziz Barkat Aug 21 '23 at 12:29
  • 1
    Another way is to simply [redirect stdout to a file temporarily](https://stackoverflow.com/questions/4675728/redirect-stdout-to-a-file-in-python) since `print` writes to stdout by default. – Abdul Aziz Barkat Aug 21 '23 at 12:31

1 Answers1

0

I found the answer thanks to @Swifty


import builtins

def toprint(func):
    def wrapper(*args, **kwargs):
        def _print(*args, end="\n", sep=" "):
            args = [str(i) for i in args]
            target = sep.join(args)
            with open("output", "a") as file:
                file.write(target + end)

        func.__globals__["print"] = _print

        func(*args, **kwargs)
        
        func.__globals__["print"] = builtins.print

    return wrapper

What it does is replace function's global's print function with _print function and after execution it'll revert back, because if revert is not done, whenever function will be called without decorator, function will use _print not builtin print

Hovercraft
  • 17
  • 5
  • Beware: if you use this decorator on a function from the main module, the `print` builtin will be definitively replaced! So you might want to fix your decorator. Simply re-add `temp = print` at the start, and replace the end with `func.__globals__["print"] = temp` – Swifty Aug 21 '23 at 12:12
  • I have renamed `def print(...)` to `def _print(...)` so that wouldn't happen, that's why I deleted temp variable which was in question – Hovercraft Aug 21 '23 at 12:18
  • Your builtin `print` will be replaced because `func.__globals__["print"] = _print` and `print` is the same as the global print... You should access `print` via the `builtins` module if you do want to patch it this way though rather than through `func.__globals__`. – Abdul Aziz Barkat Aug 21 '23 at 12:28