Coverage for patterns / python.py: 86%

42 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-08 15:04 -0800

1"""Pattern detection for Python source files.""" 

2 

3from __future__ import annotations 

4 

5from models import PatternViolation 

6from patterns.common import create_query, run_captures 

7from patterns.shared import check_patterns as check_patterns_shared 

8 

9_PY_NESTED_IMPORT_QUERY = """ 

10(block (import_statement) @nested_import) 

11(block (import_from_statement) @nested_import) 

12""" 

13 

14_PY_WILDCARD_IMPORT_QUERY = "(import_from_statement (wildcard_import) @wildcard)" 

15 

16 

17def _is_type_checking_import(node) -> bool: 

18 """Check if import is inside a 'if TYPE_CHECKING:' block.""" 

19 parent = node.parent 

20 while parent: 

21 if parent.type == "if_statement": 

22 for child in parent.children: 

23 if child.type in ("identifier", "expression_statement"): 

24 text = ( 

25 child.text 

26 if child.type == "identifier" 

27 else child.text.decode() 

28 if child.text 

29 else "" 

30 ) 

31 if text == b"TYPE_CHECKING" or "TYPE_CHECKING" in str(text): 

32 return True 

33 if parent.type == "module": 

34 break 

35 parent = parent.parent 

36 return False 

37 

38 

39def check_nested_imports(root) -> list[PatternViolation]: 

40 """Detect import statements not at module level (inside functions/classes).""" 

41 from parsers.python import PY_LANGUAGE 

42 

43 query = create_query(PY_LANGUAGE, _PY_NESTED_IMPORT_QUERY) 

44 captures = run_captures(query, root) 

45 

46 violations = [] 

47 for node, capture_name in captures: 

48 if _is_type_checking_import(node): 

49 continue 

50 line = node.start_point[0] + 1 

51 violations.append( 

52 PatternViolation( 

53 type="nested_import", 

54 line=line, 

55 description="Import statement inside function/class body instead of module level", 

56 severity="warning", 

57 ) 

58 ) 

59 

60 return violations 

61 

62 

63def check_wildcard_imports(root) -> list[PatternViolation]: 

64 """Detect wildcard imports like 'from module import *'.""" 

65 from parsers.python import PY_LANGUAGE 

66 

67 query = create_query(PY_LANGUAGE, _PY_WILDCARD_IMPORT_QUERY) 

68 captures = run_captures(query, root) 

69 

70 violations = [] 

71 for node, capture_name in captures: 

72 if capture_name == "wildcard": 

73 line = node.start_point[0] + 1 

74 violations.append( 

75 PatternViolation( 

76 type="wildcard_import", 

77 line=line, 

78 description="Wildcard import 'from module import *' - explicit imports preferred", 

79 severity="warning", 

80 ) 

81 ) 

82 

83 return violations 

84 

85 

86def check_patterns(root, patterns: list[str] | None = None) -> list[PatternViolation]: 

87 """Run pattern checks for Python files.""" 

88 return check_patterns_shared("python", root, patterns)