Skip to content

Commit be99971

Browse files
authored
🤖 Config option helper scripts, class (#1187)
1 parent d0ef40e commit be99971

7 files changed

Lines changed: 1249 additions & 27 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
applet/
55
.DS_Store
66
*.sublime-workspace
7+
*.pyc
78

89
# Prerequisites
910
*.d

‎bin/AGENTS.md‎

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Configuration File Helpers
2+
3+
## Overview
4+
5+
Scripts here provide robust helpers to extract data from Marlin configuration files (Configuration.h and Configuration_adv.h).
6+
7+
## The Problem
8+
9+
The MarlinFirmware/Configurations repository uses the `mfconfig` script to manage configuration files. Previously, parsing and extracting data from these files required:
10+
- Manual text parsing with regex
11+
- No type safety or error handling
12+
- Duplicated code across different scripts
13+
- Fragile parsing logic that breaks with file format changes
14+
15+
## The Solution
16+
17+
Comprehensive Python module (`config_helpers.py`) providing:
18+
19+
1. **ConfigParser Class** - Object-oriented parser for configuration files
20+
2. **Helper Functions** - High-level functions for common tasks
21+
3. **Type Hints** - Clear API with proper type annotations
22+
4. **Error Handling** - Robust error handling and fallbacks
23+
5. **Test Suite** - Comprehensive tests demonstrating usage
24+
25+
## Files
26+
27+
### 1. `config_helpers.py`
28+
Main helper module containing:
29+
30+
- **ConfigParser class** with methods:
31+
- `get_defines()` - Extract all #define directives
32+
- `get_define(name)` - Get specific define value
33+
- `get_version()` - Parse configuration version
34+
- `find_section(section_name)` - Locate section in file
35+
- `get_enabled_features()` - List enabled features
36+
- `get_disabled_features()` - List disabled features
37+
- `has_error_directive()` - Check for #error directives
38+
- `get_error_messages()` - Extract error messages
39+
- `get_comments_for_define(name)` - Get associated comments
40+
41+
- **Helper functions**:
42+
- `parse_configuration_file(filepath)` - Parse and extract key info
43+
- `compare_configurations(file1, file2)` - Compare two configs
44+
- `extract_settings_by_category(filepath)` - Group settings by category
45+
46+
### 2. `test_config_helpers.py`
47+
Comprehensive test suite demonstrating:
48+
- Basic parsing
49+
- Feature detection
50+
- Category extraction
51+
- File comparison
52+
- Advanced parsing
53+
54+
### 3. `example_usage.py`
55+
Practical examples showing:
56+
- Checking configuration version
57+
- Extracting printer settings
58+
- Checking enabled features
59+
- Error detection
60+
- File comparison
61+
- Batch parsing
62+
63+
### 4. `README_CONFIG_HELPERS.md`
64+
Complete documentation including:
65+
- Usage examples
66+
- API reference
67+
- Integration guide
68+
- Benefits and requirements
69+
70+
## Integration with Existing Code
71+
72+
### Updated `mfconfig` Script
73+
74+
The `mfconfig` script has been updated to use the new helpers:
75+
76+
```python
77+
try:
78+
from config_helpers import ConfigParser, parse_configuration_file
79+
except ImportError:
80+
ConfigParser = None
81+
82+
# Use ConfigParser if available
83+
if ConfigParser:
84+
parser = ConfigParser(config_file)
85+
defines = parser.get_defines()
86+
# More robust parsing with error handling
87+
```
88+
89+
The `add_path_labels()` function now uses `ConfigParser` for more reliable parsing with fallback to the original method if needed.
90+
91+
## Usage Examples
92+
93+
### Basic Parsing
94+
```python
95+
from config_helpers import ConfigParser
96+
97+
parser = ConfigParser('Configuration.h')
98+
version = parser.get_version()
99+
extruders = parser.get_define('EXTRUDERS')
100+
```
101+
102+
### Feature Detection
103+
```python
104+
enabled = parser.get_enabled_features()
105+
disabled = parser.get_disabled_features()
106+
```
107+
108+
### File Comparison
109+
```python
110+
from config_helpers import compare_configurations
111+
112+
diff = compare_configurations('config1.h', 'config2.h')
113+
print(f"Different values: {len(diff['different_values'])}")
114+
```
115+
116+
### Batch Processing
117+
```python
118+
from config_helpers import parse_configuration_file
119+
120+
for config_file in config_files:
121+
data = parse_configuration_file(config_file)
122+
print(f"{data['filename']}: {len(data['defines'])} defines")
123+
```
124+
125+
## Testing
126+
127+
Run the test suite:
128+
```bash
129+
cd bin
130+
python3 test_config_helpers.py
131+
```
132+
133+
Run the example usage:
134+
```bash
135+
cd bin
136+
python3 example_usage.py
137+
```
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Configuration File Helpers
2+
3+
Helper functions to extract and manipulate data from Marlin configuration files (Configuration.h and Configuration_adv.h).
4+
5+
These utilities provide a reliable way to parse and extract settings from Marlin firmware configuration files, avoiding the need for fragile text parsing or manual inspection.
6+
7+
## Files
8+
9+
- `config_helpers.py` - Main helper module with configuration parsing utilities
10+
- `test_config_helpers.py` - Test suite demonstrating usage of the helpers
11+
- `mfconfig` - Main configuration management script (uses config_helpers)
12+
13+
## Usage
14+
15+
### Basic Example
16+
17+
```python
18+
from pathlib import Path
19+
from config_helpers import ConfigParser, parse_configuration_file
20+
21+
# Parse a configuration file
22+
config_file = Path('path/to/Configuration.h')
23+
parser = ConfigParser(config_file)
24+
25+
# Get version
26+
version = parser.get_version()
27+
print(f"Version: {version}")
28+
29+
# Get specific setting
30+
extruders = parser.get_define('EXTRUDERS')
31+
print(f"Extruders: {extruders}")
32+
33+
# Get all defines
34+
defines = parser.get_defines()
35+
print(f"Total defines: {len(defines)}")
36+
```
37+
38+
### Parse Full Configuration
39+
40+
```python
41+
from config_helpers import parse_configuration_file
42+
43+
data = parse_configuration_file(config_file)
44+
45+
print(f"Version: {data['version']}")
46+
print(f"Total defines: {len(data['defines'])}")
47+
print(f"Enabled features: {len(data['enabled_features'])}")
48+
print(f"Has errors: {data['has_errors']}")
49+
```
50+
51+
### Compare Two Configuration Files
52+
53+
```python
54+
from pathlib import Path
55+
from config_helpers import compare_configurations
56+
57+
file1 = Path('config1/Configuration.h')
58+
file2 = Path('config2/Configuration.h')
59+
60+
diff = compare_configurations(file1, file2)
61+
62+
print(f"Settings only in file1: {len(diff['only_in_file1'])}")
63+
print(f"Settings only in file2: {len(diff['only_in_file2'])}")
64+
print(f"Different values: {len(diff['different_values'])}")
65+
66+
for key, (val1, val2) in diff['different_values'].items():
67+
print(f"{key}: '{val1}' != '{val2}'")
68+
```
69+
70+
### Extract Settings by Category
71+
72+
```python
73+
from config_helpers import extract_settings_by_category
74+
75+
categories = extract_settings_by_category(config_file)
76+
77+
for category, settings in categories.items():
78+
if settings:
79+
print(f"{category}: {len(settings)} settings")
80+
for key, value in list(settings.items())[:5]:
81+
print(f" {key} = {value}")
82+
```
83+
84+
### Feature Detection
85+
86+
```python
87+
from config_helpers import ConfigParser
88+
89+
parser = ConfigParser(config_file)
90+
91+
# Get enabled features
92+
enabled = parser.get_enabled_features()
93+
print(f"Enabled: {enabled}")
94+
95+
# Get disabled features
96+
disabled = parser.get_disabled_features()
97+
print(f"Disabled: {disabled}")
98+
99+
# Check for error directives
100+
if parser.has_error_directive():
101+
errors = parser.get_error_messages()
102+
for msg in errors:
103+
print(f"Error: {msg}")
104+
```
105+
106+
## API Reference
107+
108+
### ConfigParser Class
109+
110+
Main class for parsing configuration files.
111+
112+
#### Methods
113+
114+
- `__init__(filepath: Path)` - Initialize parser with configuration file
115+
- `get_defines() -> Dict[str, str]` - Extract all #define directives
116+
- `get_define(name: str, default: Optional[str] = None) -> Optional[str]` - Get value of specific define
117+
- `get_version() -> Optional[str]` - Get configuration version
118+
- `find_section(section_name: str) -> Optional[Tuple[int, int]]` - Find line range of a section
119+
- `get_enabled_features() -> List[str]` - Get list of enabled features
120+
- `get_disabled_features() -> List[str]` - Get list of disabled features
121+
- `has_error_directive() -> bool` - Check if file contains #error directives
122+
- `get_error_messages() -> List[str]` - Extract all #error messages
123+
- `get_comments_for_define(name: str) -> Optional[str]` - Get comment for a define
124+
125+
### Helper Functions
126+
127+
- `parse_configuration_file(filepath: Path) -> Dict[str, Any]` - Parse file and extract key information
128+
- `compare_configurations(file1: Path, file2: Path) -> Dict[str, Any]` - Compare two configuration files
129+
- `extract_settings_by_category(filepath: Path) -> Dict[str, Dict[str, str]]` - Extract settings grouped by category
130+
131+
## Integration with mfconfig
132+
133+
The `mfconfig` script uses `config_helpers` for robust parsing of configuration files:
134+
135+
```python
136+
try:
137+
from config_helpers import ConfigParser, parse_configuration_file
138+
except ImportError:
139+
ConfigParser = None
140+
141+
# Use ConfigParser if available
142+
if ConfigParser:
143+
parser = ConfigParser(config_file)
144+
defines = parser.get_defines()
145+
# ... process configuration
146+
```
147+
148+
## Benefits
149+
150+
1. **Reliability**: Proper parsing instead of fragile text manipulation
151+
2. **Type Safety**: Clear return types and error handling
152+
3. **Reusability**: Common functionality extracted into reusable functions
153+
4. **Testability**: Easy to write unit tests for parsing logic
154+
5. **Maintainability**: Centralized parsing logic that can be updated in one place
155+
156+
## Testing
157+
158+
Run the test suite:
159+
160+
```bash
161+
cd bin
162+
python3 test_config_helpers.py
163+
```
164+
165+
## Requirements
166+
167+
- Python 3.6+
168+
- No external dependencies (uses only standard library)
169+
170+
## License
171+
172+
Same as the Marlin Firmware Configurations repository (GPLv3)

0 commit comments

Comments
 (0)