I have simple Python code that uses dockets
#!/usr/bin/python
# http://stackoverflow.com/questions/2708178/python-using-doctests-for-classes
class Test:
def __init__(self, number):
self._number=number
def multiply_by_2(self):
"""
>>> t.multiply_by_2()
4
"""
return self._number*2
if __name__ == "__main__":
import doctest
doctest.testmod(extraglobs={'t': Test(2)})
I can use it with python interpreter:
> python simple.py
However, when I execute the code from doctest module, I get this error:
> python -m doctest simple.py
**********************************************************************
File "simple.py", line 10, in simple.Test.multiply_by_2
Failed example:
t.multiply_by_2()
Exception raised:
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/doctest.py", line 1289, in __run
compileflags, 1) in test.globs
File "<doctest simple.Test.multiply_by_2[0]>", line 1, in <module>
t.multiply_by_2()
NameError: name 't' is not defined
**********************************************************************
1 items had failures:
1 of 1 in simple.Test.multiply_by_2
***Test Failed*** 1 failures.
Why is this difference? How to resolve this issue?
The difference is that when you execute via doctest, it is the __main__ module compared to executing directly where your script's if __name__ == '__main__' block will execute.
I don't know of a good solution other than to put all the information you need in the docstring itself:
def multiply_by_2(self):
"""
>>> t = Test(2)
>>> t.multiply_by_2()
4
"""
return self._number * 2
This will have the added benefit that users who are reading your docstrings will know what's going on ... They won't have to stumble upon your extraglobs keyword to figure out what t is and how it was initialized.
Related
I try to test a property with a doctest in py.test but I get a NameError saying that the class is not defined.
Here's a little example:
class ExampleClass:
def __init__(self, attribute):
self.attribute = attribute
#property
def attribute_squared(self):
"""
Examples:
>>> ExampleClass(attribute=2).attribute_squared
4
"""
return self.attribute ** 2
When running it whin py.test I get:
Error
**********************************************************************
Line 3, in ExampleClass
Failed example:
ExampleClass(attribute=2)
Exception raised:
Traceback (most recent call last):
File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.1.5\helpers\pycharm\docrunner.py", line 140, in __run
compileflags, 1), test.globs)
File "<doctest ExampleClass[0]>", line 1, in <module>
ExampleClass(attribute=2).attribute_squared
NameError: name 'ExampleClass' is not defined
Is there a way to test class properties with or should I write a real test?
Finally I found that the test doesn't pass only when I do right click/Run 'Doctest attribute_squared' in Pycharm.
When running pytest, the test passes.
I would like to create a python module that would be called with python -m mymodule somefile.py some_arg some_arg.
The idea is that I would be able to set up an alias alias="python -m mymodule" and call files normally with python somefile.py some_arg some_arg.
In the file mymodule/__main__.py, what is the best way to load somefile.py and pass it the argument list?
I am looking for a generic solution, that would be python2 and 3 compatible.
It would be great to be as little intrusive as possible. If somefile.py would raise an exception, mymodule should barely be seen in the traceback.
What the module does is not interesting here in detail, but it sets up some python things (traceback hooks etc.), so somefile.py should be ran pythonicly in the same process. os.system or subprocess.Popen do not fit.
Ok I found something good for python 3.5, and satisfying enough for python 2.7.
mymodule/main.py
import sys
# The following block of code removes the part of
# the traceback related to this very module, and runpy
# Negative limit support came with python 3.5, so it will not work
# with previous versions.
# https://docs.python.org/3.5/library/traceback.html#traceback.print_tb
def myexcepthook(type, value, tb):
nb_noise_lines = 3
traceback_size = len(traceback.extract_tb(tb))
traceback.print_tb(tb, nb_noise_lines - traceback_size)
if sys.version_info >= (3, 5):
sys.excepthook = myexcepthook
if len(sys.argv) > 1:
file = sys.argv[1]
sys.argv = sys.argv[1:]
with open(file) as f:
code = compile(f.read(), file, 'exec')
exec(code)
somefile.py
import sys
print sys.argv
raise Exception()
in the terminal
$ python3 -m mymodule somefile.py some_arg some_arg
['somefile.py', 'some_arg', 'some_arg']
Traceback (most recent call last):
File "somefile.py", line 3, in <module>
raise Exception()
$ python2 -m mymodule somefile.py some_arg some_arg
['somefile.py', 'some_arg', 'some_arg']
Traceback (most recent call last):
File "/usr/lib64/python3.5/runpy.py", line 184, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib64/python3.5/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/azmeuk/dev/testpy/mymodule/__main__.py", line 16, in <module>
exec(code)
File "somefile.py", line 3, in <module>
raise Exception()
$ python somefile.py some_arg some_arg
['somefile.py', 'some_arg', 'some_arg']
Traceback (most recent call last):
File "somefile.py", line 3, in <module>
raise Exception()
Exception
Still, if someone has a better proposition, it would be great!
I think the negative value of limit does not work in traceback module before python 3.5. Here is an ugly hack that works with python 2.7
import sys
import traceback
class ExcFile(object):
def __init__(self, file):
self.topline = True
self.file = file
def write(self, s):
if self.topline:
u, s = s.split('\n', 1)
self.file.write(u +'\n')
self.topline = False
if '#---\n' in s:
u, s = s.split('#---\n', 1)
self.file.write(s)
self.write = self.file.write
ExcFile._instance = ExcFile(sys.stdout)
# The following block of code removes the part of
# the traceback related to this very module, and runpy
def myexcepthook(type, value, tb):
traceback.print_exception(type, value, tb, file=ExcFile._instance)
sys.excepthook = myexcepthook
if len(sys.argv) > 1:
file = sys.argv[1]
sys.argv = sys.argv[1:]
with open(file) as f:
code = compile(f.read(), file, 'exec')
exec(code) #---
All this should be written in a separate file, to avoid clutter __main__.py.
I have a simple function with a doctest, which, when run with Sphinx's make doctest, gives me the following error:
File "scheemey.rst", line ?, in default
Failed example:
verify_balanced('asdf (foo [bar] [[baz], {}, ()]')
Exception raised:
Traceback (most recent call last):
File "/usr/local/Cellar/python/2.7.8/Frameworks/Python.framework/Versions/2.7/lib/python2.7/doctest.py", line 1315, in __run
compileflags, 1) in test.globs
File "<doctest default[0]>", line 1, in <module>
verify_balanced('asdf (foo [bar] [[baz], {}, ()]')
NameError: name 'verify_balanced' is not defined
What could be causing this?
I can reproduce the error in the question if the module with the tested function is not imported properly.
To make it work, you can use a testsetup directive:
.. testsetup::
from yourmodule import verify_balanced
>>> verify_balanced('asdf (foo) [bar] [[baz], {}, ()]')
>>> verify_balanced('asdf (foo [bar] [[baz], {}, ()]')
5
Note that doctest ignores None return values (see Python doctests: test for None).
I use below method to generate cases automatically in python unittest.
import unittest
class Tests(unittest.TestCase):
def check(self, i):
self.assertNotEquals(0, i)
for i in [0, 1, 2]:
def ch(i):
return lambda self: self.check(i)
setattr(Tests, "test_%d" % i, ch(i))
if __name__ == "__main__":
unittest.main()
It works well when "python test.py" to run all cases together.
But fails to run a specific case, like "python test.py Tests.test_0"
Traceback (most recent call last):
File "test.py", line 12, in <module>
unittest.main()
File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
self.parseArgs(argv)
File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
self.createTests()
File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
self.module)
File "/usr/lib/python2.7/unittest/loader.py", line 128, in loadTestsFromNames
suites = [self.loadTestsFromName(name, module) for name in names]
File "/usr/lib/python2.7/unittest/loader.py", line 109, in loadTestsFromName
return self.suiteClass([parent(obj.__name__)])
File "/usr/lib/python2.7/unittest/case.py", line 191, in __init__
(self.__class__, methodName))
ValueError: no such test method in <class '__main__.Tests'>: <lambda>
Anyone can help?
It appears (from the error message) and from perusing the source of unittest, that the test runner gets a reference to the method and then uses the method's __name__ attribute to figure out which method to run. This is a bit silly (after all, we already picked up a reference to the method!), but it does have some benefits in terms of code simplicity. The quick fix is to make sure that you set the name on your lambda functions as well:
import unittest
class Tests(unittest.TestCase):
def check(self, i):
self.assertNotEquals(0, i)
for i in [0, 1, 2]:
def ch(i):
return lambda self: self.check(i)
f = ch(i)
name = "test_%d" % i
f.__name__ = name
setattr(Tests, name, f)
if __name__ == "__main__":
unittest.main()
Now everything works as it should:
mgilson$ python test.py Tests.test_0
F
======================================================================
FAIL: test_0 (__main__.Tests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 13, in <lambda>
return lambda self: self.check(i)
File "test.py", line 5, in check
self.assertNotEquals(0, i)
AssertionError: 0 == 0
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
and:
mgilson$ python test.py Tests.test_1
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Note that it appears this this was a bug which was fixed. In the current HEAD, we can see that the name is parsed from the string as opposed to in the version which I have (and apparently you do to) where the name is picked up from the object (function) to be run.
With
def show(a):
""" Shows a string
>>> show(a)
a
"""
print(a)
def test():
import doctest
doctest.testmod()
if __name__ == '__main__': test()
I am getting an error while trying to learn how a docstring works.
Both this method and running it from command line with
python -m doctest unittest.py
ends with errors.
Traceback (most recent call last):
File "/home/liquid/workspace/MyPythonProject/src/unittest.py", line 19, in <module>
if __name__ == '__main__': test()
File "/home/liquid/workspace/MyPythonProject/src/unittest.py", line 16, in test
import doctest
File "/usr/lib/python3.2/doctest.py", line 2105, in <module>
class DocTestCase(unittest.TestCase):
AttributeError: 'module' object has no attribute 'TestCase'
Why?
Unfortunately you named your module the same as the one containing TestCase. Rename unittest.py to myunittest.py and see if it works.