Pattern Heuristics & Fixes (alnoms.patterns & alnoms.fixes)
The internal diagnostic rule engine that identifies computational bottlenecks and their corresponding deterministic cures.
🔍 Detectors
Orchestrates AST analysis using the registered pattern detectors.
The engine loads all OSS detectors by default and conditionally extends the registry with PRO and ENTERPRISE detectors based on environment feature flags. Each detector is responsible for identifying a specific algorithmic anti‑pattern.
This class exposes a single static method, analyze_code, which parses
the file into an AST and dispatches it to all registered detectors.
Source code in src/alnoms/patterns/heuristics.py
analyze_code(path)
staticmethod
Analyze a Python file using all registered pattern detectors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Path to the Python source file. |
required |
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
List[Dict[str, Any]]: A flat list of findings from all detectors. Each finding is a dictionary containing detector‑specific metadata such as: - "function": Function where the issue occurs - "pattern_id": Identifier of the detector - "issue": Description of the anti‑pattern - "line": Line number of the issue - Additional detector‑specific fields |
Notes
- Empty files return an empty list.
- Syntax errors or unexpected exceptions are captured and returned as a single file‑level finding rather than raising an exception.
Source code in src/alnoms/patterns/heuristics.py
Bases: PatternDetector
Detects nested loops and classifies their algorithmic intent.
This detector identifies functions containing nested for or while
loops and applies lightweight heuristics to infer the likely purpose
of the nested structure. The intent classification enables downstream
fixers to provide context‑aware remediation strategies.
Supported intent categories
• membership — equality checks, membership tests, pairwise scans • sorting — range(len(...)) patterns resembling selection/bubble sort • dfs — nested iteration over adjacency lists or graph structures • generic — fallback for unclassified quadratic scans
Source code in src/alnoms/patterns/nested_loops.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | |
detect(tree)
Detects nested loops within function bodies.
Traverses the AST to locate functions containing nested for or
while loops. When a nested loop is found, the detector assigns
an intent classification and records a single finding per function
to avoid noise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree
|
AST
|
The parsed AST of the module being analyzed. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
List[Dict[str, Any]]
|
List[Dict[str, Any]]: A list of findings, where each finding |
|
includes |
List[Dict[str, Any]]
|
|
Source code in src/alnoms/patterns/nested_loops.py
Bases: PatternDetector
Detect inefficient membership tests inside loops.
This detector flags occurrences of:
- `x in some_list`
- `x not in some_list`
- membership checks on literal lists/tuples
- membership checks on variables that are likely lists
When these appear inside loops, they create a hidden O(N²) pattern due to
repeated linear scans. The detector uses heuristics to avoid false positives
on safe containers such as sets, dicts, ranges, and variables whose names
imply O(1) lookup semantics (e.g., visited, cache, lookup).
Attributes:
| Name | Type | Description |
|---|---|---|
SAFE_CONTAINER_HINTS |
set[str]
|
Variable‑name substrings that imply O(1) membership semantics. |
Source code in src/alnoms/patterns/inefficient_membership.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
detect(tree)
Analyze the AST and flag inefficient membership tests inside loops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree
|
AST
|
Parsed AST of the target Python file. |
required |
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
List[Dict[str, Any]]: A list of findings. Each finding includes: - "function": Function where the issue occurs - "pattern_id": Identifier for this detector - "issue": Description of the membership test - "complexity": Estimated complexity impact - "suggestion": Recommended remediation - "line": Line number of the issue |
Notes
- Literal lists/tuples of size ≤ 3 are ignored as they are cheap.
- Safe containers (set/dict/range) are skipped.
- Variable names are heuristically classified for safety.
Source code in src/alnoms/patterns/inefficient_membership.py
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
Bases: PatternDetector
Detect potentially expensive function calls inside loops.
This detector walks the AST of each function and inspects all for and
while loops. Any function call inside a loop that is not part of the
SAFE_CALLS whitelist is flagged as a potential performance risk.
The whitelist includes:
- Common O(1) built‑ins (
len,range,int, etc.) - Common O(1) container methods (
append,pop,get, etc.) - Sorting and I/O calls, which are handled by dedicated detectors
Attributes:
| Name | Type | Description |
|---|---|---|
SAFE_CALLS |
set[str]
|
Set of known O(1) or trivial operations that should not be flagged. |
Source code in src/alnoms/patterns/expensive_calls.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | |
detect(tree)
Analyze the AST and flag non‑whitelisted calls inside loops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree
|
AST
|
Parsed AST of the target Python file. |
required |
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
List[Dict[str, Any]]: A list of findings. Each finding includes: - "function": Name of the function containing the loop. - "pattern_id": Identifier for this detector. - "issue": Description of the detected call. - "complexity": Estimated complexity impact. - "suggestion": Recommended remediation. - "line": Line number of the call. |
Notes
- Only explicit
ast.Callnodes insidefor/whileloops are inspected. - Calls to safe O(1) operations are ignored.
- Sorting and I/O calls are delegated to other detectors.
Source code in src/alnoms/patterns/expensive_calls.py
Bases: PatternDetector
Detect high‑frequency I/O operations inside loops.
This detector walks the AST of each function and inspects all for and
while loops. Any call to open, read, or write inside a loop is
flagged as a performance risk due to:
- Excessive system calls
- Kernel context switching
- Disk or network latency
- Reduced throughput compared to buffered operations
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
Unique identifier for this detector. |
name |
str
|
Human‑readable name for reporting. |
description |
str
|
Short description of the detector's purpose. |
Source code in src/alnoms/patterns/high_freq_io.py
detect(tree)
Analyze the AST and flag I/O operations inside loops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree
|
AST
|
Parsed AST of the target Python file. |
required |
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
List[Dict[str, Any]]: A list of findings. Each finding includes: - "function": Name of the function containing the loop. - "pattern_id": Identifier for this detector. - "issue": Description of the detected I/O call. - "complexity": Qualitative complexity impact. - "suggestion": Recommended remediation. - "line": Line number of the call. |
Notes
- Only explicit
ast.Callnodes insidefor/whileloops are inspected. - This detector focuses on file and network I/O primitives.
- Buffered or batched I/O is recommended for performance.
Source code in src/alnoms/patterns/high_freq_io.py
Bases: PatternDetector
Detects in-place string/list concatenation inside loops.
This detector identifies patterns where += or *= are used on
potentially non-numeric variables inside for or while loops.
Such operations can trigger repeated memory reallocations, resulting
in O(N^2) scaling.
The detector includes heuristics to avoid false positives on numeric accumulation patterns (e.g., counters, totals, index updates).
Source code in src/alnoms/patterns/inplace_concat.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | |
detect(tree)
Detects in-place concatenation patterns inside loops.
Traverses the AST to find augmented assignments (+=, *=) that
occur within for or while loops. The detector filters out
numeric accumulation patterns and flags only those operations that
are likely to cause O(N^2) memory behavior due to repeated
reallocation of strings or lists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree
|
AST
|
The parsed AST of the target Python module or function. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
List[Dict[str, Any]]
|
List[Dict[str, Any]]: A list of findings, where each finding |
|
includes |
List[Dict[str, Any]]
|
|
Source code in src/alnoms/patterns/inplace_concat.py
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | |
Bases: PatternDetector
Detects repeated sorting operations executed inside loops.
This detector inspects loop bodies for calls to Python's built‑in
sorting mechanisms (sorted() or list .sort()). When these calls
appear inside for or while loops, they can introduce
O(N^2 log N) behavior due to repeated sorting of the same or similar
data structures.
Sorting is typically intended to be performed once before iteration, or once after data collection, rather than on every loop iteration.
Source code in src/alnoms/patterns/redundant_sort.py
detect(tree)
Detects sorting calls (sort, sorted) inside loop bodies.
Traverses the AST to locate function definitions and inspects all nested loops for calls to Python's standard sorting functions. When found, the detector records a finding indicating that sorting should be moved outside the loop to avoid repeated O(N log N) operations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree
|
AST
|
The parsed AST of the module being analyzed. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
List[Dict[str, Any]]
|
List[Dict[str, Any]]: A list of findings, where each finding |
|
includes |
List[Dict[str, Any]]
|
|
Source code in src/alnoms/patterns/redundant_sort.py
đź’Š Fixers
Bases: Fixer
Remediation strategy for nested loop patterns.
This fixer provides intent‑aware remediation for nested loops, distinguishing between cubic patterns (e.g., matrix multiplication) and quadratic patterns such as membership scans, manual sorting, and DFS‑like adjacency traversal. Recommendations include algorithmic redesign, vectorization, hashing, and use of efficient data structures.
Attributes:
| Name | Type | Description |
|---|---|---|
pattern_id |
str
|
Identifier for the associated detector pattern. |
Source code in src/alnoms/fixes/nested_loops_fixer.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | |
cost_estimate(finding, detected_complexity='Unknown')
Provides a qualitative estimate of the complexity improvement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
finding
|
Dict
|
The detector finding associated with the issue. |
required |
detected_complexity
|
str
|
The complexity classification. |
'Unknown'
|
Returns:
| Type | Description |
|---|---|
|
Dict[str, str]: A dictionary describing expected improvements |
|
|
in time and memory complexity for cubic and quadratic cases. |
Source code in src/alnoms/fixes/nested_loops_fixer.py
explain(finding, detected_complexity='Unknown')
Provides a human‑readable explanation of the nested loop issue.
This method generates intent‑aware explanations for nested loops, distinguishing between cubic and quadratic patterns. It also provides domain‑specific guidance for matrix multiplication, membership scans, sorting‑like routines, and DFS‑style traversal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
finding
|
Dict
|
The detector finding describing the nested loop. |
required |
detected_complexity
|
str
|
Static or empirical complexity. |
'Unknown'
|
Returns:
| Name | Type | Description |
|---|---|---|
str |
A narrative explanation describing the issue and the |
|
|
recommended remediation strategy. |
Source code in src/alnoms/fixes/nested_loops_fixer.py
snippet_before_after(finding, detected_complexity='Unknown')
Returns before/after code snippets illustrating the fix.
Snippets are intent‑aware and tailored to the specific nested loop pattern detected. Cubic patterns receive vectorization or pruning guidance, while quadratic patterns receive hashing, sorting, or DFS‑related improvements.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
finding
|
Dict
|
The detector finding describing the nested loop. |
required |
detected_complexity
|
str
|
Complexity classification. |
'Unknown'
|
Returns:
| Type | Description |
|---|---|
|
Dict[str, str]: A dictionary containing:
- |
Source code in src/alnoms/fixes/nested_loops_fixer.py
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | |
Bases: Fixer
Remediation strategy for inefficient membership tests inside loops.
This fixer addresses patterns where membership checks such as
x in list or x in tuple occur inside a loop. These operations
are O(N) per lookup, leading to accidental O(N*M) or O(N²) behavior.
The recommended remediation is to convert the container to a set
to achieve O(1) average‑case membership checks.
Attributes:
| Name | Type | Description |
|---|---|---|
pattern_id |
str
|
Identifier for the associated detector pattern. |
Source code in src/alnoms/fixes/inefficient_membership_fixer.py
cost_estimate(finding, detected_complexity='Unknown')
Provides a qualitative estimate of the complexity improvement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
finding
|
Dict
|
The detector finding associated with the issue. |
required |
detected_complexity
|
str
|
The complexity classification. |
'Unknown'
|
Returns:
| Type | Description |
|---|---|
|
Dict[str, str]: A dictionary describing expected improvements |
|
|
in time and memory complexity. Converting to a set reduces |
|
|
repeated O(N) scans to O(1) lookups. |
Source code in src/alnoms/fixes/inefficient_membership_fixer.py
explain(finding, detected_complexity='Unknown')
Provides a human‑readable explanation of the optimization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
finding
|
Dict
|
The detector finding describing the membership pattern. |
required |
detected_complexity
|
str
|
The static or empirical complexity associated with the anti‑pattern. |
'Unknown'
|
Returns:
| Name | Type | Description |
|---|---|---|
str |
A narrative explanation describing why list‑based membership |
|
|
checks inside loops are slow and how converting to a set improves |
||
|
performance. |
Source code in src/alnoms/fixes/inefficient_membership_fixer.py
snippet_before_after(finding, detected_complexity='Unknown')
Returns before/after code snippets illustrating the fix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
finding
|
Dict
|
The detector output for the inefficient membership test. |
required |
detected_complexity
|
str
|
Complexity classification used to contextualize the snippet. |
'Unknown'
|
Returns:
| Type | Description |
|---|---|
|
Dict[str, str]: A dictionary containing:
- |