8

I am using python 2.6.6 and I need to overload the default python print function. I need to do it because this code may be used on a system where a built-in function has to be used to generate output, otherwise no output is displayed.

So, just for example, if you have a python script like this:

from __future__ import print_function

def NewPrint(Str):
    with open("somefile.txt","a") as AFile:
        AFile.write(Str)

def OverloadPrint():
    global print
    print = NewPrint

OverloadPrint()
print("ha")

It works fine. The input to the "overloaded" print is in the file specified by NewPrint.

Now with that in mind I would like to be able to run the couple of lines above and have print to do what NewPrint does during the entire execution of the script. Right now if I call a function from another module that uses print it will use the built-in print and not the one I just overwrote. I guess this has something to do with namespaces and built-ins, but my python is not good enough.

Edit:

I tried to keep simple, but looks like this caused more confusion...

  1. The function that overloads print will print to a GUI, so no need to care about redirection IF the function is overloaded.
  2. I know my example does not do what the print function actually does. A better example of how I am thinking of coding it is (still not great I know):

    def hli_print(*args, **kw):
        """
        print([object, ...], sep=' ', end='\n', file=sys.stdout)
        """
        sep = kw.get('sep', ' ')
        end = kw.get('end', '\n')
        File = kw.get('file', sys.stdout)
        args = [str(arg) for arg in args]
        string = sep.join(args) + end
        File.write(string)
        hli_Print(string)
    
  3. From 2 above you can see the function I have to use to print to the GUI "hli_Print" it is a C++ function exposed through a swig wrapper.

  4. We only use the standard library and our own swig wrappers, also our developers use print as a function (getting used to 3.X). So I did not really worry much about some other module calling print and having something else instead.

From all the comments I guess that just using some print_() function instead of print() (which is what we currently do) may be the best, but I got really curious to see if in python it would be possible to do what I described.

Mac
  • 3,397
  • 3
  • 33
  • 58
  • Possible duplicate of http://stackoverflow.com/questions/550470/overload-print-python – StoryTeller - Unslander Monica Dec 14 '12 at 00:33
  • 1
    Why do you want to do this? If you're running Python 2.6, everyone else's code that you interact with will be written to use the `print` statement rather than the function, so you'll only be affecting your own code. So why not define a `myprint` function, which defaults to `print` but you can replace with `NewPrint`, and just call that everywhere? (And if you _do_ want to affect code that uses the `print` statement, overloading the function won't help—but replacing `sys.stdout` may be what you want?) – abarnert Dec 14 '12 at 00:33
  • The concept of "global" in Python is not what you'd intuitively think. (I.e.: A variable global in the whole program.) It means "accessible to this function from some other scope than its local scope". – millimoose Dec 14 '12 at 00:33
  • Do you care what `sys.stdout.write('sample text')` does? – Matt Dec 14 '12 at 00:33
  • Also, for the record, this is a terrible idea. Instead I'd follow the [12factor approach to logging](http://www.12factor.net/logs) where the routing and storage of log statements is external to the program itself. Basically, doing what you want is much less likely to break third-party code if you make a launcher script with `python myprogram.py >> somefile.txt`. (Assuming said third-party code doesn't expect stdout to be a `tty` for whichever reason.) – millimoose Dec 14 '12 at 00:36
  • 2
    You really shouldn't do this. If you want to change where output goes, set up logging or redirect your program output from the calling context (ex the shell). You never know how libraries you call work under the hood, and they may (rightly so) expect basic system interactions like `print` to work the way they are supposed to, and break in weird, unfixable ways if they do not. As a rule, your code shouldn't have side effects for code that is not yours. – Silas Ray Dec 14 '12 at 00:36
  • I've updated my answer, now that I better understand your actual goals. – abarnert Dec 14 '12 at 02:19

2 Answers2

15

As @abarnert's answer and several comments have pointed out, replacing print is probably not a good idea. But just for the sake of completeness, here's why your code was not successfully overriding it for other modules.

The print function is defined in the module __builtin__ (which is renamed to builtins in Python 3). The Python interpreter makes everything in the __builtin__ module's namespace available to all other code it is running without importing it into your module's own namespace. It's magical!

However, when you create your own function named print (using print = NewPrint) it doesn't overwrite the original version in __builtin__. You're just creating a new variable in your module's namespace that shadows the old one from __builtin__. The global statement doesn't help, as it only lets you tell Python to that you want to write into your module's global namespace rather than some inner namespace inside a function.

To replace the default print function, you will need to explicitly replace it in the __builtin__ module. Here's some example code that does that:

from __future__ import print_function
try:
    import __builtin__ as builtins # Python 2
except ImportError:
    import builtins # Python 3

_print = print # keep a local copy of the original print
builtins.print = lambda *args, **kwargs: _print("foo:", *args, **kwargs)

To repeat, this is really not a good idea. While making sure I understood what I was talking about in this answer, I managed to crash one of my Python sessions by replacing print with a lambda function that didn't accept the file parameter that Python uses to print to standard error. A few lines later the exception handler was not pleased when it got a second exception while trying to print another exception's traceback.

There's almost certainly a better way to get the results you want.

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • You answered my question, but I guess I have to check the other answer for all the time he put into it. – Mac Dec 14 '12 at 13:37
  • It turns out it was very handy to pinpoint the location of a leftover debug print in a large codebase. I just set `builtins.print = None` and I got a backtrace showing the location of the offending print. Hacky, but effective. Thanks! – fgiraldeau Aug 26 '20 at 15:00
7

I don't think your question makes any sense.

First, if you're running Python 2.6, everything you import, etc., will be using print statements, even if your own module is using the print function. So, overloading the function will not affect anything else.

Second, you say "I need to do it because this code may be used on a system where a built-in function has to be used to generate output, otherwise no output is displayed." Well, your NewPrint is not a built-in function, so this won't help anyway.

It's also worth noting that your NewPrint doesn't implement most of the functionality of the print function, and even the bit that it does implement, it does wrong (print(s) will print s followed by a newline). So, if you did replace the builtin print function with yours, you'd just end up breaking most of your own code and any stdlib/third-party code you depend on.

It may be that you can accomplish what you want by creating a file-like object that replaces sys.stdout. Otherwise, I can't see how anything could work. For example:

class FakeStdOut(object):
    # … lots of other stuff to implement or inherit
    def write(s):
        with open("somefile.txt", "a") as f:
            f.write(s)

def OverloadPrint():
    sys.stdout = FakeStdOut()

But even if this works, it probably isn't what you really want. For a quick&dirty script, on a platform with a defective shell, this is sometimes a handy idea. But otherwise, it will probably cause you more trouble in the long run than coming up with a better solution. Here's just a few things that can go wrong (just as examples, not an exhaustive list)

  • If you ever want to change the file the output goes to, you have to modify the script. If you instead used >> in the shell, you could just call the script differently.
  • Someone reading or debugging your code (like, say, you, three months after you forgot how it worked) will be surprised by what's going on.
  • Some stdlib/third-party/coworker/etc. code you call will check that stdout is a tty before you make the change, and configure itself for interactive output.
  • Some code will print before you got a chance to redirect, and you'll spend hours trying to figure out how to reorder things to work around the problem.
  • You have to know how to implement a 'file-like object' completely—and that concept is not fully defined in 2.6—or it will break with some code.
  • Somewhere, there's some code that you thought was printing, but it was actually, say, logging or writing to sys.stderr or doing something else, so you've given yourself a false sense of security that you're now logging everything in somefile.txt, and won't discover otherwise until 6 months later, when you desperately need that missing information to debug a problem at a customer site.

Since you've edited the question, here's some further responses:

From all the comments I guess that just using some print_() function instead of print()

Yes, that's a more reasonable option. But I probably wouldn't call it print_. And it's simpler to put the "do or do not" logic inside the function, instead of swapping implementations in and out of the global name (especially since you're going to screw that up at some point if your code isn't all in one big module).

I worked on a project with a similar use case: We had messages we wanted to go to the syslogs, and also go to a GUI "log window" if it was open. So we wrote a glog function that wrapped that up, and nobody complained that they wanted to write print instead. (In fact, at least one guy on the team was very happy that he could use print for quick-and-dirty printouts while debugging without affecting the real output, especially when he had to debug the GUI logging code.)

But that was just because we didn't have any experience with the new (back then) logging module. Nowadays, I think I'd create a logging Handler implementation that writes to the GUI window, and just add that handler, and use the standard logging methods everywhere. And it sounds like that might be the best option for you.

Also, one last probably-irrelevant side issue:

We only use the standard library and our own swig wrappers, also our developers use print as a function (getting used to 3.X).

So why not use 3.x in the first place? Obviously 3.x has the actual 3.x standard library, instead of something kind of close to the 3.x standard library if you do some __future__ statements, and SWIG works with 3.x…

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • This is the way that this question is usually answered. It is also something that should never be done outside of a hacky test environment. – Silas Ray Dec 14 '12 at 00:40
  • @sr2222: As the answer says, "But even if this works, it probably isn't what you really want." Do you think I need to explain further all the problems he's going to run into? – abarnert Dec 14 '12 at 00:42
  • Sorry, didn't mean to come off as critical towards you, just wanted to reiterate that it really shouldn't be done. – Silas Ray Dec 14 '12 at 00:45
  • @sr2222: No, that was a sincere question. And now that I think about, it, I think the answer _would_ be better with some reasons. – abarnert Dec 14 '12 at 00:47
  • I think another good point is that even if you remember that you did this, if something `print`s at some point before your code is run, it won't be redirected, and you could very likely run in to problems trying to reorganize the code so that your override gets done before anything else. – Silas Ray Dec 14 '12 at 00:56
  • @sr2222: Good point. At least that one will be somewhat obvious to find (unlike, say, someone calling `isatty` before you redirect), but it _can_ be hard to work around, as you say. – abarnert Dec 14 '12 at 01:12
  • python 3.x is not yet an option because we would have to migrate some other code. I will have a look at logging, thanks! – Mac Dec 14 '12 at 13:35