Can I have an ellipsis at the beginning of the lin

2019-02-21 09:45发布

问题:

Python doctests are cool. Let me start with a simple example:

def foo():
  """
  >>> foo()
  hello world
  """
  print "hello world"

Now let's assume some part is somewhat varying, e.g., because it is a time value or a random number. In general, doctests allow me to specify a wildcard saying using the +ELLIPSIS option.

This works fine when for instance "world" is a varying string:

def foo():
  """
  >>> foo()   # doctest: +ELLIPSIS
  hello ...
  """
  print "hello world"

In my case however, the variable string is at the beginning of the line:

def foo():
  """
  >>> foo() # doctest: +ELLIPSIS
  ... world
  """
  print "hello world"

which is bad, because the 3 dots in the beginning are interpreted as line continuation characters and not as ellipsis for the output. Therefore, this test fails:

Failed example:
    foo() # doctest: +ELLIPSIS
    world
Expected nothing
Got:
    hello world

So, I could now rewrite my could to have the variable part somewhere else, but is there any way to teach doctest that the 3 dots at the beginning of a line are an ellipsis?

回答1:

Here's a quick and dirty hack for you:

def foo():
    """
    >>> foo() # doctest: +ELLIPSIS
    [...] world
    """
    print "hello world"

if __name__ == "__main__":
    import doctest

    OC = doctest.OutputChecker
    class AEOutputChecker(OC):
        def check_output(self, want, got, optionflags):
            from re import sub
            if optionflags & doctest.ELLIPSIS:
                want = sub(r'\[\.\.\.\]', '...', want)
            return OC.check_output(self, want, got, optionflags)

    doctest.OutputChecker = AEOutputChecker
    doctest.testmod()

This still understands the normal ( ... ) ellipsis, but it adds a new one ( [...] ) that doesn't raise the line start ambiguity.

It would be seriously hard for doctest to guess whether there is a line continuation pending or whether its a line start ellipsis - it can be done, in theory, if you subclass DocTestParser to do that work but it probably won't be fun.

In complex situations you should probably roll your own DocTestRunner that would use the new OutputChecker and use that instead of the normal testmod but this should do in simple scenarios.



回答2:

You can update the ELLIPSIS_MARKER for your test so that ... does not get confused with the line continuation dots:

def foo():
    """
    >>> import doctest
    >>> doctest.ELLIPSIS_MARKER = '-ignore-'
    >>> foo()
    hello world
    >>> foo() # doctest: +ELLIPSIS
    -ignore- world
    """
    print "hello world"

if __name__ == "__main__":
    import doctest
    doctest.testmod()


Disclaimer: the example above works when doctests are run as

$ py.test --doctest-module foo.py

or

$ python foo.py

However, for reasons I don't understand it does not work when running doctests via

$ python -m doctest foo.py


回答3:

Here is a somewhat simpler way to do this: Just print a dummy string before the line that begins with the unknown output.

Like this:

def foo():
  """
  >>> print 'ignore'; foo() # doctest: +ELLIPSIS
  ignore... world
  """
  print "hello world"