mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-12 06:02:49 +08:00
ENH: Add doctest_skip_parser allowing conditional skipping of doctests
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
"""Testing utilities."""
|
||||
|
||||
|
||||
import re
|
||||
|
||||
|
||||
SKIP_RE = re.compile("(\s*>>>.*?)(\s*)#\s*skip\s+if\s+(.*)$")
|
||||
|
||||
|
||||
def _assert_less(a, b, msg=None):
|
||||
message = "%r is not lower than %r" % (a, b)
|
||||
if msg is not None:
|
||||
@@ -24,3 +30,48 @@ try:
|
||||
from nose.tools import assert_greater
|
||||
except ImportError:
|
||||
assert_greater = _assert_greater
|
||||
|
||||
|
||||
def doctest_skip_parser(func):
|
||||
""" Decorator replaces custom skip test markup in doctests
|
||||
|
||||
Say a function has a docstring::
|
||||
|
||||
>>> something # skip if not HAVE_AMODULE
|
||||
>>> something + else
|
||||
>>> something # skip if HAVE_BMODULE
|
||||
|
||||
This decorator will evaluate the expresssion after ``skip if``. If this
|
||||
evaluates to True, then the comment is replaced by ``# doctest: +SKIP``. If
|
||||
False, then the comment is just removed. The expression is evaluated in the
|
||||
``globals`` scope of `func`.
|
||||
|
||||
For example, if the module global ``HAVE_AMODULE`` is False, and module
|
||||
global ``HAVE_BMODULE`` is False, the returned function will have docstring::
|
||||
|
||||
>>> something # doctest: +SKIP
|
||||
>>> something + else
|
||||
>>> something
|
||||
|
||||
"""
|
||||
lines = func.__doc__.split('\n')
|
||||
new_lines = []
|
||||
for line in lines:
|
||||
match = SKIP_RE.match(line)
|
||||
if match is None:
|
||||
new_lines.append(line)
|
||||
continue
|
||||
code, space, expr = match.groups()
|
||||
|
||||
try:
|
||||
# Works as a function decorator
|
||||
if eval(expr, func.__globals__):
|
||||
code = code + space + "# doctest: +SKIP"
|
||||
except AttributeError:
|
||||
# Works as a class decorator
|
||||
if eval(expr, func.__init__.__globals__):
|
||||
code = code + space + "# doctest: +SKIP"
|
||||
|
||||
new_lines.append(code)
|
||||
func.__doc__ = "\n".join(new_lines)
|
||||
return func
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
""" Testing decorators module
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from nose.tools import (assert_true, assert_raises, assert_equal)
|
||||
from skimage._shared.testing import doctest_skip_parser
|
||||
|
||||
|
||||
def test_skipper():
|
||||
def f():
|
||||
pass
|
||||
|
||||
class c():
|
||||
def __init__(self):
|
||||
self.me = "I think, therefore..."
|
||||
|
||||
docstring = \
|
||||
""" Header
|
||||
|
||||
>>> something # skip if not HAVE_AMODULE
|
||||
>>> something + else
|
||||
>>> a = 1 # skip if not HAVE_BMODULE
|
||||
>>> something2 # skip if HAVE_AMODULE
|
||||
"""
|
||||
f.__doc__ = docstring
|
||||
c.__doc__ = docstring
|
||||
|
||||
global HAVE_AMODULE, HAVE_BMODULE
|
||||
HAVE_AMODULE = False
|
||||
HAVE_BMODULE = True
|
||||
|
||||
f2 = doctest_skip_parser(f)
|
||||
c2 = doctest_skip_parser(c)
|
||||
assert_true(f is f2)
|
||||
assert_true(c is c2)
|
||||
|
||||
assert_equal(f2.__doc__,
|
||||
""" Header
|
||||
|
||||
>>> something # doctest: +SKIP
|
||||
>>> something + else
|
||||
>>> a = 1
|
||||
>>> something2
|
||||
""")
|
||||
assert_equal(c2.__doc__,
|
||||
""" Header
|
||||
|
||||
>>> something # doctest: +SKIP
|
||||
>>> something + else
|
||||
>>> a = 1
|
||||
>>> something2
|
||||
""")
|
||||
|
||||
HAVE_AMODULE = True
|
||||
HAVE_BMODULE = False
|
||||
f.__doc__ = docstring
|
||||
c.__doc__ = docstring
|
||||
f2 = doctest_skip_parser(f)
|
||||
c2 = doctest_skip_parser(c)
|
||||
|
||||
assert_true(f is f2)
|
||||
assert_equal(f2.__doc__,
|
||||
""" Header
|
||||
|
||||
>>> something
|
||||
>>> something + else
|
||||
>>> a = 1 # doctest: +SKIP
|
||||
>>> something2 # doctest: +SKIP
|
||||
""")
|
||||
assert_equal(c2.__doc__,
|
||||
""" Header
|
||||
|
||||
>>> something
|
||||
>>> something + else
|
||||
>>> a = 1 # doctest: +SKIP
|
||||
>>> something2 # doctest: +SKIP
|
||||
""")
|
||||
|
||||
del HAVE_AMODULE
|
||||
f.__doc__ = docstring
|
||||
c.__doc__ = docstring
|
||||
assert_raises(NameError, doctest_skip_parser, f)
|
||||
assert_raises(NameError, doctest_skip_parser, c)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
np.testing.run_module_suite()
|
||||
@@ -5,11 +5,18 @@ from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..qt import QtGui
|
||||
from ..qt import QtGui, qt_api
|
||||
from ..qt.QtCore import Qt, Signal
|
||||
from ..utils import RequiredAttr, init_qtapp
|
||||
from skimage._shared.testing import doctest_skip_parser
|
||||
|
||||
if qt_api is not None:
|
||||
has_qt = True
|
||||
else:
|
||||
has_qt = False
|
||||
|
||||
|
||||
@doctest_skip_parser
|
||||
class Plugin(QtGui.QDialog):
|
||||
"""Base class for plugins that interact with an ImageViewer.
|
||||
|
||||
@@ -52,13 +59,14 @@ class Plugin(QtGui.QDialog):
|
||||
>>> from skimage.viewer.widgets import Slider
|
||||
>>> from skimage import data
|
||||
>>>
|
||||
>>> plugin = Plugin(image_filter=lambda img, threshold: img > threshold)
|
||||
>>> plugin += Slider('threshold', 0, 255)
|
||||
>>> plugin = Plugin(image_filter=lambda img,
|
||||
... threshold: img > threshold) # skip if not has_qt
|
||||
>>> plugin += Slider('threshold', 0, 255) # skip if not has_qt
|
||||
>>>
|
||||
>>> image = data.coins()
|
||||
>>> viewer = ImageViewer(image)
|
||||
>>> viewer += plugin
|
||||
>>> # viewer.show()
|
||||
>>> viewer = ImageViewer(image) # skip if not has_qt
|
||||
>>> viewer += plugin # skip if not has_qt
|
||||
>>> viewer.show() # skip if not has_qt
|
||||
|
||||
The plugin will automatically delegate parameters to `image_filter` based
|
||||
on its parameter type, i.e., `ptype` (widgets for required arguments must
|
||||
@@ -99,7 +107,7 @@ class Plugin(QtGui.QDialog):
|
||||
self.row = 0
|
||||
|
||||
self.arguments = []
|
||||
self.keyword_arguments= {}
|
||||
self.keyword_arguments = {}
|
||||
|
||||
self.useblit = useblit
|
||||
self.cids = []
|
||||
@@ -254,4 +262,3 @@ class Plugin(QtGui.QDialog):
|
||||
you don't want to return a value.
|
||||
"""
|
||||
return (self.image_viewer.image, None)
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
"""
|
||||
ImageViewer class for viewing and interacting with images.
|
||||
"""
|
||||
from ..qt import QtGui
|
||||
from ..qt import QtGui, qt_api
|
||||
from ..qt.QtCore import Qt, Signal
|
||||
|
||||
if qt_api is not None:
|
||||
has_qt = True
|
||||
else:
|
||||
has_qt = False
|
||||
|
||||
from skimage import io, img_as_float
|
||||
from skimage.util.dtype import dtype_range
|
||||
from skimage.exposure import rescale_intensity
|
||||
from skimage._shared.testing import doctest_skip_parser
|
||||
import numpy as np
|
||||
from .. import utils
|
||||
from ..widgets import Slider
|
||||
@@ -45,6 +51,7 @@ def mpl_image_to_rgba(mpl_image):
|
||||
return img_as_float(image)
|
||||
|
||||
|
||||
@doctest_skip_parser
|
||||
class ImageViewer(QtGui.QMainWindow):
|
||||
"""Viewer for displaying images.
|
||||
|
||||
@@ -72,8 +79,8 @@ class ImageViewer(QtGui.QMainWindow):
|
||||
--------
|
||||
>>> from skimage import data
|
||||
>>> image = data.coins()
|
||||
>>> viewer = ImageViewer(image)
|
||||
>>> viewer.show() # doctest: +SKIP
|
||||
>>> viewer = ImageViewer(image) # skip if not has_qt
|
||||
>>> viewer.show() # skip if not has_qt
|
||||
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user