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
|
|
|
|