133

Is there a way to stop a function from calling print?


I am using the pygame.joystick module for a game I am working on.

I created a pygame.joystick.Joystick object and in the actual loop of the game call its member function get_button to check for user input. The function does everything I need it to do, but the problem is that it also calls print, which slows down the game considerably.

Can I block this call to print?

martineau
  • 119,623
  • 25
  • 170
  • 301
dmlicht
  • 2,328
  • 2
  • 15
  • 16
  • 3
    This ought to be considered a bug in the module in question (perhaps long since fixed, of course). Libraries have no business writing to the standard streams except on request. – Davis Herring Sep 07 '21 at 04:18

15 Answers15

167

Python lets you overwrite standard output (stdout) with any file object. This should work cross platform and write to the null device.

import sys, os

# Disable
def blockPrint():
    sys.stdout = open(os.devnull, 'w')

# Restore
def enablePrint():
    sys.stdout = sys.__stdout__


print 'This will print'

blockPrint()
print "This won't"

enablePrint()
print "This will too"

If you don't want that one function to print, call blockPrint() before it, and enablePrint() when you want it to continue. If you want to disable all printing, start blocking at the top of the file.

Brigand
  • 84,529
  • 20
  • 165
  • 173
  • 46
    This seems to have permanently blocked print for me. enablePrint does not restore it – Johnny V Feb 27 '18 at 17:46
  • 15
    This solution won't restore properly printing to the Jupyter cells – oski86 Jul 11 '19 at 12:47
  • 1
    I guess the argument to print is still evaluated, so it will take longer than the script with all print lines manually commented out? – Radio Controlled Sep 08 '19 at 08:38
  • 2
    for Jupyter you can monkey-patch-save the original stdout in `sys` as `sys._jupyter_stdout = sys.stdout` in `blockPrint` and restore to it in `enablePrint` – IljaBek May 04 '21 at 22:48
149

Use with

Based on @FakeRainBrigand solution I'm suggesting a safer solution:

import os, sys

class HiddenPrints:
    def __enter__(self):
        self._original_stdout = sys.stdout
        sys.stdout = open(os.devnull, 'w')

    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.stdout.close()
        sys.stdout = self._original_stdout

Then you can use it like this:

with HiddenPrints():
    print("This will not be printed")

print("This will be printed as before")

This is much safer because you can not forget to re-enable stdout, which is especially critical when handling exceptions.

Without with — Bad practice

The following example uses enable/disable prints functions that were suggested in previous answer.

Imagine that there is a code that may raise an exception. We had to use finally statement in order to enable prints in any case.

try:
    disable_prints()
    something_throwing()
    enable_prints() # This will not help in case of exception
except ValueError as err:
    handle_error(err)
finally:
    enable_prints() # That's where it needs to go.

If you forgot the finally clause, none of your print calls would print anything anymore.

It is safer to use the with statement, which makes sure that prints will be reenabled.

Note: It is not safe to use sys.stdout = None, because someone could call methods like sys.stdout.write()

Alexander C
  • 3,597
  • 1
  • 23
  • 39
  • 7
    Very good solution! I just wrote the same thing and then found you already answered that way :D I'll add a bit of information about why this is a better way to do it. – iFreilicht Oct 23 '17 at 16:31
  • Noobie question here: Would it be important to close() devnull after exiting the class? – jlsecrest Jan 13 '18 at 17:26
  • @Wadsworth, I don't know the answer. I guess devnull's properties do not require it to be closed properly. But rumors say that you should always close open handlers to release resources. CPython as I know should close devnull itself as far as garbage collector get it. – Alexander C Jan 13 '18 at 22:36
  • 3
    I was getting `ResourceWarning: unclosed file <_io.TextIOWrapper name='/dev/null' mode='w' encoding='UTF-8'>` when using this code, solved it by setting sys.stdout = None instead of open(os.devnull,'w') – WellDone2094 Feb 05 '18 at 13:23
  • 1
    @WellDone2094, thanks. I've added `sys.stdout.close()` to the exit method. This should help. Note that `sys.stdout = None` may cause an error, because someone may call stdout's methods like `sys.stdout.write()`. – Alexander C May 13 '20 at 20:58
  • My function processes successfully if I don't use with HiddenPrints(). When I use it throws an error "UnicodeEncodeError: 'charmap' codec can't encode character '\u017c' in position 204: character maps to " – Syed Md Ismail Mar 15 '21 at 11:20
  • @SyedMdIsmail I think you should check what line causes error exactly. You may have some logger attached or, who knows, maybe you have replaced original print function with another implementation. – Alexander C Mar 25 '21 at 13:02
  • This is extremely useful for debugging. Why didn't I see this before? – SurpriseDog Jun 22 '21 at 17:57
75

As @Alexander Chzhen suggested, using a context manager would be safer than calling a pair of state-changing functions.

However, you don't need to reimplement the context manager - it's already in the standard library. You can redirect stdout (the file object that print uses) with contextlib.redirect_stdout, and also stderr with contextlib.redirect_stderr.

import os
import contextlib

with open(os.devnull, "w") as f, contextlib.redirect_stdout(f):
    print("This won't be printed.")
Asclepius
  • 57,944
  • 17
  • 167
  • 143
ForgottenUmbrella
  • 1,052
  • 14
  • 14
  • Does not work for me! Function is still printing... – PascalIv Apr 12 '21 at 08:49
  • @PascalIv What exactly did you try? Still works for me in Python 3.9 on Linux. – ForgottenUmbrella Apr 12 '21 at 08:56
  • 3
    I realized it's because I am using Colab. But I got it to work with `from IPython.utils import io with io.capture_output() as captured: print("I will not be printed.")` – PascalIv Apr 12 '21 at 08:58
  • for me, this used to work in Python 3.7, but moving to 3.9 no longer works – Guillaume May 26 '22 at 08:59
  • @Guillaume works for me on Python 3.10.4 on Linux. It also worked for me on Python 3.9 last time someone raised an issue with it, as evident in a previous comment. You're going to need to provide more context. – ForgottenUmbrella May 27 '22 at 07:15
  • 1
    I have created a question for it, because maybe it is related to AWS Lambda then? on my Mac with Python 3.9, simply doing `logger.removeHandler(sys.stdout)` is enough, but somehow the Lambda/CloudWatch still prints everything! See https://stackoverflow.com/questions/72389347/disable-logging-on-aws-lambda-python-3-9/72393167 – Guillaume May 27 '22 at 07:29
  • This does not work in a multithreaded application. – Burak Mar 18 '23 at 15:23
  • @Burak The `contextlib` documentation advises that this method may not be suitable for multithreaded applications, but without further information, I cannot provide help. – ForgottenUmbrella Mar 21 '23 at 09:30
18

If you want to block print calls made by a particular function, there is a neater solution using decorators. Define the following decorator:

# decorater used to block function printing to the console
def blockPrinting(func):
    def func_wrapper(*args, **kwargs):
        # block all printing to the console
        sys.stdout = open(os.devnull, 'w')
        # call the method in question
        value = func(*args, **kwargs)
        # enable all printing to the console
        sys.stdout = sys.__stdout__
        # pass the return value of the method back
        return value

    return func_wrapper

Then just place @blockPrinting before any function. For example:

# This will print
def helloWorld():
    print("Hello World!")
helloWorld()

# This will not print
@blockPrinting
def helloWorld2():
    print("Hello World!")
helloWorld2()

For Jupyter Notebooks you may need to replace the func_wrapper code with:

from IPython.utils.io import capture_output

def func_wrapper(*args, **kwargs):
    with capture_output():
        value = func(*args, **kwargs)
    return value
Fowler
  • 514
  • 4
  • 11
  • Used this in a jupyter notebook - printing has been permanently disabled i.e. even helloWorld() wouldn't print anything if I run it for the second time – newbie101 Jun 16 '23 at 05:53
  • 1
    I've updated the answer with a code snippet which will work with Jupyter Notebooks – Fowler Jun 20 '23 at 09:09
8

If you are using Jupyter Notebook or Colab use this:

from IPython.utils import io

with io.capture_output() as captured:
    print("I will not be printed.")
PascalIv
  • 595
  • 7
  • 21
  • This does work well with functions(that print stuff) defined in the notebook itself. However, if I use an api call (to [Microsoft Form recognizer](https://learn.microsoft.com/en-us/python/api/azure-ai-formrecognizer/azure.ai.formrecognizer.documentanalysisclient?view=azure-python#azure-ai-formrecognizer-documentanalysisclient-begin-analyze-document)) , it starts printing the api request details anyways. Do you know why this would happen? Is there something that the api is overriding? – newbie101 Jun 16 '23 at 06:05
3

You can do a simple redirection, this seems a lot safer than messing with stdout, and doesn't pull in any additional libraries.

enable_print  = print
disable_print = lambda *x, **y: None

print = disable_print
function_that_has_print_in_it(1)  # nothing is printed

print = enable_print
function_that_has_print_in_it(2)  # printing works again!

Note: this only works to disable the print() function, and would not disable all output if you're making calls to something else that is producing output. For instance if you were calling a C library that was producing it's own output to stdout, or if you were using intput().

xelf
  • 79
  • 1
  • 6
  • The example I listed does work, but if you have an example where you have tried something similar and it did not work for you, you will have to be more explicit about what you tried and what changes you made. What version of Python were you using? – xelf May 25 '21 at 18:07
  • I like the idea, but it only worked for me if I dealt with `__builtins__.print` specifically. – Alex Peters May 21 '22 at 16:11
3

A completely different approach would be redirecting at the command line. If you're on Windows, this means a batch script. On Linux, bash.

/full/path/to/my/game/game.py > /dev/null
C:\Full\Path\To\My\Game.exe > nul

Unless you're dealing with multiple processes, this should work. For Windows users this could be the shortcuts you're creating (start menu / desktop).

Velociraptors
  • 2,012
  • 16
  • 22
Brigand
  • 84,529
  • 20
  • 165
  • 173
2

I have had the same problem, and I did not come to another solution but to redirect the output of the program (I don't know exactly whether the spamming happens on stdout or stderr) to /dev/null nirvana.

Indeed, it's open source, but I wasn't passionate enough to dive into the pygame sources - and the build process - to somehow stop the debug spam.

EDIT :

The pygame.joystick module has calls to printf in all functions that return the actual values to Python:

printf("SDL_JoystickGetButton value:%d:\n", value);

Unfortunately you would need to comment these out and recompile the whole thing. Maybe the provided setup.py would make this easier than I thought. You could try this...

moooeeeep
  • 31,622
  • 22
  • 98
  • 187
1

The module I used printed to stderr. So the solution in that case would be:

sys.stdout = open(os.devnull, 'w')
David Schumann
  • 13,380
  • 9
  • 75
  • 96
1
"stop a function from calling print"
# import builtins
# import __builtin__ # python2, not test
printenabled = False
def decorator(func):
    def new_func(*args,**kwargs):
        if printenabled:
            func("print:",*args,**kwargs)
    return new_func
print = decorator(print) # current file
# builtins.print = decorator(builtins.print)  # all files
# __builtin__.print = decorator(__builtin__.print) # python2

import sys
import xxxxx
def main():
    global printenabled
    printenabled = True
    print("1 True");
    printenabled = False
    print("2 False");
    printenabled = True
    print("3 True");
    printenabled = False
    print("4 False");
if __name__ == '__main__':
    sys.exit(main())

#output
print: 1 True
print: 3 True

https://stackoverflow.com/a/27622201

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 27 '21 at 22:58
1

Change value of file object of print() function. By default it's sys.stdout, instead we can write to null device by open(os.devnull, 'w')

import os, sys

mode = 'debug' #'prod'

if mode == 'debug':
    fileobj = sys.stdout
else:
    fileobj = open(os.devnull,'w')

print('Hello Stackoverflow', file = fileobj)
1

No, there is not, especially that majority of PyGame is written in C.

But if this function calls print, then it's PyGame bug, and you should just report it.

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
0

Based on @Alexander Chzhen solution, I present here the way to apply it on a function with an option to suppress printing or not.

    import os, sys
    class SuppressPrints:
        #different from Alexander`s answer
        def __init__(self, suppress=True):
            self.suppress = suppress

        def __enter__(self):
            if self.suppress:
                self._original_stdout = sys.stdout
                sys.stdout = open(os.devnull, 'w')

        def __exit__(self, exc_type, exc_val, exc_tb):
            if self.suppress:
                sys.stdout.close()
                sys.stdout = self._original_stdout
    #implementation
    def foo(suppress=True):
        with SuppressPrints(suppress):
            print("It will be printed, or not")

    foo(True)  #it will not be printed
    foo(False) #it will be printed

I hope I can add my solution below answer of Alexander as a comment, but I don`t have enough (50) reputations to do so.

Quan Hoang
  • 93
  • 7
0

If you want to enable/disable print with a variable, you could call an auxiliary function instead print, something like printe(the name is just for convenience)

def printe(*what_to_print):
if prints_enable:
    string = ""
    for items in what_to_print:
        string += str(items) + " "
    print(string)
-1

Define a new Print function where you enable print first. print your output next. And then disable print again.

def Print (*output):
   enablePrint()
   print (output)
   disablePrint()

with one of the above "safe" enable / disable pair of function

user2800464
  • 113
  • 3
  • 11
  • what's "enablePrint" and "disablePrint"? – misantroop Dec 16 '22 at 00:57
  • I did not want to repeat the codings again. So you you could choose any of the above mentioned stdout redirection functionds, especially the "safe" one by Alexander C, with the "with" option. – user2800464 Dec 17 '22 at 05:33