|
8 | 8 | # importing anything else |
9 | 9 | import iris.tests as tests # isort:skip |
10 | 10 |
|
| 11 | +import ast |
11 | 12 | from datetime import datetime |
12 | 13 | from fnmatch import fnmatch |
13 | 14 | from glob import glob |
14 | 15 | import os |
15 | 16 | from pathlib import Path |
16 | | -import re |
17 | 17 | import subprocess |
18 | | -from typing import List, NamedTuple, Tuple |
| 18 | +from typing import List, Tuple |
19 | 19 |
|
20 | 20 | import iris |
21 | 21 | from iris.fileformats.netcdf import _thread_safe_nc |
@@ -145,40 +145,45 @@ def test_categorised_warnings(): |
145 | 145 | Use :func:`~iris.exceptions.warning_combo` . |
146 | 146 |
|
147 | 147 | """ |
148 | | - warn_regex = re.compile(r"(?s)warn\(.*?\)") |
149 | | - |
150 | | - class ResultTuple(NamedTuple): |
151 | | - file_path: Path |
152 | | - line_number: int |
153 | | - match_string: str |
154 | | - |
155 | | - warns_without_category: list[ResultTuple] = [] |
156 | | - warns_with_user_warning: list[ResultTuple] = [] |
| 148 | + warns_without_category = [] |
| 149 | + warns_with_user_warning = [] |
| 150 | + tmp_list = [] |
157 | 151 |
|
158 | 152 | for file_path in Path(IRIS_DIR).rglob("*.py"): |
159 | 153 | file_text = file_path.read_text() |
| 154 | + parsed = ast.parse(source=file_text) |
| 155 | + calls = filter(lambda node: hasattr(node, "func"), ast.walk(parsed)) |
| 156 | + warn_calls = filter( |
| 157 | + lambda c: getattr(c.func, "attr", None) == "warn", calls |
| 158 | + ) |
160 | 159 |
|
161 | | - matched = warn_regex.search(file_text) |
162 | | - while matched: |
163 | | - start, end = matched.span() |
164 | | - result_tuple = ResultTuple( |
165 | | - file_path=file_path, |
166 | | - line_number=file_text[:start].count("\n") + 1, |
167 | | - match_string=file_text[start:end], |
168 | | - ) |
169 | | - if not re.search(r"category=", result_tuple.match_string): |
170 | | - warns_without_category.append(result_tuple) |
171 | | - if re.search(r"\bUserWarning\b", result_tuple.match_string): |
172 | | - warns_with_user_warning.append(result_tuple) |
173 | | - |
174 | | - matched = warn_regex.search(file_text, start + 1) |
175 | | - |
176 | | - # All warnings raised by Iris must be raised with a category kwarg. |
177 | | - # (This avoids UserWarnings being raised by unwritten default behaviour). |
178 | | - assert warns_without_category == [] |
| 160 | + warn_call: ast.Call |
| 161 | + for warn_call in warn_calls: |
| 162 | + warn_ref = f"{file_path}:{warn_call.lineno}" |
| 163 | + tmp_list.append(warn_ref) |
179 | 164 |
|
180 | | - # No warnings raised by Iris can be the base UserWarning class. |
181 | | - assert warns_with_user_warning == [] |
| 165 | + category_kwargs = filter( |
| 166 | + lambda k: k.arg == "category", warn_call.keywords |
| 167 | + ) |
| 168 | + category_kwarg: ast.keyword = next(category_kwargs, None) |
| 169 | + |
| 170 | + if category_kwarg is None: |
| 171 | + warns_without_category.append(warn_ref) |
| 172 | + # Work with Attribute or Name instances. |
| 173 | + elif ( |
| 174 | + getattr(category_kwarg.value, "attr", None) |
| 175 | + or getattr(category_kwarg.value, "id", None) |
| 176 | + ) == "UserWarning": |
| 177 | + warns_with_user_warning.append(warn_ref) |
| 178 | + |
| 179 | + # This avoids UserWarnings being raised by unwritten default behaviour. |
| 180 | + assert ( |
| 181 | + warns_without_category == [] |
| 182 | + ), "All warnings raised by Iris must be raised with the category kwarg." |
| 183 | + |
| 184 | + assert ( |
| 185 | + warns_with_user_warning == [] |
| 186 | + ), "No warnings raised by Iris can be the base UserWarning class." |
182 | 187 |
|
183 | 188 |
|
184 | 189 | class TestLicenseHeaders(tests.IrisTest): |
|
0 commit comments