Chapter 12: Practical Projects and Best Practices
Haiyue
22min
Chapter 12: Practical Projects and Best Practices
Learning Objectives
- Design a complete code quality checking solution
- Implement team-level code standard management
- Master the combined use of Pylint with other tools
- Summarize best practices for using Pylint
Key Concepts
Code Quality Management System
A complete code quality management system should include:
🔄 正在渲染 Mermaid 图表...
Pylint’s Position in the Development Process
| Development Stage | Pylint Role | Tool Integration |
|---|---|---|
| Coding Phase | Real-time code checking | IDE integration |
| Pre-commit | Code quality validation | pre-commit hooks |
| Code Review | Automated standard checking | GitHub Actions |
| Integration Testing | Quality gates | CI/CD pipeline |
| Pre-release | Final quality check | Automation scripts |
Code Examples
Complete Project Quality Checking Solution
# quality_management.py - Code Quality Management System
import os
import sys
import json
import subprocess
import datetime
from pathlib import Path
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass, asdict
@dataclass
class QualityMetrics:
"""Code quality metrics"""
pylint_score: float
coverage_percentage: float
test_count: int
line_count: int
complexity_score: float
security_issues: int
timestamp: str
@dataclass
class QualityConfig:
"""Quality configuration"""
min_pylint_score: float = 8.0
min_coverage: float = 80.0
max_complexity: float = 10.0
enable_security_check: bool = True
fail_on_error: bool = True
class CodeQualityManager:
"""Code quality manager"""
def __init__(self, project_root: str, config: QualityConfig = None):
self.project_root = Path(project_root)
self.config = config or QualityConfig()
self.results = {}
def run_pylint_check(self) -> Tuple[bool, Dict]:
"""Run Pylint check"""
print("Running Pylint check...")
try:
result = subprocess.run(
['pylint', '--output-format=json', '--reports=yes', 'src/'],
cwd=self.project_root,
capture_output=True,
text=True
)
if result.stdout:
# Parse JSON output
issues = json.loads(result.stdout) if result.stdout.strip() else []
# Extract score
score = self._extract_pylint_score(result.stderr)
pylint_result = {
'score': score,
'issues': issues,
'total_issues': len(issues),
'error_count': len([i for i in issues if i['type'] == 'error']),
'warning_count': len([i for i in issues if i['type'] == 'warning']),
'convention_count': len([i for i in issues if i['type'] == 'convention']),
'refactor_count': len([i for i in issues if i['type'] == 'refactor']),
'passed': score >= self.config.min_pylint_score
}
self.results['pylint'] = pylint_result
return pylint_result['passed'], pylint_result
else:
return False, {'error': 'Pylint run failed', 'stderr': result.stderr}
except Exception as e:
return False, {'error': f'Pylint check exception: {str(e)}'}
def run_coverage_check(self) -> Tuple[bool, Dict]:
"""Run coverage check"""
print("Running coverage check...")
try:
# Run tests and generate coverage report
subprocess.run(['pytest', '--cov=src', '--cov-report=json'],
cwd=self.project_root, check=True)
# Read coverage report
coverage_file = self.project_root / 'coverage.json'
if coverage_file.exists():
with open(coverage_file) as f:
coverage_data = json.load(f)
total_coverage = coverage_data['totals']['percent_covered']
coverage_result = {
'percentage': total_coverage,
'lines_covered': coverage_data['totals']['covered_lines'],
'lines_total': coverage_data['totals']['num_statements'],
'passed': total_coverage >= self.config.min_coverage
}
self.results['coverage'] = coverage_result
return coverage_result['passed'], coverage_result
else:
return False, {'error': 'Coverage report file does not exist'}
except Exception as e:
return False, {'error': f'Coverage check exception: {str(e)}'}
def run_security_check(self) -> Tuple[bool, Dict]:
"""Run security check"""
if not self.config.enable_security_check:
return True, {'skipped': True}
print("Running security check...")
try:
result = subprocess.run(
['bandit', '-r', 'src/', '-f', 'json'],
cwd=self.project_root,
capture_output=True,
text=True
)
if result.stdout:
security_data = json.loads(result.stdout)
security_result = {
'high_severity': len([r for r in security_data['results']
if r['issue_severity'] == 'HIGH']),
'medium_severity': len([r for r in security_data['results']
if r['issue_severity'] == 'MEDIUM']),
'low_severity': len([r for r in security_data['results']
if r['issue_severity'] == 'LOW']),
'total_issues': len(security_data['results']),
'passed': len([r for r in security_data['results']
if r['issue_severity'] == 'HIGH']) == 0
}
self.results['security'] = security_result
return security_result['passed'], security_result
except subprocess.CalledProcessError:
# Bandit may not be installed or found serious security issues
return False, {'error': 'Security check failed'}
except Exception as e:
return False, {'error': f'Security check exception: {str(e)}'}
def run_complexity_check(self) -> Tuple[bool, Dict]:
"""Run complexity check"""
print("Running complexity check...")
try:
result = subprocess.run(
['radon', 'cc', 'src/', '--json'],
cwd=self.project_root,
capture_output=True,
text=True
)
if result.stdout:
complexity_data = json.loads(result.stdout)
# Calculate average complexity
all_complexities = []
for file_data in complexity_data.values():
for item in file_data:
if 'complexity' in item:
all_complexities.append(item['complexity'])
avg_complexity = sum(all_complexities) / len(all_complexities) if all_complexities else 0
max_complexity = max(all_complexities) if all_complexities else 0
complexity_result = {
'average_complexity': avg_complexity,
'max_complexity': max_complexity,
'high_complexity_count': len([c for c in all_complexities if c > 10]),
'passed': avg_complexity <= self.config.max_complexity
}
self.results['complexity'] = complexity_result
return complexity_result['passed'], complexity_result
except Exception as e:
return False, {'error': f'Complexity check exception: {str(e)}'}
def generate_quality_report(self) -> Dict:
"""Generate quality report"""
print("Generating quality report...")
report = {
'project': str(self.project_root),
'timestamp': datetime.datetime.now().isoformat(),
'config': asdict(self.config),
'results': self.results,
'summary': self._generate_summary()
}
# Save report
report_file = self.project_root / 'quality_report.json'
with open(report_file, 'w') as f:
json.dump(report, f, indent=2)
return report
def run_full_check(self) -> bool:
"""Run complete quality check"""
print("Starting complete code quality check...")
checks = [
('Pylint', self.run_pylint_check),
('Coverage', self.run_coverage_check),
('Security', self.run_security_check),
('Complexity', self.run_complexity_check)
]
all_passed = True
failed_checks = []
for check_name, check_func in checks:
try:
passed, result = check_func()
if not passed:
all_passed = False
failed_checks.append(check_name)
if self.config.fail_on_error:
print(f"{check_name} check failed: {result}")
else:
print(f"{check_name} check did not pass: {result}")
else:
print(f"{check_name} check passed")
except Exception as e:
print(f"{check_name} check error: {str(e)}")
all_passed = False
failed_checks.append(check_name)
# Generate report
self.generate_quality_report()
# Output summary
if all_passed:
print("\nAll quality checks passed!")
else:
print(f"\nThe following checks did not pass: {', '.join(failed_checks)}")
return all_passed
def _extract_pylint_score(self, stderr_output: str) -> float:
"""Extract score from Pylint stderr output"""
for line in stderr_output.split('\n'):
if 'rated at' in line:
try:
score_str = line.split('rated at ')[1].split('/')[0]
return float(score_str)
except (IndexError, ValueError):
continue
return 0.0
def _generate_summary(self) -> Dict:
"""Generate check result summary"""
summary = {
'total_checks': len(self.results),
'passed_checks': 0,
'failed_checks': 0,
'overall_status': 'unknown'
}
for check_result in self.results.values():
if check_result.get('passed', False):
summary['passed_checks'] += 1
else:
summary['failed_checks'] += 1
summary['overall_status'] = 'passed' if summary['failed_checks'] == 0 else 'failed'
return summary
# Team-level quality management
class TeamQualityStandards:
"""Team quality standards management"""
def __init__(self):
self.standards = {
'pylint_score': 8.5,
'coverage_threshold': 85.0,
'max_complexity': 8.0,
'security_tolerance': 'medium',
'quality_gates': {
'development': {'pylint_score': 7.0, 'coverage': 70.0},
'staging': {'pylint_score': 8.0, 'coverage': 80.0},
'production': {'pylint_score': 9.0, 'coverage': 90.0}
}
}
def get_environment_config(self, environment: str) -> QualityConfig:
"""Get quality configuration for specific environment"""
env_standards = self.standards['quality_gates'].get(environment, {})
return QualityConfig(
min_pylint_score=env_standards.get('pylint_score', self.standards['pylint_score']),
min_coverage=env_standards.get('coverage', self.standards['coverage_threshold']),
max_complexity=self.standards['max_complexity'],
enable_security_check=True,
fail_on_error=environment == 'production'
)
def validate_team_compliance(self, project_path: str, environment: str = 'development') -> bool:
"""Validate whether project complies with team standards"""
config = self.get_environment_config(environment)
quality_manager = CodeQualityManager(project_path, config)
return quality_manager.run_full_check()
# Tool chain integration example
def setup_development_tools():
"""Set up development tool chain"""
# 1. Create pre-commit configuration
precommit_config = """
repos:
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:
- id: isort
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
language: system
types: [python]
args: [--fail-under=8.0]
- repo: https://github.com/PyCQA/bandit
rev: 1.7.4
hooks:
- id: bandit
args: [-r, src/]
"""
# 2. Create GitHub Actions workflow
github_workflow = """
name: Code Quality Check
on: [push, pull_request]
jobs:
quality-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pylint pytest-cov bandit radon
- name: Run quality checks
run: |
python -m quality_management
- name: Upload quality report
uses: actions/upload-artifact@v3
with:
name: quality-report
path: quality_report.json
"""
# 3. Create Makefile
makefile_content = """
.PHONY: lint test coverage security complexity quality
lint:
\tpylint src/
test:
\tpytest tests/
coverage:
\tpytest --cov=src --cov-report=html --cov-report=term
security:
\tbandit -r src/
complexity:
\tradon cc src/
quality: lint test coverage security complexity
\t@echo "All quality checks completed"
fix:
\tblack src/ tests/
\tisort src/ tests/
install-dev:
\tpip install -r requirements-dev.txt
\tpre-commit install
"""
return precommit_config, github_workflow, makefile_content
# Practical project example
class ProjectQualityBootstrap:
"""Project quality bootstrap program"""
def __init__(self, project_path: str):
self.project_path = Path(project_path)
def bootstrap_quality_system(self):
"""Bootstrap project quality system"""
print("Setting up project quality system...")
# 1. Create .pylintrc
self._create_pylintrc()
# 2. Create quality check script
self._create_quality_script()
# 3. Set up pre-commit
self._setup_precommit()
# 4. Create CI configuration
self._create_ci_config()
# 5. Generate documentation
self._generate_documentation()
print("Project quality system setup complete!")
def _create_pylintrc(self):
"""Create .pylintrc configuration file"""
pylintrc_content = """
[MASTER]
jobs=0
persistent=yes
[MESSAGES CONTROL]
disable=missing-module-docstring,
too-few-public-methods
[FORMAT]
max-line-length=88
[DESIGN]
max-args=5
max-locals=15
min-public-methods=1
[BASIC]
good-names=i,j,k,ex,Run,_,df,ax,fig
argument-naming-style=snake_case
class-naming-style=PascalCase
function-naming-style=snake_case
"""
with open(self.project_path / '.pylintrc', 'w') as f:
f.write(pylintrc_content)
def _create_quality_script(self):
"""Create quality check script"""
script_content = '''#!/usr/bin/env python3
"""Project quality check script"""
from quality_management import CodeQualityManager, QualityConfig
def main():
config = QualityConfig(
min_pylint_score=8.0,
min_coverage=80.0,
max_complexity=10.0
)
manager = CodeQualityManager('.', config)
success = manager.run_full_check()
return 0 if success else 1
if __name__ == '__main__':
exit(main())
'''
script_file = self.project_path / 'check_quality.py'
with open(script_file, 'w') as f:
f.write(script_content)
# Make script executable
script_file.chmod(0o755)
def _setup_precommit(self):
"""Set up pre-commit"""
precommit_config, _, _ = setup_development_tools()
with open(self.project_path / '.pre-commit-config.yaml', 'w') as f:
f.write(precommit_config)
def _create_ci_config(self):
"""Create CI configuration"""
_, github_workflow, makefile = setup_development_tools()
# Create GitHub Actions directory and file
github_dir = self.project_path / '.github' / 'workflows'
github_dir.mkdir(parents=True, exist_ok=True)
with open(github_dir / 'quality.yml', 'w') as f:
f.write(github_workflow)
# Create Makefile
with open(self.project_path / 'Makefile', 'w') as f:
f.write(makefile)
def _generate_documentation(self):
"""Generate quality standards documentation"""
doc_content = """# Code Quality Standards
## Quality Metrics
- **Pylint Score**: >= 8.0
- **Test Coverage**: >= 80%
- **Cyclomatic Complexity**: <= 10
- **Security Check**: No high-risk issues
## Tool Chain
1. **Pylint**: Code quality checking
2. **pytest**: Unit testing
3. **coverage**: Coverage checking
4. **bandit**: Security checking
5. **black**: Code formatting
6. **isort**: Import sorting
## Usage
```bash
# Run complete quality check
make quality
# Run individual checks
make lint
make test
make coverage
make security
# Fix formatting issues
make fix
# Install development dependencies
make install-dev
CI/CD Integration
The project has configured GitHub Actions to automatically run quality checks. Checks are triggered on every push and pull request.
Pre-commit Hooks
Pre-commit hooks are configured to automatically run formatting and basic checks before commits. """
with open(self.project_path / 'QUALITY.md', 'w') as f:
f.write(doc_content)
Usage example
def main(): """Main function example""" import argparse
parser = argparse.ArgumentParser(description='Code Quality Management Tool')
parser.add_argument('--project-path', default='.', help='Project path')
parser.add_argument('--environment', default='development',
choices=['development', 'staging', 'production'],
help='Environment type')
parser.add_argument('--bootstrap', action='store_true', help='Bootstrap project quality system')
args = parser.parse_args()
if args.bootstrap:
bootstrap = ProjectQualityBootstrap(args.project_path)
bootstrap.bootstrap_quality_system()
else:
# Run quality check
team_standards = TeamQualityStandards()
success = team_standards.validate_team_compliance(
args.project_path,
args.environment
)
if success:
print("Project quality check passed!")
return 0
else:
print("Project quality check did not pass")
return 1
if name == ‘main’: sys.exit(main())
::: note Best Practices Summary
1. **Gradual Improvement**: Don't introduce all rules at once, gradually raise standards
2. **Tool Integration**: Use Pylint in combination with other tools (black, isort, mypy)
3. **Environment Differentiation**: Set different quality standards for different environments
4. **Automation**: Automatically execute checks through CI/CD and pre-commit hooks
5. **Team Collaboration**: Establish team consensus, regularly review and update quality standards
:::
::: warning Implementation Considerations
1. **Change Management**: Consider change impact when introducing Pylint to large projects
2. **Performance Considerations**: Checking large codebases can be slow, consider incremental checking
3. **Exception Handling**: Set reasonable exception strategies for legacy code
4. **Training Support**: Provide Pylint usage training for team members
:::
Through systematic practical project experience, a complete code quality assurance system can be established to ensure long-term maintainability of projects and team development efficiency.