37

I'd like to write a doctest like this:

"""
>>> print a.string()
          foo : a
          bar : b
         date : <I don't care about the date output>
          baz : c
"""

Is there any way to do this? I think it would make more sense to switch to unittest, but I'm curious whether it's possible to specify a range of output that shouldn't be matched for the test in doctest.

Thanks!

cjb
  • 679
  • 1
  • 6
  • 8

6 Answers6

46

With doctest.ELLIPSIS, you can use ... to mean "match any string here". You can set doctest options with a doctest directive, to make it active for just one test case: one example in the online docs is:

>>> print range(20) # doctest:+ELLIPSIS
[0, 1, ..., 18, 19]

If you want a doctest option to be active throughout, you can pass it as the optionflags= argument to whatever doctest functions you use, e.g. doctest.testfile. (You can pass multiple option flags there by using the | operator to bit-or them).

Alex Martelli
  • 762,786
  • 156
  • 1,160
  • 1,345
  • 3
    But how can we ignore the whole line? – Roman Dolgiy Jan 11 '10 at 17:44
  • 2
    @t0ster, just put `...` as the whole of the "expected output" (with `doctest.ELLIPSIS` set, of course), and doctest will accept any content on that output line, i.e. "ignore the whole line" of output. – Alex Martelli Jan 11 '10 at 18:42
  • 10
    "ignore the whole line" as above does not work since doctest confuses the ellipsis with a (python) continuation of the previous line. I could not find any way to ignore the whole line. Actually, I was looking for "ignore the whole output". – amit Jun 03 '11 at 05:53
  • 1
    @RomanDolgiy and phaedrus: if you don't care about side effects, use doctest.SKIP. If you do care about side effects, then there's no great solutions, but in a separate answer below, I wrote up some ways to make it work if you really need it. – Edward Loper Feb 22 '12 at 18:36
17

Responding to questions about "how can we ignore the whole line": yes, the fact that "..." also looks like a continuation like makes it hard to ignore the entire output. You can use "#doctest: +SKIP" if you just want to skip the example entirely, but that won't work if you are relying on its side-effects. If you really need to do this, I suppose you could monkey-patch the doctest module itself, though I wouldn't particularly recommend it:

>>> import doctest
>>> doctest.ELLIPSIS_MARKER = '-etc-'
>>> print 12 # doctest: +ELLIPSIS
-etc-

(this test passes.)

Or you could temporarily suppress stdout and/or stderr:

>>> # Suppress stdout
>>> import sys
>>> class DevNull:
...     def noop(*args, **kwargs): pass
...     close = write = flush = writelines = noop
>>> sys.stdout = DevNull()
>>> # Run a test and ignore output (but we need its side effects)
>>> print 12 # NOTE: stdout is suppressed!
>>> # Restore stdout
>>> sys.stdout = sys.__stdout__

(this test also passes.)

Edward Loper
  • 13,458
  • 5
  • 38
  • 50
  • Python 3 [**`io.TextIO.write`**](https://docs.python.org/3/library/io.html#io.TextIOBase.write) now returns the number of characters written. – Peter Wood Apr 08 '21 at 08:49
10

I found it easier to simply assign the unneeded return values to a variable:

>>> _ = do_something()
>>> check_something()
True
WilliamMayor
  • 675
  • 5
  • 14
10

Ignoring the whole line is bit tricky though. Here:

"""
>>> do_your_thing() #doctest:+ELLIPSIS
...
"""

The triple dot will be interpreted as line continuation, and cause a syntax error.

If you want to ignore the whole line, you'll need something like:

"""
>>> sys.stdout.write('skip from here '); do_your_thing() #doctest:+ELLIPSIS
skip from here ...
"""
Mark Horvath
  • 934
  • 1
  • 8
  • 19
5

You can write tuples before and after your function (hack inspired by answer of Mark Horvath):

def foo():
    """
    >>> ();foo();() # doctest: +ELLIPSIS
    (...)
    """
    print "Hello world"
    return "Hello world"
  • I like this one the most. Uses Mark's trick without needing to import another module just for doctest. Great idea! – Connor Aug 17 '18 at 04:34
0

Can I have an ellipsis at the beginning of the line in a Python doctest? explains how to create a custom output checker that uses an additional string as an ellipsis. This would let you write the following, while still used '...' elsewhere.

def foo():
  """
  >>> foo() # doctest: +ELLIPSIS
  [...] world
  """
  print "hello world"
Community
  • 1
  • 1
samwyse
  • 2,298
  • 1
  • 22
  • 32