CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
201 lines
6.7 KiB
Markdown
201 lines
6.7 KiB
Markdown
---
|
|
name: amdsmi-python-style-guide
|
|
description: "ROCm Python style guide based on TheRock. Use when: writing Python code, reviewing Python PRs, checking Python style, creating Python scripts, type hints, error handling patterns, pathlib usage, argparse CLI design."
|
|
---
|
|
|
|
# Python Style Guide (ROCm / TheRock)
|
|
|
|
Follows PEP 8, enforced by **Ruff** (amd-smi) or **Black** (TheRock) via pre-commit.
|
|
Source: [TheRock python_style_guide.md](https://github.com/ROCm/TheRock/blob/main/docs/development/style_guides/python_style_guide.md)
|
|
|
|
## Core Principles
|
|
|
|
- Fail fast and loud — false positives over silent corruption
|
|
- When in doubt, raise an exception
|
|
|
|
---
|
|
|
|
## Type Hints
|
|
|
|
- Type hints on **all** function signatures
|
|
- Syntax that is compatible with python 3.6.8+
|
|
- **Never** use `Any` except in rare generic code
|
|
- **Never** use `Optional[T]`, `Union[X, Y]`, `List`, `Dict` — use built-in equivalents
|
|
- **Do NOT** use `from __future__ import annotations`
|
|
|
|
### Extract Complex Types
|
|
|
|
- Type appears in multiple signatures → `NamedTuple` or `TypeAlias`
|
|
- Dict/tuple with 3+ fields → `dataclass` or `NamedTuple`
|
|
- If you document what tuple fields mean → use `NamedTuple`
|
|
|
|
```python
|
|
# Good
|
|
class KpackInfo(NamedTuple):
|
|
kpack_path: Path
|
|
size: int
|
|
kernel_count: int
|
|
|
|
# Bad
|
|
def get_info() -> tuple[Path, int, int]: ...
|
|
```
|
|
|
|
### Dataclasses Over Tuples
|
|
|
|
Structured data with multiple fields → `@dataclass` or `NamedTuple`, not raw tuples.
|
|
Tuples OK for: simple pairs `(x, y)`, stdlib unpacking, single-use immediate unpacking.
|
|
|
|
---
|
|
|
|
## Function Calls & Arguments
|
|
|
|
- 3+ parameters or ambiguous types → **use keyword arguments**
|
|
- Multiple booleans → **always** use keyword arguments
|
|
|
|
```python
|
|
# Good
|
|
result = build(amdgpu_family="gfx942", enable_testing=True, use_ccache=False)
|
|
|
|
# Bad
|
|
result = build("gfx942", True, False)
|
|
```
|
|
|
|
---
|
|
|
|
## Code Organization
|
|
|
|
- All imports at **top of file** — inline only for documented circular deps (add comment explaining why)
|
|
- Scripts must have `if __name__ == "__main__":` guard
|
|
- CLI scripts must use `argparse` (not raw `sys.argv`)
|
|
- Access `argparse` attrs directly: `args.foo`, not `getattr(args, "foo", default)`
|
|
- No duplicate code — extract to shared functions
|
|
- No magic numbers or fake estimates
|
|
- Methods < 30 lines, classes < 200 lines (ideally)
|
|
- If a class has 7+ responsibilities → split into focused classes
|
|
- God objects doing everything → multiple focused classes; 100+ line methods → extract helpers
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
| Rule | Detail |
|
|
|------|--------|
|
|
| Fail fast | Never silently continue or produce incomplete results |
|
|
| Specific exceptions | `FileNotFoundError`, `subprocess.CalledProcessError` — not bare `except Exception` |
|
|
| Preserve chains | Always use `from e` when re-raising |
|
|
| Validate results | After critical ops: check file exists, non-empty, expected size |
|
|
| No binutils timeouts | Never `timeout=` on `readelf`, `objcopy`, etc. |
|
|
|
|
```python
|
|
# Good — distinguish error conditions
|
|
try:
|
|
output = subprocess.check_output([readelf, "-S", str(file_path)])
|
|
except subprocess.CalledProcessError as e:
|
|
if e.returncode == 1:
|
|
return False # legitimate "not found"
|
|
raise RuntimeError(f"readelf failed: {e.output}") from e
|
|
except FileNotFoundError as e:
|
|
raise RuntimeError(f"readelf not found: {readelf}") from e
|
|
|
|
# Bad — swallows everything
|
|
try:
|
|
output = subprocess.check_output([readelf, "-S", str(file_path)])
|
|
except Exception:
|
|
return False
|
|
```
|
|
|
|
---
|
|
|
|
## Filesystem & Paths
|
|
|
|
- **`pathlib.Path` everywhere** — no `os.path`, no string concatenation for paths
|
|
- No assumptions about CWD — derive from `Path(__file__).resolve().parent`
|
|
- No hard-coded project paths — use env vars, `tempfile`, or relative to `__file__`
|
|
|
|
```python
|
|
# Good
|
|
THIS_DIR = Path(__file__).resolve().parent
|
|
config = THIS_DIR / "config.json"
|
|
|
|
# Bad
|
|
config = Path("build_tools/config.json") # assumes CWD
|
|
config = Path("/home/user/project/config.json") # hard-coded
|
|
```
|
|
|
|
---
|
|
|
|
## Performance
|
|
|
|
- Compile regexes at **module level**, not inside functions
|
|
- Check cheap conditions before expensive ones (magic bytes before subprocess)
|
|
- Cache expensive computations when called repeatedly
|
|
- Use generators for large datasets
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
- Verify fail-fast behavior with `pytest.raises`
|
|
- Use real temp files over mocks for filesystem operations
|
|
- Mock only external dependencies (network, expensive tools)
|
|
- Integration tests should exercise the full path
|
|
|
|
---
|
|
|
|
## amd-smi Python Conventions
|
|
|
|
### Naming
|
|
- `py-interface/amdsmi_interface.py` mirrors C API names (`amdsmi_get_*`, `amdsmi_set_*`)
|
|
- `amdsmi_cli/` uses `snake_case` methods
|
|
- Private functions/methods: `_` prefix
|
|
|
|
### Project Layout
|
|
| Directory | Purpose |
|
|
|-----------|---------|
|
|
| `py-interface/` | Python bindings — `amdsmi_interface.py`, `amdsmi_wrapper.py`, `amdsmi_exception.py` |
|
|
| `amdsmi_cli/` | CLI tool — `amdsmi_commands.py`, `amdsmi_parser.py`, `amdsmi_helpers.py` |
|
|
|
|
### Critical Python Files (high churn — review carefully)
|
|
- `amdsmi_cli/amdsmi_commands.py` — CLI behavior regressions, output format changes
|
|
- `py-interface/amdsmi_interface.py` — must stay in sync with C header `amdsmi.h`
|
|
- `py-interface/amdsmi_wrapper.py` — generated bindings + library loader (see loader rules below)
|
|
- `amdsmi_cli/amdsmi_parser.py` — argument parsing
|
|
|
|
### Library Loader Rules (`amdsmi_wrapper.py`)
|
|
Changes to `_detect_install_context`, `_build_candidate_paths`, or `_load_library` are **❌ BLOCKING** if they break system or pip install context. Verify:
|
|
- `Path(__file__).resolve()` used correctly
|
|
- pip detection logic intact
|
|
- `_libraries['libamd_smi.so']` key preserved
|
|
- Sync with `tools/generator.py`
|
|
|
|
### Python Testing
|
|
- Tests must work with **both** system-installed and pip-installed amdsmi
|
|
- CLI tests live in `amdsmi_cli/`
|
|
- Verify fail-fast behavior with `pytest.raises`
|
|
- Use real temp files over mocks for filesystem operations
|
|
- Mock only external dependencies (network, expensive tools)
|
|
- Integration tests should exercise the full path
|
|
|
|
---
|
|
|
|
## Review Checklist
|
|
|
|
When reviewing Python code, verify:
|
|
|
|
- [ ] Type hints on all functions — modern syntax, no `Any`
|
|
- [ ] No `from __future__ import annotations`
|
|
- [ ] Complex types extracted to `NamedTuple`/`dataclass`
|
|
- [ ] Specific exception handling — no bare `except Exception`
|
|
- [ ] Fail-fast — no silent `continue` on errors
|
|
- [ ] `from e` on all re-raises
|
|
- [ ] `pathlib.Path` for all filesystem ops
|
|
- [ ] No CWD assumptions, no hard-coded paths
|
|
- [ ] No magic numbers or fake estimates
|
|
- [ ] No duplicate code
|
|
- [ ] `argparse` for CLI, `__main__` guard for scripts
|
|
- [ ] Keyword args for complex function calls
|
|
- [ ] All imports at top (inline only with circular-dep comment)
|
|
- [ ] No timeouts on binutils
|
|
- [ ] Output validation after critical operations
|
|
- [ ] Methods < 30 lines, classes < 200 lines
|