|
55 | 55 | _AccessNodes = Union[nodes.Attribute, nodes.AssignAttr]
|
56 | 56 |
|
57 | 57 | INVALID_BASE_CLASSES = {"bool", "range", "slice", "memoryview"}
|
| 58 | +ALLOWED_PROPERTIES = {"bultins.property", "functools.cached_property"} |
58 | 59 | BUILTIN_DECORATORS = {"builtins.property", "builtins.classmethod"}
|
59 | 60 | ASTROID_TYPE_COMPARATORS = {
|
60 | 61 | nodes.Const: lambda a, b: a.value == b.value,
|
@@ -1251,11 +1252,15 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None:
|
1251 | 1252 | # attribute affectation will call this method, not hiding it
|
1252 | 1253 | return
|
1253 | 1254 | if isinstance(decorator, nodes.Name):
|
1254 |
| - if decorator.name == "property": |
| 1255 | + if decorator.name in ALLOWED_PROPERTIES: |
1255 | 1256 | # attribute affectation will either call a setter or raise
|
1256 | 1257 | # an attribute error, anyway not hiding the function
|
1257 | 1258 | return
|
1258 | 1259 |
|
| 1260 | + if isinstance(decorator, nodes.Attribute): |
| 1261 | + if self._check_functools_or_not(decorator): |
| 1262 | + return |
| 1263 | + |
1259 | 1264 | # Infer the decorator and see if it returns something useful
|
1260 | 1265 | inferred = safe_infer(decorator)
|
1261 | 1266 | if not inferred:
|
@@ -1453,6 +1458,24 @@ def _check_invalid_overridden_method(
|
1453 | 1458 | node=function_node,
|
1454 | 1459 | )
|
1455 | 1460 |
|
| 1461 | + def _check_functools_or_not(self, decorator: nodes.Attribute) -> bool: |
| 1462 | + if decorator.attrname != "cached_property": |
| 1463 | + return False |
| 1464 | + |
| 1465 | + if not isinstance(decorator.expr, nodes.Name): |
| 1466 | + return False |
| 1467 | + |
| 1468 | + _, import_nodes = decorator.expr.lookup(decorator.expr.name) |
| 1469 | + |
| 1470 | + if not import_nodes: |
| 1471 | + return False |
| 1472 | + import_node = import_nodes[0] |
| 1473 | + |
| 1474 | + if not isinstance(import_node, (astroid.Import, astroid.ImportFrom)): |
| 1475 | + return False |
| 1476 | + |
| 1477 | + return "functools" in dict(import_node.names) |
| 1478 | + |
1456 | 1479 | def _check_slots(self, node: nodes.ClassDef) -> None:
|
1457 | 1480 | if "__slots__" not in node.locals:
|
1458 | 1481 | return
|
|
0 commit comments