(
cls, tree: ast.Module, lines: list[str]
)
| 139 | |
| 140 | @classmethod |
| 141 | def iter_patch_entries( |
| 142 | cls, tree: ast.Module, lines: list[str] |
| 143 | ) -> "Iterator[typing.Self]": |
| 144 | import re |
| 145 | import sys |
| 146 | |
| 147 | for cls_node, fn_node in iter_tests(tree): |
| 148 | parent_class = cls_node.name |
| 149 | for dec_node in fn_node.decorator_list: |
| 150 | if not isinstance(dec_node, (ast.Attribute, ast.Call)): |
| 151 | continue |
| 152 | |
| 153 | attr_node = ( |
| 154 | dec_node if isinstance(dec_node, ast.Attribute) else dec_node.func |
| 155 | ) |
| 156 | |
| 157 | if ( |
| 158 | isinstance(attr_node, ast.Name) |
| 159 | or getattr(attr_node.value, "id", None) != UT |
| 160 | ): |
| 161 | continue |
| 162 | |
| 163 | cond = None |
| 164 | try: |
| 165 | ut_method = UtMethod(attr_node.attr) |
| 166 | except ValueError: |
| 167 | continue |
| 168 | |
| 169 | # If our ut_method has args then, |
| 170 | # we need to search for a constant that contains our `COMMENT`. |
| 171 | # Otherwise we need to search it in the raw source code :/ |
| 172 | if ut_method.has_args(): |
| 173 | reason = next( |
| 174 | ( |
| 175 | node.value |
| 176 | for node in ast.walk(dec_node) |
| 177 | if isinstance(node, ast.Constant) |
| 178 | and isinstance(node.value, str) |
| 179 | and COMMENT in node.value |
| 180 | ), |
| 181 | None, |
| 182 | ) |
| 183 | |
| 184 | # If we didn't find a constant containing <COMMENT>, |
| 185 | # then we didn't put this decorator |
| 186 | if not reason: |
| 187 | continue |
| 188 | |
| 189 | if ut_method.has_cond(): |
| 190 | cond = ast.unparse(dec_node.args[0]) |
| 191 | else: |
| 192 | # Search first on decorator line, then in the line before |
| 193 | for line in lines[dec_node.lineno - 1 : dec_node.lineno - 3 : -1]: |
| 194 | if found := re.search(rf"{COMMENT}.?(.*)", line): |
| 195 | reason = found.group() |
| 196 | break |
| 197 | else: |
| 198 | # Didn't find our `COMMENT` :) |
no test coverage detected