111

I'm using the following code to hide stderr on Linux/OSX for a Python library I do not control that writes to stderr by default:

f = open("/dev/null","w")
zookeeper.set_log_stream(f)

Is there an easy cross platform alternative to /dev/null? Ideally it would not consume memory since this is a long running process.

3
  • 2
    possible duplicate of /dev/null in Windows? Commented May 28, 2010 at 14:39
  • 10
    @msw: I don't think so, Python has more ways you can deal with this issue. Commented May 28, 2010 at 14:45
  • note: ■ hand-written null file object might actually be slower than open(os.devnull) according to user stackoverflow.com/a/13944391/5267751 ■ if you want to redirect stdout there's a special solution stackoverflow.com/a/47419332/5267751 Commented Nov 16, 2022 at 9:54

6 Answers 6

193

How about os.devnull ?

import os
f = open(os.devnull,"w")
zookeeper.set_log_stream(f)
Sign up to request clarification or add additional context in comments.

1 Comment

os.devnull also works for with open() blocks, where the 'file-like object' solution throws: TypeError: expected str, bytes or os.PathLike, not Devnull
49
class Devnull(object):
    def write(self, *_): pass

zookeeper.set_log_stream(Devnull())

Opening os.devnull is fine too of course, but this way every output operation occurs (as a noop) "in process" -- no context switch to the OS and back, and also no buffering (while some buffering is normally used by an open) and thus even less memory consumption.

5 Comments

I understand that using os.devnull may produce some overhead. But if one uses your object what if the zookeeper object calls other methods then write of its log_stream file object? Maybe it calls the writelines method? Then there is an exception.
This doesn't work when you need a real file, e.g. one with fileno().
@JonathonReinhart For that, I suppose you could create the file descriptor lazily on request, using os.open(os.devnull, os.O_RDWR) and yielding the same fd for subsequent calls to fileno (since all data are discarded anyway)
You'll also need close()
@shoosh Would inhering from io.IOBase and just overwrite write() as above do the trick? That would provide all the methods clients might expect.
9
>>> import os
>>> os.devnull
'nul'

1 Comment

Just to clarify: 'nul' is given on Windows. Linux will return '/dev/null'.
5

Create your own file-like object which doesn't do anything?

class FakeSink(object):
    def write(self, *args):
        pass
    def writelines(self, *args):
        pass
    def close(self, *args):
        pass

2 Comments

Idiomatically, you're right, but 'self' is just another parameter and will be passed in as the first element of args. As we don't use any of the parameters, the only reason to care is aesthetic. I'll fix it...
Some operations also need 'fileno'
3

Cheap solution warning!

class DevNull():
  def __init__(self, *args):
    self.closed = False
    self.mode = "w"
    self.name = "<null>"
    self.encoding = None
    self.errors = None
    self.newlines = None
    self.softspace = 0
  def close(self):
    self.closed == True
  @open_files_only
  def flush(self):
    pass
  @open_files_only
  def next(self):
    raise IOError("Invalid operation")
  @open_files_only
  def read(size = 0):
    raise IOError("Invalid operation")
  @open_files_only
  def readline(self):
    raise IOError("Invalid operation")
  @open_files_only
  def readlines(self):
    raise IOError("Invalid operation")
  @open_files_only
  def xreadlines(self):
    raise IOError("Invalid operation")
  @open_files_only
  def seek(self):
    raise IOError("Invalid operation")
  @open_files_only
  def tell(self):
    return 0
  @open_files_only
  def truncate(self):
    pass
  @open_files_only
  def write(self):
    pass
  @open_files_only
  def writelines(self):
    pass

def open_files_only(fun):
  def wrapper(self, *args):
    if self.closed:
      raise IOError("File is closed")
    else:
      fun(self, *args)
  return wrapper

2 Comments

I threw in a decorator just for fun :D
Also need enter and exit ?
0

Use builtin len that would also return amount of bytes "written" (as expected from write):

import types

f = types.SimpleNamespace(write=len)
zookeeper.set_log_stream(f)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.