• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pytroll / pyresample / 4653744392

pending completion
4653744392

Pull #511

github

GitHub
Merge edb481dd6 into 7ca8789a3
Pull Request #511: Bump pypa/gh-action-pypi-publish from 1.8.4 to 1.8.5

12270 of 13075 relevant lines covered (93.84%)

3.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.77
/pyresample/test/utils.py
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
# Copyright (c) 2016 David Hoese
4
# Author(s):
5
#   David Hoese <david.hoese@ssec.wisc.edu>
6
#
7
# This program is free software: you can redistribute it and/or modify it under
8
# the terms of the GNU Lesser General Public License as published by the Free
9
# Software Foundation, either version 3 of the License, or (at your option) any
10
# later version.
11
#
12
# This program is distributed in the hope that it will be useful, but WITHOUT
13
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
15
# details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License along
18
# with this program.  If not, see <http://www.gnu.org/licenses/>.
19
"""Utilities for testing.
4✔
20

21
This mostly takes from astropy's method for checking collected_warnings during
22
tests.
23
"""
24
import sys
4✔
25
import types
4✔
26
import warnings
4✔
27
from contextlib import contextmanager
4✔
28

29
import numpy as np
4✔
30

31
try:
4✔
32
    from pyproj import CRS
4✔
33
except ImportError:
×
34
    CRS = None
×
35

36
_deprecations_as_exceptions = False
4✔
37
_include_astropy_deprecations = False
4✔
38
AstropyDeprecationWarning = None
4✔
39
AstropyPendingDeprecationWarning = None
4✔
40

41

42
def treat_deprecations_as_exceptions():
4✔
43
    """Turn all DeprecationWarnings into exceptions.
44

45
    Deprecation warnings indicate deprecated uses of Python itself or Numpy.
46
    This completely resets the warning filters and any "already seen"
47
    warning state.
48
    """
49
    # First, totally reset the warning state
50
    for module in sys.modules.values():
4✔
51
        # We don't want to deal with six.MovedModules, only "real"
52
        # modules.
53
        if (isinstance(module, types.ModuleType) and
4✔
54
                hasattr(module, '__warningregistry__')):
55
            del module.__warningregistry__
4✔
56

57
    if not _deprecations_as_exceptions:
4✔
58
        return
4✔
59

60
    warnings.resetwarnings()
×
61

62
    # Hide the next couple of DeprecationWarnings
63
    warnings.simplefilter('ignore', DeprecationWarning)
×
64
    # Here's the wrinkle: a couple of our third-party dependencies
65
    # (py.test and scipy) are still using deprecated features
66
    # themselves, and we'd like to ignore those.  Fortunately, those
67
    # show up only at import time, so if we import those things *now*,
68
    # before we turn the warnings into exceptions, we're golden.
69
    try:
×
70
        # A deprecated stdlib module used by py.test
71
        import compiler  # noqa
×
72
    except ImportError:
×
73
        pass
×
74

75
    try:
×
76
        import scipy  # noqa
×
77
    except ImportError:
×
78
        pass
×
79

80
    # Now, start over again with the warning filters
81
    warnings.resetwarnings()
×
82
    # Now, turn DeprecationWarnings into exceptions
83
    warnings.filterwarnings("error", ".*", DeprecationWarning)
×
84

85
    # Only turn astropy deprecation warnings into exceptions if requested
86
    if _include_astropy_deprecations:
×
87
        warnings.filterwarnings("error", ".*", AstropyDeprecationWarning)
×
88
        warnings.filterwarnings("error", ".*", AstropyPendingDeprecationWarning)
×
89

90
    # py.test reads files with the 'U' flag, which is now
91
    # deprecated in Python 3.4.
92
    warnings.filterwarnings(
×
93
        "ignore",
94
        r"'U' mode is deprecated",
95
        DeprecationWarning)
96

97
    # BeautifulSoup4 triggers a DeprecationWarning in stdlib's
98
    # html module.x
99
    warnings.filterwarnings(
×
100
        "ignore",
101
        r"The strict argument and mode are deprecated\.",
102
        DeprecationWarning)
103
    warnings.filterwarnings(
×
104
        "ignore",
105
        r"The value of convert_charrefs will become True in 3\.5\. "
106
        r"You are encouraged to set the value explicitly\.",
107
        DeprecationWarning)
108
    # Filter out pyresample's deprecation warnings.
109
    warnings.filterwarnings(
×
110
        "ignore",
111
        r"This module will be removed in pyresample 2\.0\, please use the"
112
        r"\`pyresample.spherical\` module functions and class instead\.",
113
        DeprecationWarning)
114

115
    if sys.version_info[:2] >= (3, 5):
×
116
        # py.test raises this warning on Python 3.5.
117
        # This can be removed when fixed in py.test.
118
        # See https://github.com/pytest-dev/pytest/pull/1009
119
        warnings.filterwarnings(
×
120
            "ignore",
121
            r"inspect\.getargspec\(\) is deprecated, use "
122
            r"inspect\.signature\(\) instead",
123
            DeprecationWarning)
124

125

126
class catch_warnings(warnings.catch_warnings):
4✔
127
    """A high-powered version of warnings.catch_warnings to use for testing.
128

129
    Makes sure that there is no dependence on the order in which the tests
130
    are run.
131

132
    This completely blitzes any memory of any warnings that have
133
    appeared before so that all warnings will be caught and displayed.
134

135
    ``*args`` is a set of warning classes to collect.  If no arguments are
136
    provided, all warnings are collected.
137

138
    Use as follows::
139

140
        with catch_warnings(MyCustomWarning) as w:
141
            do.something.bad()
142
        assert len(w) > 0
143
    """
144

145
    def __init__(self, *classes):
4✔
146
        """Initialize the classes of warnings to catch."""
147
        super(catch_warnings, self).__init__(record=True)
4✔
148
        self.classes = classes
4✔
149

150
    def __enter__(self):
4✔
151
        """Catch any warnings during this context."""
152
        warning_list = super(catch_warnings, self).__enter__()
4✔
153
        treat_deprecations_as_exceptions()
4✔
154
        if len(self.classes) == 0:
4✔
155
            warnings.simplefilter('always')
4✔
156
        else:
157
            warnings.simplefilter('ignore')
4✔
158
            for cls in self.classes:
4✔
159
                warnings.simplefilter('always', cls)
4✔
160
        return warning_list
4✔
161

162
    def __exit__(self, type, value, traceback):
4✔
163
        """Raise any warnings as errors."""
164
        treat_deprecations_as_exceptions()
4✔
165

166

167
def create_test_longitude(start, stop, shape, twist_factor=0.0, dtype=np.float32):
4✔
168
    """Get basic sample of longitude data."""
169
    if start > 0 > stop:
4✔
170
        # cross anti-meridian
171
        stop += 360.0
4✔
172

173
    num_cols = 1 if len(shape) < 2 else shape[1]
4✔
174
    lon_row = np.linspace(start, stop, num=num_cols).astype(dtype)
4✔
175
    twist_array = np.arange(shape[0]).reshape((shape[0], 1)) * twist_factor
4✔
176
    lon_array = np.repeat([lon_row], shape[0], axis=0)
4✔
177
    lon_array += twist_array
4✔
178

179
    if stop > 360.0:
4✔
180
        lon_array[lon_array > 360.0] -= 360
×
181
    return lon_array
4✔
182

183

184
def create_test_latitude(start, stop, shape, twist_factor=0.0, dtype=np.float32):
4✔
185
    """Get basic sample of latitude data."""
186
    num_cols = 1 if len(shape) < 2 else shape[1]
4✔
187
    lat_col = np.linspace(start, stop, num=shape[0]).astype(dtype).reshape((shape[0], 1))
4✔
188
    twist_array = np.arange(num_cols) * twist_factor
4✔
189
    lat_array = np.repeat(lat_col, num_cols, axis=1)
4✔
190
    lat_array += twist_array
4✔
191
    return lat_array
4✔
192

193

194
class CustomScheduler(object):
4✔
195
    """Scheduler raising an exception if data are computed too many times."""
196

197
    def __init__(self, max_computes=1):
4✔
198
        """Set starting and maximum compute counts."""
199
        self.max_computes = max_computes
4✔
200
        self.total_computes = 0
4✔
201

202
    def __call__(self, dsk, keys, **kwargs):
4✔
203
        """Compute dask task and keep track of number of times we do so."""
204
        import dask
4✔
205
        self.total_computes += 1
4✔
206
        if self.total_computes > self.max_computes:
4✔
207
            raise RuntimeError("Too many dask computations were scheduled: "
×
208
                               "{}".format(self.total_computes))
209
        return dask.get(dsk, keys, **kwargs)
4✔
210

211

212
@contextmanager
4✔
213
def assert_maximum_dask_computes(max_computes=1):
4✔
214
    """Context manager to make sure dask computations are not executed more than ``max_computes`` times."""
215
    import dask
4✔
216
    with dask.config.set(scheduler=CustomScheduler(max_computes=max_computes)) as new_config:
4✔
217
        yield new_config
4✔
218

219

220
def friendly_crs_equal(expected, actual, keys=None, use_obj=True, use_wkt=True):
4✔
221
    """Test if two projection definitions are equal.
222

223
    The main purpose of this function is to help manage differences
224
    between pyproj versions. Depending on the version installed and used
225
    pyresample may provide a different `proj_dict` or other similar
226
    CRS definition.
227

228
    Args:
229
        expected (dict, str, pyproj.crs.CRS): Expected CRS definition as
230
            a PROJ dictionary or string or CRS object.
231
        actual (dict, str, pyproj.crs.CRS): Actual CRS definition
232
        keys (list): Specific PROJ parameters to look for. Only takes effect
233
            if `use_obj` is `False`.
234
        use_obj (bool): Use pyproj's CRS object to test equivalence. Default
235
            is True.
236
        use_wkt (bool): Increase likely hood of making CRS objects equal by
237
            converting WellKnownText before converting to the final CRS
238
            object. Requires `use_obj`. Defaults to True.
239

240
    """
241
    if CRS is not None and use_obj:
4✔
242
        if hasattr(expected, 'crs'):
4✔
243
            expected = expected.crs
×
244
        if hasattr(actual, 'crs'):
4✔
245
            actual = actual.crs
4✔
246
        expected_crs = CRS.from_user_input(expected)
4✔
247
        actual_crs = CRS.from_user_input(actual)
4✔
248
        if use_wkt:
4✔
249
            expected_crs = CRS(expected_crs.to_wkt())
4✔
250
            actual_crs = CRS(actual_crs.to_wkt())
4✔
251
        assert expected_crs == actual_crs
4✔
252
        return
4✔
253
    raise NotImplementedError("""TODO""")
×
254

255

256
def assert_warnings_contain(collected_warnings: list, message: str, count: int = 1):
4✔
257
    """Check that collected warnings with catch_warnings contain certain messages.
258

259
    Args:
260
        collected_warnings:
261
            List of warnings collected using
262
            ``warnings.catch_warnings(record=True)``.
263
        message:
264
            Lowercase string to check is in one or more of the warning
265
            messages.
266
        count:
267
            Number of warnings that should contain the provided message
268
            string.
269

270
    Examples:
271
        Use during test code with::
272

273
            with warnings.catch_warnings(record=True) as w:
274
                # test code
275
            assert_warnings_contain(w, "invalid data", 1)
276

277
    """
278
    msgs = [msg.message.args[0].lower() for msg in collected_warnings]
4✔
279
    msgs_with = [msg for msg in msgs if message in msg]
4✔
280
    assert len(msgs_with) == count
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc