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

pysat / pysat / 13036165242

29 Jan 2025 04:44PM UTC coverage: 97.404%. Remained the same
13036165242

push

github

web-flow
Merge pull request #1211 from pysat/control_distribution_statement

LGL: Control distribution statement

11780 of 12094 relevant lines covered (97.4%)

7.73 hits per line

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

98.55
pysat/utils/testing.py
1
#!/usr/bin/env python
2
# Full license can be found in License.md
3
# Full author list can be found in .zenodo.json file
4
# DOI:10.5281/zenodo.1199703
5
#
6
# Review Status for Classified or Controlled Information by NRL
7
# -------------------------------------------------------------
8
# DISTRIBUTION STATEMENT A: Approved for public release. Distribution is
9
# unlimited.
10
# ----------------------------------------------------------------------------
11
"""Utilities to perform common evaluations."""
1✔
12

13
import numpy as np
8✔
14
import pysat.utils
8✔
15

16

17
def assert_list_contains(small_list, big_list, test_nan=False, test_case=True):
8✔
18
    """Assert all elements of one list exist within the other list.
19

20
    Parameters
21
    ----------
22
    small_list : list
23
        List whose values must all be present within big_list
24
    big_list : list
25
        List that must contain all the values in small_list
26
    test_nan : bool
27
        Test the lists for the presence of NaN values
28
    test_case : bool
29
        Requires strings to be the same case when testing
30

31
    Raises
32
    ------
33
    AssertionError
34
        If a small_list value is missing from big_list
35

36
    """
37
    if test_nan:
8✔
38
        big_num_nan = np.isnan(big_list).sum()
8✔
39
        small_num_nan = 0
8✔
40
    elif not test_case:
8✔
41
        big_lower = [value.lower() for value in big_list]
8✔
42

43
    # Test the presence of non-NaN values from `small_list` in `big_list` and
44
    # determine the number of NaN values in `small_list`
45
    for value in small_list:
8✔
46
        if test_nan and np.isnan(value):
8✔
47
            small_num_nan += 1
8✔
48
        elif test_case:
8✔
49
            assert value in big_list, "{:} not in {:}".format(value.__repr__(),
8✔
50
                                                              big_list)
51
        else:
52
            assert value.lower() in big_lower, "{:} not in {:}".format(
8✔
53
                value.lower(), big_lower)
54

55
    if test_nan:
8✔
56
        # Ensure `small_list` does not have more NaNs than `big_list`
57
        assert small_num_nan <= big_num_nan
8✔
58
    return
8✔
59

60

61
def assert_lists_equal(list1, list2, test_nan=False, test_case=True):
8✔
62
    """Assert that the lists contain the same elements.
63

64
    Parameters
65
    ----------
66
    list1 : list
67
        Input list one
68
    list2 : list
69
        Input list two
70
    test_nan : bool
71
        Test the lists for the presence of NaN values
72
    test_case : bool
73
        Requires strings to be the same case when testing
74

75
    Raises
76
    ------
77
    AssertionError
78
        If a list1 value is missing from list2 or list lengths are unequal
79

80
    Note
81
    ----
82
    This test does not require that the lists have the same elements in the
83
    same order, and so is also a good test for keys.
84

85
    """
86

87
    assert len(list1) == len(list2)
8✔
88
    assert_list_contains(list1, list2, test_nan=test_nan, test_case=test_case)
8✔
89

90
    return
8✔
91

92

93
def assert_hasattr(obj, attr_name):
8✔
94
    """Provide useful info if object is missing a required attribute.
95

96
    Parameters
97
    ----------
98
    obj : object
99
        Name of object to check
100
    attr_name : str
101
        Name of required attribute that must be present in `obj`
102

103
    Raises
104
    ------
105
    AssertionError
106
        If `obj` does not have attribute `attr_name`
107

108
    """
109

110
    estr = "Object {:} missing attribute {:}".format(obj.__repr__(),
8✔
111
                                                     attr_name)
112
    assert hasattr(obj, attr_name), estr
8✔
113
    return
8✔
114

115

116
def assert_isinstance(obj, obj_type):
8✔
117
    """Provide useful info if object is the wrong type.
118

119
    Parameters
120
    ----------
121
    obj : object
122
        Name of object to check
123
    obj_type : str
124
        Required type of object
125

126
    Raises
127
    ------
128
    AssertionError
129
        If `obj` is not type `obj_type`
130

131
    """
132

133
    estr = "Object {:} is type {:}, but should be type {:}".format(
8✔
134
        obj.__repr__(), type(obj), obj_type)
135
    assert isinstance(obj, obj_type), estr
8✔
136
    return
8✔
137

138

139
def nan_equal(value1, value2):
8✔
140
    """Determine if values are equal or are both NaN.
141

142
    Parameters
143
    ----------
144
    value1 : scalar-like
145
        Value of any type that can be compared without iterating
146
    value2 : scalar-like
147
        Another value of any type that can be compared without iterating
148

149
    Returns
150
    -------
151
    is_equal : bool
152
        True if both values are equal or NaN, False if they are not
153

154
    """
155

156
    is_equal = (value1 == value2)
8✔
157

158
    if not is_equal:
8✔
159
        try:
8✔
160
            if np.isnan(value1) and np.isnan(value2):
8✔
161
                is_equal = True
8✔
162
        except TypeError:
8✔
163
            # One or both of value1 and value2 cannot be evaluated by np.isnan
164
            # and so have been correctly identified as unequal
165
            pass
8✔
166

167
    return is_equal
8✔
168

169

170
def eval_warnings(warns, check_msgs, warn_type=DeprecationWarning):
8✔
171
    """Evaluate warnings by category and message.
172

173
    Parameters
174
    ----------
175
    warns : list
176
        List of warnings.WarningMessage objects
177
    check_msgs : list
178
        List of strings containing the expected warning messages
179
    warn_type : type or list-like
180
        Type or list-like for the warning messages (default=DeprecationWarning)
181

182
    Raises
183
    ------
184
    AssertionError
185
        If warning category doesn't match type or an expected message is missing
186

187
    """
188

189
    # Ensure inputs are list-like
190
    warn_types = pysat.utils.listify(warn_type)
8✔
191
    check_msgs = pysat.utils.listify(check_msgs)
8✔
192

193
    # Initialize the output
194
    found_msgs = [False for msg in check_msgs]
8✔
195

196
    # If only one warning type provided then expand to match
197
    # number of messages
198
    simple_out = False
8✔
199
    if len(warn_types) == 1:
8✔
200
        warn_types = warn_types * len(check_msgs)
8✔
201
        simple_out = True
8✔
202

203
    # Test the warning messages, ensuring each attribute is present
204
    for iwar in warns:
8✔
205
        for i, (msg, iwartype) in enumerate(zip(check_msgs, warn_types)):
8✔
206
            if str(iwar.message).find(msg) >= 0:
8✔
207
                assert iwar.category == iwartype, \
8✔
208
                    "bad warning type for message: {:}".format(msg)
209
                found_msgs[i] = True
8✔
210

211
    # If all warnings are of the same kind, we don't need to repeat the
212
    # same type in the output string.
213
    if simple_out:
8✔
214
        warn_repr_str = repr(warn_type)
8✔
215
    else:
216
        not_found_msgs = [not msg for msg in found_msgs]
8✔
217
        warn_repr_str = repr((np.array(warn_types)[not_found_msgs]))
8✔
218

219
    assert np.all(found_msgs), "did not find {:d} expected {:}".format(
8✔
220
        len(found_msgs) - np.sum(found_msgs), warn_repr_str)
221

222
    return
8✔
223

224

225
def eval_bad_input(func, error, err_msg, input_args=None, input_kwargs=None):
8✔
226
    """Evaluate bad function or method input.
227

228
    Parameters
229
    ----------
230
    func : function, method, or class
231
        Function, class, or method to be evaluated
232
    error : class
233
        Expected error or exception
234
    err_msg : str
235
        Expected error message
236
    input_args : list or NoneType
237
        Input arguments or None for no input arguments (default=None)
238
    input_kwargs : dict or NoneType
239
        Input keyword arguments or None for no input kwargs (default=None)
240

241
    Raises
242
    ------
243
    AssertionError
244
        If unexpected error message is returned
245
    Exception
246
        If error or exception of unexpected type is returned, it is raised
247

248
    """
249

250
    # Ensure there are appropriate input lists and dicts
251
    if input_args is None:
8✔
252
        input_args = []
8✔
253

254
    if input_kwargs is None:
8✔
255
        input_kwargs = {}
8✔
256

257
    # Call the function, catching only the expected error type
258
    try:
8✔
259
        func(*input_args, **input_kwargs)
8✔
260
        raise AssertionError('no {:} raised'.format(repr(error)))
×
261
    except error as err:
8✔
262
        # Evaluate the error message
263
        assert str(err).find(err_msg) >= 0, \
8✔
264
            "unexpected error message ('{:}' not in '{:}')".format(err_msg,
265
                                                                   str(err))
266

267
    return
8✔
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

© 2026 Coveralls, Inc