fix: Resolve 21 ruff linting errors (SIM102, SIM117, B904, SIM113, B007)

Fixed all 21 linting errors identified in GitHub Actions:

SIM102 (7 errors - nested if statements):
- config_extractor.py:468 - Combined nested conditions
- config_validator.py (was B904, already fixed)
- pattern_recognizer.py:430,538,916 - Combined nested conditions
- test_example_extractor.py:365,412,460 - Combined nested conditions
- unified_skill_builder.py:1070 - Combined nested conditions

SIM117 (9 errors - multiple with statements):
- test_install_agent.py:418 - Combined with statements
- test_issue_219_e2e.py:278 - Combined with statements
- test_llms_txt_downloader.py:33,88 - Combined with statements
- test_skip_llms_txt.py:75,98,121,148,172,304 - Combined with statements

B904 (1 error - exception handling):
- config_validator.py:62 - Added 'from e' to exception chain

SIM113 (1 error - enumerate usage):
- doc_scraper.py:1068 - Removed unused 'completed' counter variable

B007 (1 error - unused loop variable):
- pdf_scraper.py:167 - Changed 'keywords' to '_' for unused variable

All changes improve code quality without altering functionality.
Tests: 1214 passed, 167 skipped (4 pre-existing failures unrelated)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
yusyus
2026-01-17 23:54:22 +03:00
parent 6439c85cde
commit 9666938eb0
11 changed files with 173 additions and 199 deletions

View File

@@ -466,24 +466,23 @@ class ConfigParser:
tree = ast.parse(config_file.raw_content)
for node in ast.walk(tree):
if isinstance(node, ast.Assign):
# Get variable name and skip private variables
if len(node.targets) == 1 and isinstance(node.targets[0], ast.Name) and not node.targets[0].id.startswith("_"):
key = node.targets[0].id
# Get variable name and skip private variables
if isinstance(node, ast.Assign) and len(node.targets) == 1 and isinstance(node.targets[0], ast.Name) and not node.targets[0].id.startswith("_"):
key = node.targets[0].id
# Extract value
try:
value = ast.literal_eval(node.value)
setting = ConfigSetting(
key=key,
value=value,
value_type=self._infer_type(value),
description=self._extract_python_docstring(node),
)
config_file.settings.append(setting)
except (ValueError, TypeError):
# Can't evaluate complex expressions
pass
# Extract value
try:
value = ast.literal_eval(node.value)
setting = ConfigSetting(
key=key,
value=value,
value_type=self._infer_type(value),
description=self._extract_python_docstring(node),
)
config_file.settings.append(setting)
except (ValueError, TypeError):
# Can't evaluate complex expressions
pass
except SyntaxError as e:
config_file.parse_errors.append(f"Python parse error: {str(e)}")

View File

@@ -59,7 +59,7 @@ class ConfigValidator:
except FileNotFoundError as e:
raise ValueError(f"Config file not found: {self.config_path}") from e
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in config file: {e}")
raise ValueError(f"Invalid JSON in config file: {e}") from e
def _detect_format(self) -> bool:
"""

View File

@@ -1056,7 +1056,6 @@ class DocToSkillConverter:
futures.append(future)
# Wait for some to complete before submitting more
completed = 0
for future in as_completed(futures[:batch_size]):
# Check for exceptions
try:
@@ -1065,8 +1064,6 @@ class DocToSkillConverter:
with self.lock:
logger.warning(" ⚠️ Worker exception: %s", e)
completed += 1
with self.lock:
self.pages_scraped += 1

View File

@@ -427,13 +427,12 @@ class SingletonDetector(BasePatternDetector):
for method in class_sig.methods:
# Python: __init__ or __new__
# Java/C#: private constructor (detected by naming)
if method.name in ["__new__", "__init__", "constructor"]:
# Check if it has logic (not just pass)
if method.docstring or len(method.parameters) > 1:
evidence.append(f"Controlled initialization: {method.name}")
confidence += 0.3
has_init_control = True
break
# Check if it has logic (not just pass)
if method.name in ["__new__", "__init__", "constructor"] and (method.docstring or len(method.parameters) > 1):
evidence.append(f"Controlled initialization: {method.name}")
confidence += 0.3
has_init_control = True
break
# Check for class-level instance storage
# This would require checking class attributes (future enhancement)
@@ -535,10 +534,9 @@ class FactoryDetector(BasePatternDetector):
factory_method_names = ["create", "make", "build", "new", "get"]
for method in class_sig.methods:
method_lower = method.name.lower()
if any(name in method_lower for name in factory_method_names):
# Check if method returns something (has return type or is not void)
if method.return_type or "create" in method_lower:
return PatternInstance(
# Check if method returns something (has return type or is not void)
if any(name in method_lower for name in factory_method_names) and (method.return_type or "create" in method_lower):
return PatternInstance(
pattern_type=self.pattern_type,
category=self.category,
confidence=0.6,
@@ -913,16 +911,15 @@ class DecoratorDetector(BasePatternDetector):
# Check __init__ for composition (takes object parameter)
init_method = next((m for m in class_sig.methods if m.name == "__init__"), None)
if init_method:
# Check if takes object parameter (not just self)
if len(init_method.parameters) > 1: # More than just 'self'
param_names = [p.name for p in init_method.parameters if p.name != "self"]
if any(
name in ["wrapped", "component", "inner", "obj", "target"]
for name in param_names
):
evidence.append(f"Takes wrapped object in constructor: {param_names}")
confidence += 0.4
# Check if takes object parameter (not just self)
if init_method and len(init_method.parameters) > 1: # More than just 'self'
param_names = [p.name for p in init_method.parameters if p.name != "self"]
if any(
name in ["wrapped", "component", "inner", "obj", "target"]
for name in param_names
):
evidence.append(f"Takes wrapped object in constructor: {param_names}")
confidence += 0.4
if confidence >= 0.5:
return PatternInstance(

View File

@@ -164,7 +164,7 @@ class PDFToSkillConverter:
else:
# Keyword-based categorization
# Initialize categories
for cat_key, keywords in self.categories.items():
for cat_key, _ in self.categories.items():
categorized[cat_key] = {"title": cat_key.replace("_", " ").title(), "pages": []}
# Categorize by keywords

View File

@@ -361,36 +361,35 @@ class PythonTestAnalyzer:
examples = []
for node in ast.walk(func_node):
if isinstance(node, ast.Assign) and isinstance(node.value, ast.Call):
# Check if meaningful instantiation
if self._is_meaningful_instantiation(node):
code = ast.unparse(node)
# Check if meaningful instantiation
if isinstance(node, ast.Assign) and isinstance(node.value, ast.Call) and self._is_meaningful_instantiation(node):
code = ast.unparse(node)
# Skip trivial or mock-only
if len(code) < 20 or "Mock()" in code:
continue
# Skip trivial or mock-only
if len(code) < 20 or "Mock()" in code:
continue
# Get class name
class_name = self._get_class_name(node.value)
# Get class name
class_name = self._get_class_name(node.value)
example = TestExample(
example_id=self._generate_id(code),
test_name=func_node.name,
category="instantiation",
code=code,
language="Python",
description=f"Instantiate {class_name}: {description}",
expected_behavior=self._extract_assertion_after(func_node, node),
setup_code=setup_code,
file_path=file_path,
line_start=node.lineno,
line_end=node.end_lineno or node.lineno,
complexity_score=self._calculate_complexity(code),
confidence=0.8,
tags=tags,
dependencies=imports,
)
examples.append(example)
example = TestExample(
example_id=self._generate_id(code),
test_name=func_node.name,
category="instantiation",
code=code,
language="Python",
description=f"Instantiate {class_name}: {description}",
expected_behavior=self._extract_assertion_after(func_node, node),
setup_code=setup_code,
file_path=file_path,
line_start=node.lineno,
line_end=node.end_lineno or node.lineno,
complexity_score=self._calculate_complexity(code),
confidence=0.8,
tags=tags,
dependencies=imports,
)
examples.append(example)
return examples
@@ -408,39 +407,37 @@ class PythonTestAnalyzer:
statements = func_node.body
for i, stmt in enumerate(statements):
# Look for method calls
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
# Check if next statement is an assertion
if i + 1 < len(statements):
next_stmt = statements[i + 1]
if self._is_assertion(next_stmt):
method_call = ast.unparse(stmt)
assertion = ast.unparse(next_stmt)
# Look for method calls and check if next statement is an assertion
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call) and i + 1 < len(statements):
next_stmt = statements[i + 1]
if self._is_assertion(next_stmt):
method_call = ast.unparse(stmt)
assertion = ast.unparse(next_stmt)
code = f"{method_call}\n{assertion}"
code = f"{method_call}\n{assertion}"
# Skip trivial assertions
if any(trivial in assertion for trivial in self.trivial_patterns):
continue
# Skip trivial assertions
if any(trivial in assertion for trivial in self.trivial_patterns):
continue
example = TestExample(
example_id=self._generate_id(code),
test_name=func_node.name,
category="method_call",
code=code,
language="Python",
description=description,
expected_behavior=assertion,
setup_code=setup_code,
file_path=file_path,
line_start=stmt.lineno,
line_end=next_stmt.end_lineno or next_stmt.lineno,
complexity_score=self._calculate_complexity(code),
confidence=0.85,
tags=tags,
dependencies=imports,
)
examples.append(example)
example = TestExample(
example_id=self._generate_id(code),
test_name=func_node.name,
category="method_call",
code=code,
language="Python",
description=description,
expected_behavior=assertion,
setup_code=setup_code,
file_path=file_path,
line_start=stmt.lineno,
line_end=next_stmt.end_lineno or next_stmt.lineno,
complexity_score=self._calculate_complexity(code),
confidence=0.85,
tags=tags,
dependencies=imports,
)
examples.append(example)
return examples
@@ -457,31 +454,30 @@ class PythonTestAnalyzer:
examples = []
for node in ast.walk(func_node):
if isinstance(node, ast.Assign) and isinstance(node.value, ast.Dict):
# Must have 2+ keys and be meaningful
if len(node.value.keys) >= 2:
code = ast.unparse(node)
# Must have 2+ keys and be meaningful
if isinstance(node, ast.Assign) and isinstance(node.value, ast.Dict) and len(node.value.keys) >= 2:
code = ast.unparse(node)
# Check if looks like configuration
if self._is_config_dict(node.value):
example = TestExample(
example_id=self._generate_id(code),
test_name=func_node.name,
category="config",
code=code,
language="Python",
description=f"Configuration example: {description}",
expected_behavior=self._extract_assertion_after(func_node, node),
setup_code=setup_code,
file_path=file_path,
line_start=node.lineno,
line_end=node.end_lineno or node.lineno,
complexity_score=self._calculate_complexity(code),
confidence=0.75,
tags=tags,
dependencies=imports,
)
examples.append(example)
# Check if looks like configuration
if self._is_config_dict(node.value):
example = TestExample(
example_id=self._generate_id(code),
test_name=func_node.name,
category="config",
code=code,
language="Python",
description=f"Configuration example: {description}",
expected_behavior=self._extract_assertion_after(func_node, node),
setup_code=setup_code,
file_path=file_path,
line_start=node.lineno,
line_end=node.end_lineno or node.lineno,
complexity_score=self._calculate_complexity(code),
confidence=0.75,
tags=tags,
dependencies=imports,
)
examples.append(example)
return examples

View File

@@ -1067,11 +1067,10 @@ This skill combines knowledge from multiple sources:
languages = c3_data["architecture"].get("languages", {})
# If no languages from C3.7, try to get from GitHub data
if not languages:
# github_data already available from method scope
if github_data.get("languages"):
# GitHub data has languages as list, convert to dict with count 1
languages = dict.fromkeys(github_data["languages"], 1)
# github_data already available from method scope
if not languages and github_data.get("languages"):
# GitHub data has languages as list, convert to dict with count 1
languages = dict.fromkeys(github_data["languages"], 1)
if languages:
f.write("**Languages Detected**:\n")