|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +python-rpm-spec is a Python library for parsing RPM spec files without requiring |
| 6 | +`/usr/bin/rpm` or any RPM tools. It provides read-only access to spec file |
| 7 | +contents and supports all platforms including Windows. |
| 8 | + |
| 9 | +The library uses regex-based parsing to extract information from RPM spec files. |
| 10 | +While not complete, it handles the most common spec file elements. |
| 11 | + |
| 12 | +## Key Commands |
| 13 | + |
| 14 | +### Development Setup |
| 15 | + |
| 16 | +```bash |
| 17 | +python3 -m venv .venv |
| 18 | +source .venv/bin/activate |
| 19 | +pip install -r requirements.txt |
| 20 | +``` |
| 21 | + |
| 22 | +### Testing |
| 23 | + |
| 24 | +```bash |
| 25 | +pytest # Run all tests |
| 26 | +pytest --cov=pyrpm.spec # Run tests with coverage |
| 27 | +pytest tests/test_spec_file_parser.py -k test_name # Run single test |
| 28 | +``` |
| 29 | + |
| 30 | +### Type Checking |
| 31 | + |
| 32 | +```bash |
| 33 | +mypy . # Run type checker (strict mode enabled) |
| 34 | +``` |
| 35 | + |
| 36 | +### Linting |
| 37 | + |
| 38 | +```bash |
| 39 | +pylint --rcfile=.pylintrc pyrpm # Run linter on main module |
| 40 | +``` |
| 41 | + |
| 42 | +### Code Formatting |
| 43 | + |
| 44 | +```bash |
| 45 | +black --line-length 130 . # Format code (line length: 130) |
| 46 | +``` |
| 47 | + |
| 48 | +### Build/Release |
| 49 | + |
| 50 | +```bash |
| 51 | +flit build # Build distribution |
| 52 | +flit publish # Publish to PyPI |
| 53 | +``` |
| 54 | + |
| 55 | +## Architecture |
| 56 | + |
| 57 | +### Core Components |
| 58 | + |
| 59 | +**pyrpm/spec.py** (562 lines) - Single-file implementation containing all |
| 60 | +parsing logic |
| 61 | + |
| 62 | +1. **Tag Parsing System** - Abstract base class `_Tag` with specialized |
| 63 | + subclasses: |
| 64 | + - `_NameValue`: Simple key-value tags (Name, Version, Summary, etc.) |
| 65 | + - `_List`: List-based tags (BuildRequires, Requires, etc.) |
| 66 | + - `_ListAndDict`: Tags that populate both list and dict (Source, Patch) |
| 67 | + - `_SetterMacroDef`: Global and local macro definitions (%global, %define) |
| 68 | + - Parse context tracks current subpackage and multiline states |
| 69 | + |
| 70 | +2. **Main API Classes**: |
| 71 | + - `Spec`: Represents entire spec file, parses via `Spec.from_file()` or |
| 72 | + `Spec.from_string()` |
| 73 | + - `Package`: Represents main package or subpackage (from %package directives) |
| 74 | + - `Requirement`: Parses versioned dependencies with operators (>=, <=, =) |
| 75 | + |
| 76 | +3. **Macro Expansion**: `replace_macros(string, spec)` - Expands RPM macros like |
| 77 | + `%{version}` into actual values |
| 78 | + |
| 79 | +### Parsing Flow |
| 80 | + |
| 81 | +1. `Spec.from_file(filename)` reads spec file line by line |
| 82 | +2. Each line tested against registered `_tags` list (pattern matching) |
| 83 | +3. Matching tag's `update()` method modifies Spec object and parse context |
| 84 | +4. Parse context tracks current subpackage and multiline sections (description, |
| 85 | + changelog) |
| 86 | +5. Macros stored in `spec.macros` dict for later expansion |
| 87 | + |
| 88 | +### Data Access Patterns |
| 89 | + |
| 90 | +- **Simple attributes**: `spec.name`, `spec.version`, `spec.summary` |
| 91 | +- **Lists**: `spec.sources`, `spec.patches`, `spec.build_requires`, |
| 92 | + `spec.requires` |
| 93 | +- **Dictionaries**: `spec.sources_dict["Source0"]`, |
| 94 | + `spec.patches_dict["Patch1"]` |
| 95 | +- **Packages**: `spec.packages` (list) or `spec.packages_dict["package-name"]` |
| 96 | +- **Subpackage attributes**: Each Package can override spec-level attributes |
| 97 | + |
| 98 | +## Type Checking Configuration |
| 99 | + |
| 100 | +mypy is configured with strict mode (mypy.ini): |
| 101 | + |
| 102 | +- `python_version = 3.14` - Target version |
| 103 | +- All strict flags enabled (disallow_untyped_calls, disallow_untyped_defs, etc.) |
| 104 | +- Tests and examples directories have `ignore_errors = True` |
| 105 | + |
| 106 | +## Linting Configuration |
| 107 | + |
| 108 | +pylint (.pylintrc): |
| 109 | + |
| 110 | +- Line length: 130 |
| 111 | +- Disabled checks: missing-docstring, invalid-name, too-few-public-methods, |
| 112 | + no-member, unused-argument, import-outside-toplevel |
| 113 | + |
| 114 | +## Project Constraints |
| 115 | + |
| 116 | +- **Python 3.10+** required (currently supports 3.10-3.14) |
| 117 | +- **No external dependencies** - stdlib only for the library itself |
| 118 | +- **Read-only parsing** - Does not modify or write spec files |
| 119 | +- **Regex-based** - Not a complete formal parser |
| 120 | +- **Incomplete coverage** - Parses common tags but not all spec file features |
| 121 | + (e.g., %include not supported) |
| 122 | + |
| 123 | +## Testing |
| 124 | + |
| 125 | +- Tests in `tests/` directory |
| 126 | +- Test data (sample spec files) in `tests/data/` |
| 127 | +- CI runs on Python 3.10-3.14 across Ubuntu, macOS, and Windows |
| 128 | +- GitHub workflow also tests against Fedora's spec files (fedora-sources.yml) |
0 commit comments