Files
foxhunt/scripts/check_unused_deps.py
jgrusewski 2df1ea92e1 feat(ml): WAVE 29 DQN Codebase Cleanup & Refactoring Campaign
BREAKING CHANGES:
- Removed orphaned dqn.rs monolithic trainer (4,975 lines)
- Removed orphaned dqn_ensemble.rs module (816 lines)
- Removed orphaned tft.rs and tft_complete_int8_integration_test.rs
- TFT trainer split into modular directory structure

DQN Module Refactoring:
- Split trainers/dqn.rs into modular structure (config.rs, statistics.rs, trainer.rs)
- Fixed hyperopt 39D search space (continuous params only)
- Boolean flags (use_dueling, use_double_dqn, use_per, use_noisy_nets) are now FIXED architectural decisions
- use_distributional defaults to false (Candle BUG #36 - scatter_add gradient issues)

Clean Module Structure:
- ml/src/trainers/dqn/ directory with proper mod.rs exports
- ml/src/trainers/tft/ directory with config.rs, types.rs, model.rs, trainer.rs, tests.rs
- All P0 features validated: TD-error clamping, batch diversity, LR scheduler, priority staleness

Documentation:
- Added comprehensive docs in docs/codebase-cleanup/
- ADR-001 for DQN refactoring decisions
- Rainbow DQN component matrix and quick reference guides

Build Status: Compiles with zero errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 23:46:13 +01:00

155 lines
5.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Analyze Rust workspace for unused dependencies.
Checks if dependencies in Cargo.toml are actually used in source code.
"""
import os
import re
import subprocess
from pathlib import Path
from collections import defaultdict
def parse_cargo_toml(path):
"""Parse dependencies from Cargo.toml"""
deps = []
try:
with open(path, 'r') as f:
content = f.read()
# Extract dependencies section
in_deps = False
in_dev_deps = False
for line in content.split('\n'):
if line.strip().startswith('[dependencies]'):
in_deps = True
in_dev_deps = False
continue
elif line.strip().startswith('[dev-dependencies]'):
in_deps = False
in_dev_deps = True
continue
elif line.strip().startswith('['):
in_deps = False
in_dev_deps = False
continue
if in_deps and line.strip() and not line.strip().startswith('#'):
# Extract dependency name
match = re.match(r'^([a-zA-Z0-9_-]+)\s*=', line)
if match:
dep_name = match.group(1).replace('-', '_')
deps.append((dep_name, 'normal'))
elif in_dev_deps and line.strip() and not line.strip().startswith('#'):
match = re.match(r'^([a-zA-Z0-9_-]+)\s*=', line)
if match:
dep_name = match.group(1).replace('-', '_')
deps.append((dep_name, 'dev'))
except Exception as e:
print(f"Error parsing {path}: {e}")
return deps
def check_dep_usage(crate_path, dep_name):
"""Check if dependency is used in Rust source files"""
src_path = os.path.join(crate_path, 'src')
tests_path = os.path.join(crate_path, 'tests')
benches_path = os.path.join(crate_path, 'benches')
examples_path = os.path.join(crate_path, 'examples')
patterns = [
f'use {dep_name}',
f'use {dep_name}::',
f'extern crate {dep_name}',
f'{dep_name}::',
]
for base_path in [src_path, tests_path, benches_path, examples_path]:
if not os.path.exists(base_path):
continue
for root, dirs, files in os.walk(base_path):
# Skip target directory
if 'target' in dirs:
dirs.remove('target')
for file in files:
if not file.endswith('.rs'):
continue
file_path = os.path.join(root, file)
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
for pattern in patterns:
if pattern in content:
return True
except Exception:
continue
return False
def analyze_workspace():
"""Analyze all crates in workspace"""
workspace_root = '/home/jgrusewski/Work/foxhunt'
# Find all Cargo.toml files (excluding target directories)
cargo_tomls = []
for root, dirs, files in os.walk(workspace_root):
# Skip target and vendor directories
if 'target' in dirs:
dirs.remove('target')
if '.git' in dirs:
dirs.remove('.git')
if 'Cargo.toml' in files:
cargo_path = os.path.join(root, 'Cargo.toml')
# Skip if it's a workspace-only Cargo.toml
with open(cargo_path, 'r') as f:
content = f.read()
if '[package]' in content:
cargo_tomls.append(cargo_path)
results = {}
for cargo_toml in cargo_tomls[:15]: # Limit to first 15 for performance
crate_path = os.path.dirname(cargo_toml)
crate_name = os.path.basename(crate_path)
print(f"\nAnalyzing {crate_name}...")
deps = parse_cargo_toml(cargo_toml)
unused = []
for dep_name, dep_type in deps:
# Skip workspace crates
if dep_name in ['trading_engine', 'risk', 'data', 'backtesting',
'common', 'storage', 'ml', 'config', 'database',
'market_data', 'tli', 'risk_data', 'trading_data',
'adaptive_strategy', 'model_loader']:
continue
# Check if dependency is used
if not check_dep_usage(crate_path, dep_name):
unused.append((dep_name, dep_type))
if unused:
results[crate_name] = unused
return results
if __name__ == '__main__':
print("=== UNUSED DEPENDENCY ANALYSIS ===\n")
results = analyze_workspace()
if not results:
print("\n✅ No obvious unused dependencies found!")
else:
print("\n⚠️ POTENTIALLY UNUSED DEPENDENCIES:\n")
for crate, unused in results.items():
print(f"\n{crate}:")
for dep, dep_type in unused:
print(f" - {dep} ({dep_type})")
print("\n=== ANALYSIS COMPLETE ===")