|
1
|
|
|
# Copyright (c) 2017 MetPy Developers. |
|
2
|
|
|
# Distributed under the terms of the BSD 3-Clause License. |
|
3
|
|
|
# SPDX-License-Identifier: BSD-3-Clause |
|
4
|
|
|
r"""Tests the operation of MetPy's unit support code.""" |
|
5
|
|
|
|
|
6
|
|
|
import sys |
|
7
|
|
|
|
|
8
|
|
|
import matplotlib.pyplot as plt |
|
9
|
|
|
import pytest |
|
10
|
|
|
|
|
11
|
|
|
from metpy.testing import set_agg_backend # noqa: F401 |
|
12
|
|
|
from metpy.units import check_units, units |
|
13
|
|
|
|
|
14
|
|
|
|
|
15
|
|
|
@pytest.mark.mpl_image_compare(tolerance=0, remove_text=True) |
|
16
|
|
|
def test_axhline(): |
|
17
|
|
|
r"""Ensure that passing a quantity to axhline does not error.""" |
|
18
|
|
|
fig, ax = plt.subplots() |
|
19
|
|
|
ax.axhline(930 * units('mbar')) |
|
20
|
|
|
ax.set_ylim(900, 950) |
|
21
|
|
|
ax.set_ylabel('') |
|
22
|
|
|
return fig |
|
23
|
|
|
|
|
24
|
|
|
|
|
25
|
|
|
@pytest.mark.mpl_image_compare(tolerance=0, remove_text=True) |
|
26
|
|
|
def test_axvline(): |
|
27
|
|
|
r"""Ensure that passing a quantity to axvline does not error.""" |
|
28
|
|
|
fig, ax = plt.subplots() |
|
29
|
|
|
ax.axvline(0 * units('degC')) |
|
30
|
|
|
ax.set_xlim(-1, 1) |
|
31
|
|
|
ax.set_xlabel('') |
|
32
|
|
|
return fig |
|
33
|
|
|
|
|
34
|
|
|
# |
|
35
|
|
|
# Tests for unit-checking decorator |
|
36
|
|
|
# |
|
37
|
|
|
|
|
38
|
|
|
|
|
39
|
|
|
def unit_calc(temp, press, dens, mixing, unitless_const): |
|
40
|
|
|
r"""Stub calculation for testing unit checking.""" |
|
41
|
|
|
pass |
|
42
|
|
|
|
|
43
|
|
|
|
|
44
|
|
|
test_funcs = [ |
|
45
|
|
|
check_units('[temperature]', '[pressure]', dens='[mass]/[volume]', |
|
46
|
|
|
mixing='[dimensionless]')(unit_calc), |
|
47
|
|
|
check_units(temp='[temperature]', press='[pressure]', dens='[mass]/[volume]', |
|
48
|
|
|
mixing='[dimensionless]')(unit_calc), |
|
49
|
|
|
check_units('[temperature]', '[pressure]', '[mass]/[volume]', |
|
50
|
|
|
'[dimensionless]')(unit_calc)] |
|
51
|
|
|
|
|
52
|
|
|
|
|
53
|
|
|
@pytest.mark.parametrize('func', test_funcs, ids=['some kwargs', 'all kwargs', 'all pos']) |
|
54
|
|
|
def test_good_units(func): |
|
55
|
|
|
r"""Test that unit checking passes good units regardless.""" |
|
56
|
|
|
func(30 * units.degC, 1000 * units.mbar, 1.0 * units('kg/m^3'), 1, 5.) |
|
57
|
|
|
|
|
58
|
|
|
|
|
59
|
|
|
test_params = [((30 * units.degC, 1000 * units.mb, 1 * units('kg/m^3'), 1, 5 * units('J/kg')), |
|
60
|
|
|
{}, [('press', '[pressure]', 'millibarn')]), |
|
61
|
|
|
((30, 1000, 1.0, 1, 5.), {}, [('press', '[pressure]', 'none'), |
|
62
|
|
|
('temp', '[temperature]', 'none'), |
|
63
|
|
|
('dens', '[mass]/[volume]', 'none')]), |
|
64
|
|
|
((30, 1000 * units.mbar), |
|
65
|
|
|
{'dens': 1.0 * units('kg / m'), 'mixing': 5 * units.m, 'unitless_const': 2}, |
|
66
|
|
|
[('temp', '[temperature]', 'none'), |
|
67
|
|
|
('dens', '[mass]/[volume]', 'kilogram / meter'), |
|
68
|
|
|
('mixing', '[dimensionless]', 'meter')])] |
|
69
|
|
|
|
|
70
|
|
|
|
|
71
|
|
|
@pytest.mark.skipif(sys.version_info < (3, 3), reason='Unit checking requires Python >= 3.3') |
|
72
|
|
|
@pytest.mark.parametrize('func', test_funcs, ids=['some kwargs', 'all kwargs', 'all pos']) |
|
73
|
|
|
@pytest.mark.parametrize('args,kwargs,bad_parts', test_params, |
|
74
|
|
|
ids=['one bad arg', 'all args no units', 'mixed args']) |
|
75
|
|
|
def test_bad(func, args, kwargs, bad_parts): |
|
76
|
|
|
r"""Test that unit checking flags appropriate arguments.""" |
|
77
|
|
|
with pytest.raises(ValueError) as exc: |
|
78
|
|
|
func(*args, **kwargs) |
|
79
|
|
|
|
|
80
|
|
|
message = str(exc.value) |
|
81
|
|
|
assert func.__name__ in message |
|
82
|
|
|
for param in bad_parts: |
|
83
|
|
|
assert '`{}` requires "{}" but given "{}"'.format(*param) in message |
|
84
|
|
|
|
|
85
|
|
|
# Should never complain about the const argument |
|
86
|
|
|
assert 'unitless_const' not in message |
|
87
|
|
|
|