Skip to content

Py3fy testing machinery. #10903

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions lib/matplotlib/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,6 @@ def is_called_from_pytest():
return getattr(mpl, '_called_from_pytest', False)


def _copy_metadata(src_func, tgt_func):
"""Replicates metadata of the function. Returns target function."""
functools.update_wrapper(tgt_func, src_func)
tgt_func.__wrapped__ = src_func # Python2 compatibility.
return tgt_func


def set_font_settings_for_testing():
mpl.rcParams['font.family'] = 'DejaVu Sans'
mpl.rcParams['text.hinting'] = False
Expand Down
19 changes: 9 additions & 10 deletions lib/matplotlib/testing/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,15 +181,14 @@ def __call__(self, orig, dest):
# reported as a regular exception below).
env.pop("DISPLAY", None) # May already be unset.
# Do not load any user options.
# `os.environ` needs native strings on Py2+Windows.
env[str("INKSCAPE_PROFILE_DIR")] = os.devnull
env["INKSCAPE_PROFILE_DIR"] = os.devnull
# Old versions of Inkscape (0.48.3.1, used on Travis as of now)
# seem to sometimes deadlock when stderr is redirected to a pipe,
# so we redirect it to a temporary file instead. This is not
# necessary anymore as of Inkscape 0.92.1.
self._stderr = TemporaryFile()
self._proc = subprocess.Popen(
[str("inkscape"), "--without-gui", "--shell"],
["inkscape", "--without-gui", "--shell"],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=self._stderr, env=env)
if not self._read_to_prompt():
Expand All @@ -210,7 +209,7 @@ def fsencode(s):
# slow solution (Inkscape uses `fgets` so it will always stop at a
# newline).
return make_external_conversion_command(lambda old, new: [
str('inkscape'), '-z', old, '--export-png', new])(orig, dest)
'inkscape', '-z', old, '--export-png', new])(orig, dest)
self._proc.stdin.write(orig_b + b" --export-png=" + dest_b + b"\n")
self._proc.stdin.flush()
if not self._read_to_prompt():
Expand Down Expand Up @@ -329,8 +328,8 @@ def calculate_rms(expectedImage, actualImage):
"Calculate the per-pixel errors, then compute the root mean square error."
if expectedImage.shape != actualImage.shape:
raise ImageComparisonFailure(
"Image sizes do not match expected size: {0} "
"actual size {1}".format(expectedImage.shape, actualImage.shape))
"Image sizes do not match expected size: {} "
"actual size {}".format(expectedImage.shape, actualImage.shape))
# Convert to float to avoid overflowing finite integer types.
return np.sqrt(((expectedImage - actualImage).astype(float) ** 2).mean())

Expand Down Expand Up @@ -361,7 +360,7 @@ def compare_images(expected, actual, tol, in_decorator=False):
--------
img1 = "./baseline/plot.png"
img2 = "./output/plot.png"
compare_images( img1, img2, 0.001 ):
compare_images(img1, img2, 0.001):

"""
if not os.path.exists(actual):
Expand Down Expand Up @@ -391,7 +390,7 @@ def compare_images(expected, actual, tol, in_decorator=False):

diff_image = make_test_filename(actual, 'failed-diff')

if tol <= 0.0:
if tol <= 0:
if np.array_equal(expectedImage, actualImage):
return None

Expand Down Expand Up @@ -431,8 +430,8 @@ def save_diff_image(expected, actual, output):
actualImage = np.array(actualImage).astype(float)
if expectedImage.shape != actualImage.shape:
raise ImageComparisonFailure(
"Image sizes do not match expected size: {0} "
"actual size {1}".format(expectedImage.shape, actualImage.shape))
"Image sizes do not match expected size: {} "
"actual size {}".format(expectedImage.shape, actualImage.shape))
absDiffImage = np.abs(expectedImage - actualImage)

# expand differences in luminance domain
Expand Down
63 changes: 21 additions & 42 deletions lib/matplotlib/testing/decorators.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from distutils.version import StrictVersion
import functools
import inspect
import os
from pathlib import Path
import shutil
Expand All @@ -23,7 +22,7 @@
from matplotlib import ft2font
from matplotlib.testing.compare import (
comparable_formats, compare_images, make_test_filename)
from . import _copy_metadata, is_called_from_pytest
from . import is_called_from_pytest
from .exceptions import ImageComparisonFailure


Expand Down Expand Up @@ -250,9 +249,9 @@ def copy_baseline(self, baseline, extension):
if os.path.exists(orig_expected_fname):
shutil.copyfile(orig_expected_fname, expected_fname)
else:
reason = ("Do not have baseline image {0} because this "
"file does not exist: {1}".format(expected_fname,
orig_expected_fname))
reason = ("Do not have baseline image {} because this "
"file does not exist: {}".format(expected_fname,
orig_expected_fname))
raise ImageComparisonFailure(reason)
return expected_fname

Expand Down Expand Up @@ -327,11 +326,12 @@ def __call__(self, func):
self.delayed_init(func)
import nose.tools

@functools.wraps(func)
@nose.tools.with_setup(self.setup, self.teardown)
def runner_wrapper():
yield from self.nose_runner()

return _copy_metadata(func, runner_wrapper)
return runner_wrapper


def _pytest_image_comparison(baseline_images, extensions, tol,
Expand All @@ -350,6 +350,7 @@ def _pytest_image_comparison(baseline_images, extensions, tol,
extensions = map(_mark_xfail_if_format_is_uncomparable, extensions)

def decorator(func):
@functools.wraps(func)
# Parameter indirection; see docstring above and comment below.
@pytest.mark.usefixtures('mpl_image_comparison_parameters')
@pytest.mark.parametrize('extension', extensions)
Expand Down Expand Up @@ -379,8 +380,7 @@ def wrapper(*args, **kwargs):
for idx, baseline in enumerate(baseline_images):
img.compare(idx, baseline, extension)

wrapper.__wrapped__ = func # For Python 2.7.
return _copy_metadata(func, wrapper)
return wrapper

return decorator

Expand Down Expand Up @@ -408,7 +408,7 @@ def image_comparison(baseline_images, extensions=None, tol=0,
extensions : [ None | list ]

If None, defaults to all supported extensions.
Otherwise, a list of extensions to test. For example ['png','pdf'].
Otherwise, a list of extensions to test, e.g. ``['png', 'pdf']``.

tol : float, optional, default: 0
The RMS threshold above which the test is considered failed.
Expand Down Expand Up @@ -464,10 +464,10 @@ def _image_directories(func):
# FIXME: this won't work for nested packages in matplotlib.tests
warnings.warn(
'Test module run as script. Guessing baseline image locations.')
script_name = sys.argv[0]
basedir = os.path.abspath(os.path.dirname(script_name))
subdir = os.path.splitext(os.path.split(script_name)[1])[0]
module_path = Path(sys.argv[0]).resolve()
subdir = module_path.stem
else:
module_path = Path(sys.modules[func.__module__].__file__)
mods = module_name.split('.')
if len(mods) >= 3:
mods.pop(0)
Expand All @@ -486,50 +486,29 @@ def _image_directories(func):
"file (can be empty).".format(module_name))
subdir = os.path.join(*mods)

import imp
def find_dotted_module(module_name, path=None):
"""A version of imp which can handle dots in the module name.
As for imp.find_module(), the return value is a 3-element
tuple (file, pathname, description)."""
res = None
for sub_mod in module_name.split('.'):
try:
res = file, path, _ = imp.find_module(sub_mod, path)
path = [path]
if file is not None:
file.close()
except ImportError:
# assume namespace package
path = list(sys.modules[sub_mod].__path__)
res = None, path, None
return res

mod_file = find_dotted_module(func.__module__)[1]
basedir = os.path.dirname(mod_file)
baseline_dir = module_path.parent / 'baseline_images' / subdir
result_dir = Path().resolve() / 'result_images' / subdir
result_dir.mkdir(parents=True, exist_ok=True)

baseline_dir = os.path.join(basedir, 'baseline_images', subdir)
result_dir = os.path.abspath(os.path.join('result_images', subdir))
Path(result_dir).mkdir(parents=True, exist_ok=True)

return baseline_dir, result_dir
return str(baseline_dir), str(result_dir)


def switch_backend(backend):
# Local import to avoid a hard nose dependency and only incur the
# import time overhead at actual test-time.

def switch_backend_decorator(func):

@functools.wraps(func)
def backend_switcher(*args, **kwargs):
try:
prev_backend = mpl.get_backend()
matplotlib.testing.setup()
plt.switch_backend(backend)
result = func(*args, **kwargs)
return func(*args, **kwargs)
finally:
plt.switch_backend(prev_backend)
return result

return _copy_metadata(func, backend_switcher)
return backend_switcher

return switch_backend_decorator


Expand Down