thailint 0.8.0__py3-none-any.whl → 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- src/cli.py +115 -0
- src/core/base.py +4 -0
- src/core/rule_discovery.py +110 -84
- src/core/violation_builder.py +75 -15
- src/linter_config/loader.py +43 -11
- src/linters/dry/block_filter.py +4 -0
- src/linters/dry/block_grouper.py +4 -0
- src/linters/dry/cache_query.py +4 -0
- src/linters/dry/token_hasher.py +5 -1
- src/linters/dry/violation_builder.py +4 -0
- src/linters/dry/violation_filter.py +4 -0
- src/linters/file_header/bash_parser.py +4 -0
- src/linters/file_placement/directory_matcher.py +4 -0
- src/linters/file_placement/pattern_matcher.py +4 -0
- src/linters/file_placement/pattern_validator.py +4 -0
- src/linters/magic_numbers/context_analyzer.py +4 -0
- src/linters/magic_numbers/typescript_analyzer.py +4 -0
- src/linters/nesting/python_analyzer.py +4 -0
- src/linters/nesting/typescript_function_extractor.py +4 -0
- src/linters/print_statements/typescript_analyzer.py +4 -0
- src/linters/srp/class_analyzer.py +4 -0
- src/linters/srp/python_analyzer.py +55 -20
- src/linters/srp/typescript_metrics_calculator.py +83 -47
- src/linters/srp/violation_builder.py +4 -0
- src/linters/stateless_class/__init__.py +25 -0
- src/linters/stateless_class/config.py +58 -0
- src/linters/stateless_class/linter.py +355 -0
- src/linters/stateless_class/python_analyzer.py +299 -0
- {thailint-0.8.0.dist-info → thailint-0.9.0.dist-info}/METADATA +112 -2
- {thailint-0.8.0.dist-info → thailint-0.9.0.dist-info}/RECORD +33 -29
- {thailint-0.8.0.dist-info → thailint-0.9.0.dist-info}/WHEEL +0 -0
- {thailint-0.8.0.dist-info → thailint-0.9.0.dist-info}/entry_points.txt +0 -0
- {thailint-0.8.0.dist-info → thailint-0.9.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -23,6 +23,10 @@ import re
|
|
|
23
23
|
class PatternMatcher:
|
|
24
24
|
"""Handles regex pattern matching for file paths."""
|
|
25
25
|
|
|
26
|
+
def __init__(self) -> None:
|
|
27
|
+
"""Initialize the pattern matcher."""
|
|
28
|
+
pass # Stateless matcher for regex patterns
|
|
29
|
+
|
|
26
30
|
def match_deny_patterns(
|
|
27
31
|
self, path_str: str, deny_patterns: list[dict[str, str]]
|
|
28
32
|
) -> tuple[bool, str | None]:
|
|
@@ -24,6 +24,10 @@ from typing import Any
|
|
|
24
24
|
class PatternValidator:
|
|
25
25
|
"""Validates regex patterns in file placement configuration."""
|
|
26
26
|
|
|
27
|
+
def __init__(self) -> None:
|
|
28
|
+
"""Initialize the pattern validator."""
|
|
29
|
+
pass # Stateless validator for regex patterns
|
|
30
|
+
|
|
27
31
|
def validate_config(self, config: dict[str, Any]) -> None:
|
|
28
32
|
"""Validate all regex patterns in configuration.
|
|
29
33
|
|
|
@@ -30,6 +30,10 @@ from pathlib import Path
|
|
|
30
30
|
class ContextAnalyzer: # thailint: ignore[srp]
|
|
31
31
|
"""Analyzes contexts to determine if numeric literals are acceptable."""
|
|
32
32
|
|
|
33
|
+
def __init__(self) -> None:
|
|
34
|
+
"""Initialize the context analyzer."""
|
|
35
|
+
pass # Stateless analyzer for context checking
|
|
36
|
+
|
|
33
37
|
def is_acceptable_context(
|
|
34
38
|
self,
|
|
35
39
|
node: ast.Constant,
|
|
@@ -44,6 +44,10 @@ class TypeScriptMagicNumberAnalyzer(TypeScriptBaseAnalyzer): # thailint: ignore
|
|
|
44
44
|
of TypeScript magic number detection - all methods support this core purpose.
|
|
45
45
|
"""
|
|
46
46
|
|
|
47
|
+
def __init__(self) -> None: # pylint: disable=useless-parent-delegation
|
|
48
|
+
"""Initialize the TypeScript magic number analyzer."""
|
|
49
|
+
super().__init__() # Sets self.tree_sitter_available from base class
|
|
50
|
+
|
|
47
51
|
def find_numeric_literals(self, root_node: Node) -> list[tuple[Node, float | int, int]]:
|
|
48
52
|
"""Find all numeric literal nodes in TypeScript/JavaScript AST.
|
|
49
53
|
|
|
@@ -25,6 +25,10 @@ import ast
|
|
|
25
25
|
class PythonNestingAnalyzer:
|
|
26
26
|
"""Calculates maximum nesting depth in Python functions."""
|
|
27
27
|
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
"""Initialize the Python nesting analyzer."""
|
|
30
|
+
pass # Stateless analyzer for nesting depth calculation
|
|
31
|
+
|
|
28
32
|
def calculate_max_depth(
|
|
29
33
|
self, func_node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
30
34
|
) -> tuple[int, int]:
|
|
@@ -27,6 +27,10 @@ from src.analyzers.typescript_base import TypeScriptBaseAnalyzer
|
|
|
27
27
|
class TypeScriptFunctionExtractor(TypeScriptBaseAnalyzer):
|
|
28
28
|
"""Extracts function information from TypeScript AST nodes."""
|
|
29
29
|
|
|
30
|
+
def __init__(self) -> None: # pylint: disable=useless-parent-delegation
|
|
31
|
+
"""Initialize the TypeScript function extractor."""
|
|
32
|
+
super().__init__() # Sets self.tree_sitter_available from base class
|
|
33
|
+
|
|
30
34
|
def collect_all_functions(self, root_node: Any) -> list[tuple[Any, str]]:
|
|
31
35
|
"""Collect all function nodes from TypeScript AST.
|
|
32
36
|
|
|
@@ -40,6 +40,10 @@ except ImportError:
|
|
|
40
40
|
class TypeScriptPrintStatementAnalyzer(TypeScriptBaseAnalyzer):
|
|
41
41
|
"""Analyzes TypeScript/JavaScript code for console.* calls using Tree-sitter."""
|
|
42
42
|
|
|
43
|
+
def __init__(self) -> None: # pylint: disable=useless-parent-delegation
|
|
44
|
+
"""Initialize the TypeScript print statement analyzer."""
|
|
45
|
+
super().__init__() # Sets self.tree_sitter_available from base class
|
|
46
|
+
|
|
43
47
|
def find_console_calls(self, root_node: Node, methods: set[str]) -> list[tuple[Node, str, int]]:
|
|
44
48
|
"""Find all console.* calls matching the specified methods.
|
|
45
49
|
|
|
@@ -31,6 +31,10 @@ from .typescript_analyzer import TypeScriptSRPAnalyzer
|
|
|
31
31
|
class ClassAnalyzer:
|
|
32
32
|
"""Coordinates class analysis for Python and TypeScript."""
|
|
33
33
|
|
|
34
|
+
def __init__(self) -> None:
|
|
35
|
+
"""Initialize the class analyzer."""
|
|
36
|
+
pass # Coordinates analysis between language-specific analyzers
|
|
37
|
+
|
|
34
38
|
def analyze_python(
|
|
35
39
|
self, context: BaseLintContext, config: SRPConfig
|
|
36
40
|
) -> list[dict[str, Any]] | list[Violation]:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Purpose: Python AST analyzer for detecting SRP violations in Python classes
|
|
3
3
|
|
|
4
|
-
Scope:
|
|
4
|
+
Scope: Functions for analyzing Python classes using AST
|
|
5
5
|
|
|
6
6
|
Overview: Implements Python-specific SRP analysis using the ast module to parse and analyze
|
|
7
7
|
class definitions. Walks the AST to find all class definitions, then analyzes each class
|
|
@@ -13,7 +13,7 @@ Overview: Implements Python-specific SRP analysis using the ast module to parse
|
|
|
13
13
|
|
|
14
14
|
Dependencies: ast module for Python AST parsing, typing for type hints, heuristics module
|
|
15
15
|
|
|
16
|
-
Exports: PythonSRPAnalyzer class
|
|
16
|
+
Exports: find_all_classes function, analyze_class function, PythonSRPAnalyzer class (compat)
|
|
17
17
|
|
|
18
18
|
Interfaces: find_all_classes(tree), analyze_class(class_node, source, config)
|
|
19
19
|
|
|
@@ -27,8 +27,58 @@ from .config import SRPConfig
|
|
|
27
27
|
from .heuristics import count_loc, count_methods, has_responsibility_keyword
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
def find_all_classes(tree: ast.AST) -> list[ast.ClassDef]:
|
|
31
|
+
"""Find all class definitions in AST.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
tree: Root AST node to search
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of all class definition nodes
|
|
38
|
+
"""
|
|
39
|
+
classes = []
|
|
40
|
+
for node in ast.walk(tree):
|
|
41
|
+
if isinstance(node, ast.ClassDef):
|
|
42
|
+
classes.append(node)
|
|
43
|
+
return classes
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def analyze_class(class_node: ast.ClassDef, source: str, config: SRPConfig) -> dict[str, Any]:
|
|
47
|
+
"""Analyze a class for SRP metrics.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
class_node: AST node representing a class definition
|
|
51
|
+
source: Full source code of the file
|
|
52
|
+
config: SRP configuration with thresholds and keywords
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Dictionary with class metrics (name, method_count, loc, etc.)
|
|
56
|
+
"""
|
|
57
|
+
method_count = count_methods(class_node)
|
|
58
|
+
loc = count_loc(class_node, source)
|
|
59
|
+
has_keyword = has_responsibility_keyword(class_node.name, config.keywords)
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
"class_name": class_node.name,
|
|
63
|
+
"method_count": method_count,
|
|
64
|
+
"loc": loc,
|
|
65
|
+
"has_keyword": has_keyword,
|
|
66
|
+
"line": class_node.lineno,
|
|
67
|
+
"column": class_node.col_offset,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Legacy class wrapper for backward compatibility
|
|
30
72
|
class PythonSRPAnalyzer:
|
|
31
|
-
"""Analyzes Python classes for SRP violations.
|
|
73
|
+
"""Analyzes Python classes for SRP violations.
|
|
74
|
+
|
|
75
|
+
Note: This class is a thin wrapper around module-level functions
|
|
76
|
+
for backward compatibility.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def __init__(self) -> None:
|
|
80
|
+
"""Initialize the analyzer."""
|
|
81
|
+
pass # No state needed
|
|
32
82
|
|
|
33
83
|
def find_all_classes(self, tree: ast.AST) -> list[ast.ClassDef]:
|
|
34
84
|
"""Find all class definitions in AST.
|
|
@@ -39,11 +89,7 @@ class PythonSRPAnalyzer:
|
|
|
39
89
|
Returns:
|
|
40
90
|
List of all class definition nodes
|
|
41
91
|
"""
|
|
42
|
-
|
|
43
|
-
for node in ast.walk(tree):
|
|
44
|
-
if isinstance(node, ast.ClassDef):
|
|
45
|
-
classes.append(node)
|
|
46
|
-
return classes
|
|
92
|
+
return find_all_classes(tree)
|
|
47
93
|
|
|
48
94
|
def analyze_class(
|
|
49
95
|
self, class_node: ast.ClassDef, source: str, config: SRPConfig
|
|
@@ -58,15 +104,4 @@ class PythonSRPAnalyzer:
|
|
|
58
104
|
Returns:
|
|
59
105
|
Dictionary with class metrics (name, method_count, loc, etc.)
|
|
60
106
|
"""
|
|
61
|
-
|
|
62
|
-
loc = count_loc(class_node, source)
|
|
63
|
-
has_keyword = has_responsibility_keyword(class_node.name, config.keywords)
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
"class_name": class_node.name,
|
|
67
|
-
"method_count": method_count,
|
|
68
|
-
"loc": loc,
|
|
69
|
-
"has_keyword": has_keyword,
|
|
70
|
-
"line": class_node.lineno,
|
|
71
|
-
"column": class_node.col_offset,
|
|
72
|
-
}
|
|
107
|
+
return analyze_class(class_node, source, config)
|
|
@@ -10,7 +10,7 @@ Overview: Provides metrics calculation functionality for TypeScript classes in S
|
|
|
10
10
|
|
|
11
11
|
Dependencies: typing
|
|
12
12
|
|
|
13
|
-
Exports: TypeScriptMetricsCalculator
|
|
13
|
+
Exports: count_methods function, count_loc function, TypeScriptMetricsCalculator class (compat)
|
|
14
14
|
|
|
15
15
|
Interfaces: count_methods(class_node), count_loc(class_node, source)
|
|
16
16
|
|
|
@@ -20,8 +20,87 @@ Implementation: Tree-sitter node type matching, AST position arithmetic
|
|
|
20
20
|
from typing import Any
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def count_methods(class_node: Any) -> int:
|
|
24
|
+
"""Count number of methods in a TypeScript class.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
class_node: Class declaration tree-sitter node
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Number of public methods (excludes constructor)
|
|
31
|
+
"""
|
|
32
|
+
class_body = _get_class_body(class_node)
|
|
33
|
+
if not class_body:
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
method_count = 0
|
|
37
|
+
for child in class_body.children:
|
|
38
|
+
if _is_countable_method(child):
|
|
39
|
+
method_count += 1
|
|
40
|
+
|
|
41
|
+
return method_count
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def count_loc(class_node: Any, source: str) -> int:
|
|
45
|
+
"""Count lines of code in a TypeScript class.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
class_node: Class declaration tree-sitter node
|
|
49
|
+
source: Full source code string
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Number of lines in class definition
|
|
53
|
+
"""
|
|
54
|
+
start_line = class_node.start_point[0]
|
|
55
|
+
end_line = class_node.end_point[0]
|
|
56
|
+
return end_line - start_line + 1
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _get_class_body(class_node: Any) -> Any:
|
|
60
|
+
"""Get the class_body node from a class declaration.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
class_node: Class declaration node
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Class body node or None
|
|
67
|
+
"""
|
|
68
|
+
for child in class_node.children:
|
|
69
|
+
if child.type == "class_body":
|
|
70
|
+
return child
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _is_countable_method(node: Any) -> bool:
|
|
75
|
+
"""Check if node is a method that should be counted.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
node: Tree-sitter node to check
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
True if node is a countable method
|
|
82
|
+
"""
|
|
83
|
+
if node.type != "method_definition":
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
# Check if it's a constructor
|
|
87
|
+
return all(
|
|
88
|
+
not (child.type == "property_identifier" and child.text.decode() == "constructor")
|
|
89
|
+
for child in node.children
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Legacy class wrapper for backward compatibility
|
|
23
94
|
class TypeScriptMetricsCalculator:
|
|
24
|
-
"""Calculates metrics for TypeScript classes.
|
|
95
|
+
"""Calculates metrics for TypeScript classes.
|
|
96
|
+
|
|
97
|
+
Note: This class is a thin wrapper around module-level functions
|
|
98
|
+
for backward compatibility.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def __init__(self) -> None:
|
|
102
|
+
"""Initialize the metrics calculator."""
|
|
103
|
+
pass # No state needed
|
|
25
104
|
|
|
26
105
|
def count_methods(self, class_node: Any) -> int:
|
|
27
106
|
"""Count number of methods in a TypeScript class.
|
|
@@ -32,16 +111,7 @@ class TypeScriptMetricsCalculator:
|
|
|
32
111
|
Returns:
|
|
33
112
|
Number of public methods (excludes constructor)
|
|
34
113
|
"""
|
|
35
|
-
|
|
36
|
-
if not class_body:
|
|
37
|
-
return 0
|
|
38
|
-
|
|
39
|
-
method_count = 0
|
|
40
|
-
for child in class_body.children:
|
|
41
|
-
if self._is_countable_method(child):
|
|
42
|
-
method_count += 1
|
|
43
|
-
|
|
44
|
-
return method_count
|
|
114
|
+
return count_methods(class_node)
|
|
45
115
|
|
|
46
116
|
def count_loc(self, class_node: Any, source: str) -> int:
|
|
47
117
|
"""Count lines of code in a TypeScript class.
|
|
@@ -53,38 +123,4 @@ class TypeScriptMetricsCalculator:
|
|
|
53
123
|
Returns:
|
|
54
124
|
Number of lines in class definition
|
|
55
125
|
"""
|
|
56
|
-
|
|
57
|
-
end_line = class_node.end_point[0]
|
|
58
|
-
return end_line - start_line + 1
|
|
59
|
-
|
|
60
|
-
def _get_class_body(self, class_node: Any) -> Any:
|
|
61
|
-
"""Get the class_body node from a class declaration.
|
|
62
|
-
|
|
63
|
-
Args:
|
|
64
|
-
class_node: Class declaration node
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
Class body node or None
|
|
68
|
-
"""
|
|
69
|
-
for child in class_node.children:
|
|
70
|
-
if child.type == "class_body":
|
|
71
|
-
return child
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
def _is_countable_method(self, node: Any) -> bool:
|
|
75
|
-
"""Check if node is a method that should be counted.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
node: Tree-sitter node to check
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
True if node is a countable method
|
|
82
|
-
"""
|
|
83
|
-
if node.type != "method_definition":
|
|
84
|
-
return False
|
|
85
|
-
|
|
86
|
-
# Check if it's a constructor
|
|
87
|
-
return all(
|
|
88
|
-
not (child.type == "property_identifier" and child.text.decode() == "constructor")
|
|
89
|
-
for child in node.children
|
|
90
|
-
)
|
|
126
|
+
return count_loc(class_node, source)
|
|
@@ -29,6 +29,10 @@ from src.core.violation_builder import BaseViolationBuilder, ViolationInfo
|
|
|
29
29
|
class ViolationBuilder(BaseViolationBuilder):
|
|
30
30
|
"""Builds SRP violations with messages and suggestions."""
|
|
31
31
|
|
|
32
|
+
def __init__(self) -> None: # pylint: disable=useless-parent-delegation
|
|
33
|
+
"""Initialize the violation builder."""
|
|
34
|
+
super().__init__() # Inherits from BaseViolationBuilder
|
|
35
|
+
|
|
32
36
|
def build_violation(
|
|
33
37
|
self,
|
|
34
38
|
metrics: dict[str, Any],
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Stateless class linter package for detecting classes without state
|
|
3
|
+
|
|
4
|
+
Scope: Python classes that should be refactored to module-level functions
|
|
5
|
+
|
|
6
|
+
Overview: Package for detecting Python classes that have no constructor (__init__
|
|
7
|
+
or __new__) and no instance state (self.attr assignments), indicating they should
|
|
8
|
+
be refactored to module-level functions. Identifies a common anti-pattern in
|
|
9
|
+
AI-generated code where classes are used as namespaces rather than for object-
|
|
10
|
+
oriented encapsulation.
|
|
11
|
+
|
|
12
|
+
Dependencies: Python AST module, base linter framework
|
|
13
|
+
|
|
14
|
+
Exports: StatelessClassRule - main rule for detecting stateless classes
|
|
15
|
+
|
|
16
|
+
Interfaces: StatelessClassRule.check(context) -> list[Violation]
|
|
17
|
+
|
|
18
|
+
Implementation: AST-based analysis checking for constructor methods and instance
|
|
19
|
+
attribute assignments while excluding legitimate patterns (ABC, Protocol, decorators)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from .linter import StatelessClassRule
|
|
23
|
+
from .python_analyzer import ClassInfo, StatelessClassAnalyzer
|
|
24
|
+
|
|
25
|
+
__all__ = ["StatelessClassRule", "StatelessClassAnalyzer", "ClassInfo"]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Configuration schema for stateless-class linter
|
|
3
|
+
|
|
4
|
+
Scope: Stateless class linter configuration for Python files
|
|
5
|
+
|
|
6
|
+
Overview: Defines configuration schema for stateless-class linter. Provides
|
|
7
|
+
StatelessClassConfig dataclass with enabled flag, min_methods threshold (default 2)
|
|
8
|
+
for determining minimum methods required to flag a class as stateless, and ignore
|
|
9
|
+
patterns list for excluding specific files or directories. Supports per-file and
|
|
10
|
+
per-directory config overrides through from_dict class method. Integrates with
|
|
11
|
+
orchestrator's configuration system via .thailint.yaml.
|
|
12
|
+
|
|
13
|
+
Dependencies: dataclasses module for configuration structure, typing module for type hints
|
|
14
|
+
|
|
15
|
+
Exports: StatelessClassConfig dataclass
|
|
16
|
+
|
|
17
|
+
Interfaces: from_dict(config, language) -> StatelessClassConfig for configuration loading
|
|
18
|
+
|
|
19
|
+
Implementation: Dataclass with defaults matching stateless class detection conventions
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class StatelessClassConfig:
|
|
28
|
+
"""Configuration for stateless-class linter."""
|
|
29
|
+
|
|
30
|
+
enabled: bool = True
|
|
31
|
+
min_methods: int = 2
|
|
32
|
+
ignore: list[str] = field(default_factory=list)
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def from_dict(
|
|
36
|
+
cls, config: dict[str, Any] | None, language: str | None = None
|
|
37
|
+
) -> "StatelessClassConfig":
|
|
38
|
+
"""Load configuration from dictionary.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
config: Dictionary containing configuration values, or None
|
|
42
|
+
language: Programming language (unused, for interface compatibility)
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
StatelessClassConfig instance with values from dictionary
|
|
46
|
+
"""
|
|
47
|
+
if config is None:
|
|
48
|
+
return cls()
|
|
49
|
+
|
|
50
|
+
ignore_patterns = config.get("ignore", [])
|
|
51
|
+
if not isinstance(ignore_patterns, list):
|
|
52
|
+
ignore_patterns = []
|
|
53
|
+
|
|
54
|
+
return cls(
|
|
55
|
+
enabled=config.get("enabled", True),
|
|
56
|
+
min_methods=config.get("min_methods", 2),
|
|
57
|
+
ignore=ignore_patterns,
|
|
58
|
+
)
|