precisionai-pace 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- precisionai_pace-0.0.1/.coveragerc-adapters +20 -0
- precisionai_pace-0.0.1/.gitattributes +12 -0
- precisionai_pace-0.0.1/.gitignore +135 -0
- precisionai_pace-0.0.1/.pre-commit-config.yaml +47 -0
- precisionai_pace-0.0.1/CHANGELOG.md +28 -0
- precisionai_pace-0.0.1/CLAUDE.md +293 -0
- precisionai_pace-0.0.1/CODE_OF_CONDUCT.md +47 -0
- precisionai_pace-0.0.1/CONTRIBUTING.md +79 -0
- precisionai_pace-0.0.1/LICENSE.md +201 -0
- precisionai_pace-0.0.1/MANIFEST.in +11 -0
- precisionai_pace-0.0.1/PKG-INFO +404 -0
- precisionai_pace-0.0.1/README.md +154 -0
- precisionai_pace-0.0.1/SECURITY.md +60 -0
- precisionai_pace-0.0.1/precisionai/__init__.py +2 -0
- precisionai_pace-0.0.1/precisionai/pace/__init__.py +52 -0
- precisionai_pace-0.0.1/precisionai/pace/_adapters.py +166 -0
- precisionai_pace-0.0.1/precisionai/pace/_env.py +208 -0
- precisionai_pace-0.0.1/precisionai/pace/_render.py +234 -0
- precisionai_pace-0.0.1/precisionai/pace/capture.py +78 -0
- precisionai_pace-0.0.1/precisionai/pace/core.py +342 -0
- precisionai_pace-0.0.1/precisionai/pace/forecast.py +759 -0
- precisionai_pace-0.0.1/precisionai/pace/lightning.py +160 -0
- precisionai_pace-0.0.1/precisionai/pace/protocol.py +584 -0
- precisionai_pace-0.0.1/precisionai/pace/scopes.py +600 -0
- precisionai_pace-0.0.1/precisionai/pace/transformers.py +125 -0
- precisionai_pace-0.0.1/precisionai/pace/version.py +31 -0
- precisionai_pace-0.0.1/precisionai_pace.egg-info/PKG-INFO +404 -0
- precisionai_pace-0.0.1/precisionai_pace.egg-info/SOURCES.txt +39 -0
- precisionai_pace-0.0.1/precisionai_pace.egg-info/dependency_links.txt +1 -0
- precisionai_pace-0.0.1/precisionai_pace.egg-info/requires.txt +25 -0
- precisionai_pace-0.0.1/precisionai_pace.egg-info/scm_file_list.json +97 -0
- precisionai_pace-0.0.1/precisionai_pace.egg-info/scm_version.json +8 -0
- precisionai_pace-0.0.1/precisionai_pace.egg-info/top_level.txt +1 -0
- precisionai_pace-0.0.1/pyproject.toml +178 -0
- precisionai_pace-0.0.1/requirements-dev.txt +18 -0
- precisionai_pace-0.0.1/requirements.txt +3 -0
- precisionai_pace-0.0.1/setup.cfg +4 -0
- precisionai_pace-0.0.1/skills/README.md +49 -0
- precisionai_pace-0.0.1/skills/adopt-pace/SKILL.md +255 -0
- precisionai_pace-0.0.1/skills/adopt-pace/patterns.md +383 -0
- precisionai_pace-0.0.1/skills/adopt-pace/verify.py +470 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Coverage for the framework adapters only, used by the `adapters` job in
|
|
2
|
+
# .github/workflows/unit-test.yml. The core matrix runs without Lightning or
|
|
3
|
+
# Transformers installed, so pyproject.toml leaves these two modules out of its
|
|
4
|
+
# coverage run and this file measures them where the frameworks are present.
|
|
5
|
+
[run]
|
|
6
|
+
include =
|
|
7
|
+
*/precisionai/pace/lightning.py
|
|
8
|
+
*/precisionai/pace/transformers.py
|
|
9
|
+
branch = true
|
|
10
|
+
relative_files = true
|
|
11
|
+
|
|
12
|
+
[report]
|
|
13
|
+
fail_under = 90
|
|
14
|
+
show_missing = true
|
|
15
|
+
exclude_lines =
|
|
16
|
+
pragma: no cover
|
|
17
|
+
if __name__ == .__main__.:
|
|
18
|
+
raise ImportError
|
|
19
|
+
except ImportError
|
|
20
|
+
if TYPE_CHECKING:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Normalize line endings to LF on commit, regardless of editor/OS settings.
|
|
2
|
+
* text=auto eol=lf
|
|
3
|
+
|
|
4
|
+
# Treat binary files as binary (no line-ending conversion).
|
|
5
|
+
*.png binary
|
|
6
|
+
*.jpg binary
|
|
7
|
+
*.jpeg binary
|
|
8
|
+
*.JPG binary
|
|
9
|
+
*.JPEG binary
|
|
10
|
+
*.svg binary
|
|
11
|
+
*.pdf binary
|
|
12
|
+
*.ipynb text eol=lf
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Virtual environments
|
|
2
|
+
.venv/
|
|
3
|
+
env/
|
|
4
|
+
venv/
|
|
5
|
+
ENV/
|
|
6
|
+
env.bak/
|
|
7
|
+
venv.bak/
|
|
8
|
+
|
|
9
|
+
# Byte-compiled / optimized / DLL files
|
|
10
|
+
__pycache__/
|
|
11
|
+
*.py[cod]
|
|
12
|
+
*$py.class
|
|
13
|
+
|
|
14
|
+
# C extensions
|
|
15
|
+
*.so
|
|
16
|
+
|
|
17
|
+
# Distribution / packaging
|
|
18
|
+
.Python
|
|
19
|
+
build/
|
|
20
|
+
develop-eggs/
|
|
21
|
+
dist/
|
|
22
|
+
downloads/
|
|
23
|
+
eggs/
|
|
24
|
+
.eggs/
|
|
25
|
+
lib/
|
|
26
|
+
lib64/
|
|
27
|
+
parts/
|
|
28
|
+
sdist/
|
|
29
|
+
var/
|
|
30
|
+
wheels/
|
|
31
|
+
share/python-wheels/
|
|
32
|
+
*.egg-info/
|
|
33
|
+
.installed.cfg
|
|
34
|
+
*.egg
|
|
35
|
+
MANIFEST
|
|
36
|
+
|
|
37
|
+
# PyInstaller
|
|
38
|
+
*.manifest
|
|
39
|
+
*.spec
|
|
40
|
+
|
|
41
|
+
# Installer logs
|
|
42
|
+
pip-log.txt
|
|
43
|
+
pip-delete-this-directory.txt
|
|
44
|
+
|
|
45
|
+
# Unit test / coverage reports
|
|
46
|
+
htmlcov/
|
|
47
|
+
.tox/
|
|
48
|
+
.nox/
|
|
49
|
+
.coverage
|
|
50
|
+
.coverage.*
|
|
51
|
+
.cache
|
|
52
|
+
nosetests.xml
|
|
53
|
+
coverage.xml
|
|
54
|
+
*.cover
|
|
55
|
+
*.py,cover
|
|
56
|
+
.hypothesis/
|
|
57
|
+
.pytest_cache/
|
|
58
|
+
cover/
|
|
59
|
+
|
|
60
|
+
# Sphinx documentation
|
|
61
|
+
docs/_build/
|
|
62
|
+
docs/readme.md
|
|
63
|
+
|
|
64
|
+
# Jupyter Notebook
|
|
65
|
+
.ipynb_checkpoints
|
|
66
|
+
|
|
67
|
+
# IPython
|
|
68
|
+
profile_default/
|
|
69
|
+
ipython_config.py
|
|
70
|
+
|
|
71
|
+
# pyenv
|
|
72
|
+
.python-version
|
|
73
|
+
|
|
74
|
+
# pipenv
|
|
75
|
+
Pipfile.lock
|
|
76
|
+
|
|
77
|
+
# pdm
|
|
78
|
+
.pdm.toml
|
|
79
|
+
|
|
80
|
+
# PEP 582
|
|
81
|
+
__pypackages__/
|
|
82
|
+
|
|
83
|
+
# mypy
|
|
84
|
+
.mypy_cache/
|
|
85
|
+
.dmypy.json
|
|
86
|
+
dmypy.json
|
|
87
|
+
|
|
88
|
+
# Pyre type checker
|
|
89
|
+
.pyre/
|
|
90
|
+
|
|
91
|
+
# pytype static type analyzer
|
|
92
|
+
.pytype/
|
|
93
|
+
|
|
94
|
+
# Cython debug symbols
|
|
95
|
+
cython_debug/
|
|
96
|
+
|
|
97
|
+
# PyCharm
|
|
98
|
+
.idea/
|
|
99
|
+
|
|
100
|
+
# VS Code
|
|
101
|
+
.vscode/
|
|
102
|
+
|
|
103
|
+
# PyTorch / Machine Learning artefacts
|
|
104
|
+
*.pth
|
|
105
|
+
*.pt
|
|
106
|
+
*.ckpt
|
|
107
|
+
*.pkl
|
|
108
|
+
*.h5
|
|
109
|
+
*.hdf5
|
|
110
|
+
*.onnx
|
|
111
|
+
*.pb
|
|
112
|
+
*.tflite
|
|
113
|
+
*.tfl
|
|
114
|
+
checkpoints/
|
|
115
|
+
models/
|
|
116
|
+
weights/
|
|
117
|
+
wandb/
|
|
118
|
+
mlruns/
|
|
119
|
+
.neptune/
|
|
120
|
+
|
|
121
|
+
# OS files
|
|
122
|
+
.DS_Store
|
|
123
|
+
.DS_Store?
|
|
124
|
+
._*
|
|
125
|
+
.Spotlight-V100
|
|
126
|
+
.Trashes
|
|
127
|
+
ehthumbs.db
|
|
128
|
+
Thumbs.db
|
|
129
|
+
*~
|
|
130
|
+
|
|
131
|
+
# Temporary files
|
|
132
|
+
*.tmp
|
|
133
|
+
*.temp
|
|
134
|
+
*.swp
|
|
135
|
+
*.swo
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
# ── File hygiene ────────────────────────────────────────────────────────────
|
|
3
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
4
|
+
rev: v6.0.0
|
|
5
|
+
hooks:
|
|
6
|
+
- id: check-added-large-files
|
|
7
|
+
args: ["--maxkb=1800"]
|
|
8
|
+
- id: check-yaml
|
|
9
|
+
- id: check-json
|
|
10
|
+
- id: check-toml
|
|
11
|
+
- id: end-of-file-fixer
|
|
12
|
+
exclude: ^\.github/badges/
|
|
13
|
+
- id: trailing-whitespace
|
|
14
|
+
exclude: ^\.github/badges/
|
|
15
|
+
- id: check-merge-conflict
|
|
16
|
+
- id: detect-private-key
|
|
17
|
+
- id: debug-statements
|
|
18
|
+
- id: fix-byte-order-marker
|
|
19
|
+
|
|
20
|
+
# ── Ruff: lint + format + import sorting ───────────────────────────────────
|
|
21
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
22
|
+
rev: v0.15.17
|
|
23
|
+
hooks:
|
|
24
|
+
- id: ruff
|
|
25
|
+
args: ["--fix"]
|
|
26
|
+
- id: ruff-format
|
|
27
|
+
|
|
28
|
+
# ── Type checking (pyright / pylance engine) ────────────────────────────────
|
|
29
|
+
- repo: local
|
|
30
|
+
hooks:
|
|
31
|
+
- id: pyright
|
|
32
|
+
name: pyright
|
|
33
|
+
entry: python -m pyright
|
|
34
|
+
language: system
|
|
35
|
+
types: [python]
|
|
36
|
+
pass_filenames: false
|
|
37
|
+
|
|
38
|
+
# ── Tests + coverage (≥ 90%) ────────────────────────────────────────────────
|
|
39
|
+
- repo: local
|
|
40
|
+
hooks:
|
|
41
|
+
- id: pytest
|
|
42
|
+
name: pytest (coverage ≥ 90%)
|
|
43
|
+
entry: python -m pytest
|
|
44
|
+
language: system
|
|
45
|
+
args: ["tests/", "-q", "--tb=short", "--no-header"]
|
|
46
|
+
pass_filenames: false
|
|
47
|
+
always_run: true
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.0.1] - 2026-11-03
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Package scaffold for `precisionai-pace`, with no runtime dependencies and support for Python 3.8 and later.
|
|
15
|
+
- Design document for the logger, the progress line format and the completion-forecast rules.
|
|
16
|
+
- `logger` with `debug`, `info`, `success`, `warning`, `error`, `critical` and `exception`, writing to stdout by default, falling back to plain ASCII on terminals that cannot show Unicode, and never raising into your code.
|
|
17
|
+
- Progress reporting with `track`, `progress`, `timed`, `phase` and `plan`, written as readable lines that also carry a `[pace:1 ...]` trailer a scheduler can read.
|
|
18
|
+
- A live progress bar on terminals, and rate-limited progress lines everywhere else, so a long run writes a few lines a minute however many steps it has.
|
|
19
|
+
- `capture_logging()` to route the standard library's `logging` (and so PyTorch's and Transformers') through Pace.
|
|
20
|
+
- `parse_line` and `LineSplitter` for programs that read Pace output, with shared conformance fixtures.
|
|
21
|
+
- Time-remaining estimates (`ScopeForecast`, `JobForecast`) shared by the terminal display and by schedulers.
|
|
22
|
+
- Callbacks for PyTorch Lightning (`precisionai.pace.lightning.PaceCallback`) and Hugging Face Transformers and TRL (`precisionai.pace.transformers.PaceCallback`), installed with the `[lightning]`, `[transformers]` or `[all]` extras.
|
|
23
|
+
- `skills/adopt-pace/`, a Claude Code skill that adds Pace to a repository and checks the result with `verify.py`.
|
|
24
|
+
- Runnable examples, one per style of logging and progress (plain loops, epochs, concurrent work, phases and a plan, resuming, unknown totals, nested scopes, `logging` capture, distributed ranks, and reading the output).
|
|
25
|
+
- User documentation: getting started, logging, progress, framework integrations, migration guides (`print`, `tqdm`, Loguru, `logging`), configuration, a guide for programs that read Pace output, and a FAQ.
|
|
26
|
+
|
|
27
|
+
[Unreleased]: https://github.com/Precision-AI-Inc/pace/compare/v0.0.1...HEAD
|
|
28
|
+
[0.0.1]: https://github.com/Precision-AI-Inc/pace/releases/tag/v0.0.1
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Pace — Project Standards
|
|
2
|
+
|
|
3
|
+
Apply these standards when writing, reviewing, or refactoring code in Pace. Read [docs/design.md](docs/design.md) before changing behaviour: it is the specification for the logger, the progress line format and the forecasting rules.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Environment
|
|
8
|
+
|
|
9
|
+
- Python 3.8+ is supported; develop on 3.9+ (pre-commit needs it), managed with `python -m venv .venv` (never conda)
|
|
10
|
+
- Install: `pip install -e ".[dev]"` → installs all deps including pre-commit
|
|
11
|
+
- Register hooks once: `pre-commit install`
|
|
12
|
+
- CI runs the tests on 3.8 through 3.13. 3.8 runs on `ubuntu-22.04`, because `actions/setup-python` has no 3.8 build for `ubuntu-latest`.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## The rules that matter most
|
|
17
|
+
|
|
18
|
+
1. **The core has no runtime dependencies.** `precisionai.pace` imports only the standard library. It is installed into users' training images and into other packages, and must never add to them. A framework adapter imports its framework only inside its own module, behind an extra (`[lightning]`, `[transformers]`).
|
|
19
|
+
2. **The line format is a public contract.** Anything a consumer parses (the `[pace:N ...]` trailer) changes only as documented in `docs/design.md`: new optional keys within a major version, anything else is a new major version. A format change updates `docs/design.md` and the fixtures in `tests/fixtures/protocol/` in the same PR. The readable text before the trailer is for people and may change freely; never make a consumer depend on it.
|
|
20
|
+
3. **Python 3.8 compatible.** See [Type hints](#type-hints) for what that rules out.
|
|
21
|
+
4. **Never log secrets on the package's own initiative.** Pace never writes environment variables, configuration or credentials by itself, and labels stay short and are not repeated on every update.
|
|
22
|
+
5. **Parsing is defensive.** Log lines are untrusted input. The parser bounds every size and value, never evaluates anything, and ignores (never raises on) a malformed or unsupported line.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Project layout
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
precisionai/pace/
|
|
30
|
+
core.py, _env.py, _render.py, capture.py # the logger
|
|
31
|
+
scopes.py # track, progress, timed, phase, plan
|
|
32
|
+
protocol.py # Record, the [pace:1 ...] trailer, parse_line, LineSplitter
|
|
33
|
+
forecast.py # ScopeForecast, JobForecast
|
|
34
|
+
lightning.py, transformers.py, _adapters.py # framework callbacks (extras only)
|
|
35
|
+
version.py # installed package version
|
|
36
|
+
__init__.py # re-exports only — no logic
|
|
37
|
+
docs/ # Sphinx (HTML + LaTeX/PDF); design.md is the specification
|
|
38
|
+
skills/adopt-pace/ # Claude Code skill that adds Pace to a repo; verify.py checks a log
|
|
39
|
+
examples/ # runnable examples
|
|
40
|
+
tests/ # mirrors package structure; fixtures/protocol/ holds format conformance cases
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`precisionai` is a fixed, namespace-only top-level package (no logic, just an SPDX header) shared by Precision AI Python projects, which is what makes `precisionai.<namespace>.*` imports consistent across them.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## pyproject.toml — canonical config
|
|
48
|
+
|
|
49
|
+
```toml
|
|
50
|
+
[project]
|
|
51
|
+
dynamic = ["version"] # version comes from the git tag via setuptools-scm, never hardcoded
|
|
52
|
+
requires-python = ">=3.8"
|
|
53
|
+
dependencies = [] # see "The rules that matter most"
|
|
54
|
+
|
|
55
|
+
[tool.setuptools_scm]
|
|
56
|
+
version_scheme = "no-guess-dev"
|
|
57
|
+
local_scheme = "no-local-version"
|
|
58
|
+
fallback_version = "0.0.0"
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 120
|
|
62
|
+
target-version = "py38"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["E","W","F","I","UP","B","SIM","N","C90","D","PT","RUF","PL","ANN","PERF","S","FA"]
|
|
66
|
+
ignore = [
|
|
67
|
+
"E501", # enforced by ruff-format
|
|
68
|
+
"SIM108", # ternary readability
|
|
69
|
+
"D100","D104", # module/package docstrings optional
|
|
70
|
+
"D203","D213", # pydocstyle conflicts — always ignore these two
|
|
71
|
+
"PLR0913","PLR2004", # arg count (progress scopes take several optional fields) + magic values in tests
|
|
72
|
+
"ANN401", # Any is allowed for genuinely dynamic types
|
|
73
|
+
]
|
|
74
|
+
[tool.ruff.lint.per-file-ignores]
|
|
75
|
+
"**/__init__.py" = ["F401"]
|
|
76
|
+
"tests/**" = ["D","PLR","ANN","S"]
|
|
77
|
+
"docs/conf.py" = ["E402","UP031","ANN","S","FA"]
|
|
78
|
+
|
|
79
|
+
[tool.ruff.lint.pydocstyle]
|
|
80
|
+
convention = "numpy" # enforces NumPy docstring style
|
|
81
|
+
|
|
82
|
+
[tool.ruff.lint.mccabe]
|
|
83
|
+
max-complexity = 10
|
|
84
|
+
|
|
85
|
+
[tool.ruff.lint.isort]
|
|
86
|
+
known-first-party = ["precisionai"]
|
|
87
|
+
|
|
88
|
+
[tool.ruff.format]
|
|
89
|
+
quote-style = "double"
|
|
90
|
+
indent-style = "space"
|
|
91
|
+
docstring-code-format = true # formats code blocks inside docstrings
|
|
92
|
+
|
|
93
|
+
[tool.pytest.ini_options]
|
|
94
|
+
addopts = "--cov=precisionai --cov-report=term-missing --cov-fail-under=90"
|
|
95
|
+
|
|
96
|
+
[tool.coverage.report]
|
|
97
|
+
fail_under = 90
|
|
98
|
+
exclude_lines = ["pragma: no cover","if __name__ == .__main__.:",
|
|
99
|
+
"raise ImportError","except ImportError","if TYPE_CHECKING:"]
|
|
100
|
+
|
|
101
|
+
[tool.pyright]
|
|
102
|
+
pythonVersion = "3.8"
|
|
103
|
+
typeCheckingMode = "standard"
|
|
104
|
+
reportMissingImports = false
|
|
105
|
+
reportMissingModuleSource = false
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## pre-commit
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
repos:
|
|
114
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
115
|
+
rev: v6.0.0
|
|
116
|
+
hooks: [check-added-large-files (--maxkb=1800), check-yaml, check-json,
|
|
117
|
+
check-toml, end-of-file-fixer, trailing-whitespace,
|
|
118
|
+
check-merge-conflict, detect-private-key, debug-statements,
|
|
119
|
+
fix-byte-order-marker]
|
|
120
|
+
|
|
121
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
122
|
+
rev: v0.15.17
|
|
123
|
+
hooks: [ruff (--fix), ruff-format]
|
|
124
|
+
|
|
125
|
+
- repo: local
|
|
126
|
+
hooks:
|
|
127
|
+
- id: pyright
|
|
128
|
+
name: pyright
|
|
129
|
+
entry: python -m pyright
|
|
130
|
+
language: system
|
|
131
|
+
types: [python]
|
|
132
|
+
pass_filenames: false
|
|
133
|
+
|
|
134
|
+
- repo: local
|
|
135
|
+
hooks:
|
|
136
|
+
- id: pytest
|
|
137
|
+
entry: python -m pytest
|
|
138
|
+
language: system
|
|
139
|
+
args: [tests/, -q, --tb=short, --no-header]
|
|
140
|
+
always_run: true
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Code style
|
|
146
|
+
|
|
147
|
+
### License header
|
|
148
|
+
- Every `.py` file starts with a two-line SPDX header, followed by a blank line before the module docstring (if any):
|
|
149
|
+
```python
|
|
150
|
+
# Copyright 2026 Precision AI
|
|
151
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
152
|
+
```
|
|
153
|
+
- No "CONFIDENTIAL" or proprietary banners — this codebase is Apache 2.0 licensed; any such banner is a bug.
|
|
154
|
+
|
|
155
|
+
### Imports
|
|
156
|
+
- All imports at the **top of the file** — never inside functions
|
|
157
|
+
- Optional framework deps (adapters only): module-level `try/except ImportError` with a flag variable
|
|
158
|
+
- No silent fallbacks unless mathematically equivalent (document why)
|
|
159
|
+
- `__init__.py` re-exports only — no logic, no comments between import blocks
|
|
160
|
+
- `__all__` must be **alphabetically sorted**
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# optional dep pattern (adapter modules only — never in the core)
|
|
164
|
+
try:
|
|
165
|
+
import lightning.pytorch as pl # type: ignore[import]
|
|
166
|
+
_LIGHTNING_AVAILABLE = True
|
|
167
|
+
except ImportError:
|
|
168
|
+
_LIGHTNING_AVAILABLE = False
|
|
169
|
+
|
|
170
|
+
# inside the class/function that needs it
|
|
171
|
+
if not _LIGHTNING_AVAILABLE:
|
|
172
|
+
raise ImportError("lightning is required: pip install precisionai-pace[lightning]") from None
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Docstrings
|
|
176
|
+
- **NumPy style** on all public functions, classes, and modules
|
|
177
|
+
- Module docstrings: plain English, no prefixes (no P0/P1/P2, no "original", no labels)
|
|
178
|
+
- One-line summary in **imperative mood** ("Compute …", "Return …", "Save …")
|
|
179
|
+
- Blank line between summary and extended description (D205)
|
|
180
|
+
|
|
181
|
+
### Type hints
|
|
182
|
+
- Required on **all** function signatures (public and private) — enforced by ruff (`ANN`) and pyright
|
|
183
|
+
- Every module with annotations starts with `from __future__ import annotations` (enforced by ruff `FA`). With it, `X | Y` and `list[int]` are fine **in annotations** on 3.8.
|
|
184
|
+
- **Outside annotations**, code runs on 3.8: no `X | Y` in `isinstance`, casts or type aliases evaluated at runtime, no `match`, no `str.removeprefix`, no parenthesised context managers, no `dataclass(slots=True)` or `kw_only`. Use `isinstance(x, (A, B))`, `typing.Union`/`Optional` in runtime aliases, and `typing.TYPE_CHECKING` for import-only types.
|
|
185
|
+
- Use `Any` from `typing` for genuinely dynamic types — don't use `object` when methods will be called on it
|
|
186
|
+
- pyright config lives in `[tool.pyright]` in `pyproject.toml` — never pass type-checker flags inline
|
|
187
|
+
|
|
188
|
+
### Comments
|
|
189
|
+
- Only when the **why** is non-obvious
|
|
190
|
+
- No section-divider comments that describe what the code already says
|
|
191
|
+
- No cryptic labels, no TODO/FIXME without a ticket reference
|
|
192
|
+
|
|
193
|
+
### Exception handling
|
|
194
|
+
- Always `raise X from err` or `raise X from None` inside `except` blocks (B904)
|
|
195
|
+
- Logging and progress calls never raise into user code because of a formatting or I/O problem; they degrade (drop the record, fall back to plain text) instead
|
|
196
|
+
|
|
197
|
+
### General
|
|
198
|
+
- `len()` returns `int` — never `int(len(...))`
|
|
199
|
+
- Loop variables not used in the body → rename to `_`
|
|
200
|
+
- `assert a and b` in tests → split into separate asserts
|
|
201
|
+
- `@pytest.fixture` not `@pytest.fixture()`
|
|
202
|
+
- `pytest.raises` always includes `match=` parameter
|
|
203
|
+
- No `# type: ignore[attr-defined]` when `Any` already covers the attribute
|
|
204
|
+
- Rates and intervals use `time.monotonic()`; wall-clock time is only for the printed timestamp
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## README is the PyPI page
|
|
209
|
+
|
|
210
|
+
`README.md` is published as the PyPI project description, where relative paths do not resolve. Every link and image in it is an absolute `https://github.com/Precision-AI-Inc/pace/blob/main/...` or `https://raw.githubusercontent.com/Precision-AI-Inc/pace/main/...` URL (enforced by `tests/test_readme.py`). Those URLs only load for anonymous visitors once the repository is public.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Docs (Sphinx)
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
docs/
|
|
218
|
+
conf.py # version from git tag, logo, LaTeX/fancyhdr, enumitem fix
|
|
219
|
+
index.rst # toctrees: user guide, reading output, reference
|
|
220
|
+
design.md # the specification — update it with any behaviour or format change
|
|
221
|
+
modules.rst # autodoc for all public submodules
|
|
222
|
+
requirements.txt # sphinx, sphinx-rtd-theme, myst-parser, sphinx-autodoc-typehints
|
|
223
|
+
Makefile # make html | make latexpdf | make clean
|
|
224
|
+
assets/logo.png
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
- `conf.py` copies root `README.md` → `docs/readme.md` at build time
|
|
228
|
+
- Version read from `git describe --tags --exact-match`, falls back to `0.0.0`
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Testing
|
|
233
|
+
|
|
234
|
+
- Mirror package structure: `tests/test_<module>.py`
|
|
235
|
+
- Shared fixtures in `tests/conftest.py`
|
|
236
|
+
- 90% coverage hard minimum — enforced by pytest and pre-commit
|
|
237
|
+
- Format conformance cases live in `tests/fixtures/protocol/` as input lines plus expected parse results; consumers run the same files
|
|
238
|
+
- Tests that depend on time use an injected clock, never `sleep`
|
|
239
|
+
- Integration tests marked `@pytest.mark.integration` and excluded from default runs
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Repository files
|
|
244
|
+
|
|
245
|
+
Besides the source layout above, the repository carries:
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
.gitattributes # normalize line endings to LF; declare binary assets
|
|
249
|
+
CHANGELOG.md # Keep a Changelog format + Semantic Versioning
|
|
250
|
+
CODE_OF_CONDUCT.md # Contributor Covenant v2.1
|
|
251
|
+
MANIFEST.in # sdist packaging — include docs/license/changelog, exclude tests/examples
|
|
252
|
+
.github/
|
|
253
|
+
dependabot.yml # weekly pip + github-actions update checks
|
|
254
|
+
ISSUE_TEMPLATE/
|
|
255
|
+
bug_report.yml
|
|
256
|
+
feature_request.yml
|
|
257
|
+
config.yml # blank_issues_enabled: false + link to SECURITY.md
|
|
258
|
+
PULL_REQUEST_TEMPLATE.md
|
|
259
|
+
workflows/
|
|
260
|
+
ci.yml # dispatches to pre-commit.yml + unit-test.yml
|
|
261
|
+
pre-commit.yml
|
|
262
|
+
unit-test.yml
|
|
263
|
+
release.yml # tag push → build, PyPI publish, GitHub Release
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Releasing
|
|
267
|
+
|
|
268
|
+
- Versions are **never hardcoded** — `setuptools-scm` derives the package version from the current git tag (see the `pyproject.toml` block above). Tag with `vX.Y.Z`.
|
|
269
|
+
- Update `CHANGELOG.md` under `[Unreleased]` as you go; move those entries to a new `## [X.Y.Z] - YYYY-MM-DD` section when cutting a release.
|
|
270
|
+
- Pushing a `v*.*.*` tag triggers `.github/workflows/release.yml`: runs the full test matrix, builds the sdist/wheel, verifies the built version matches the tag, publishes to PyPI via trusted publishing (`id-token: write`, no stored API tokens), and creates a GitHub Release with generated notes.
|
|
271
|
+
- CI checkout steps that build from git history (release builds, docs version detection) must use `fetch-depth: 0` — a shallow checkout has no tags for `setuptools-scm` or `git describe` to find.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## What to avoid
|
|
276
|
+
|
|
277
|
+
| Pattern | Instead |
|
|
278
|
+
|---|---|
|
|
279
|
+
| A new entry in `dependencies` | Put it behind an extra, imported only by the adapter module |
|
|
280
|
+
| Parsing the readable part of a progress line | Parse only the `[pace:N ...]` trailer |
|
|
281
|
+
| Changing or removing a trailer key in place | New optional key, or a new major version (see `docs/design.md`) |
|
|
282
|
+
| `isinstance(x, A \| B)`, `match`, other 3.10+ runtime syntax | `isinstance(x, (A, B))` and 3.8-compatible equivalents |
|
|
283
|
+
| `time.time()` for rates or intervals | `time.monotonic()` |
|
|
284
|
+
| `int(len(x))` | `len(x)` |
|
|
285
|
+
| `assert a and b` (tests) | two separate asserts |
|
|
286
|
+
| `@pytest.fixture()` | `@pytest.fixture` |
|
|
287
|
+
| `pytest.raises(ValueError)` | `pytest.raises(ValueError, match="…")` |
|
|
288
|
+
| `raise X` inside except | `raise X from None` or `raise X from err` |
|
|
289
|
+
| Deferred imports inside functions | Module-level try/except with flag |
|
|
290
|
+
| Cryptic prefixes (P0–P4, "original") | Plain descriptive names |
|
|
291
|
+
| `# type: ignore[attr-defined]` on `Any` | Remove — redundant |
|
|
292
|
+
| `# noqa` suppression | Fix the underlying issue |
|
|
293
|
+
| Silent fallback for optional dep | Raise `ImportError` with install hint |
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
|
6
|
+
|
|
7
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
|
8
|
+
|
|
9
|
+
## Our Standards
|
|
10
|
+
|
|
11
|
+
Examples of behavior that contributes to a positive environment for our community include:
|
|
12
|
+
|
|
13
|
+
- Demonstrating empathy and kindness toward other people
|
|
14
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
15
|
+
- Giving and gracefully accepting constructive feedback
|
|
16
|
+
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
|
17
|
+
- Focusing on what is best not just for us as individuals, but for the overall community
|
|
18
|
+
|
|
19
|
+
Examples of unacceptable behavior include:
|
|
20
|
+
|
|
21
|
+
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
|
22
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
23
|
+
- Public or private harassment
|
|
24
|
+
- Publishing others' private information, such as a physical or email address, without their explicit permission
|
|
25
|
+
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
26
|
+
|
|
27
|
+
## Enforcement Responsibilities
|
|
28
|
+
|
|
29
|
+
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
|
30
|
+
|
|
31
|
+
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
|
32
|
+
|
|
33
|
+
## Scope
|
|
34
|
+
|
|
35
|
+
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces.
|
|
36
|
+
|
|
37
|
+
## Enforcement
|
|
38
|
+
|
|
39
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at michael@precision.ai. All complaints will be reviewed and investigated promptly and fairly.
|
|
40
|
+
|
|
41
|
+
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
|
42
|
+
|
|
43
|
+
## Attribution
|
|
44
|
+
|
|
45
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1.
|
|
46
|
+
|
|
47
|
+
[homepage]: https://www.contributor-covenant.org
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to Pace.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
Requires Python 3.8+ to use, and Python 3.9+ to run the pre-commit hooks.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone <repo-url>
|
|
11
|
+
cd pace
|
|
12
|
+
|
|
13
|
+
python -m venv .venv
|
|
14
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
15
|
+
|
|
16
|
+
pip install -e ".[dev]"
|
|
17
|
+
pre-commit install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`pip install -e ".[dev]"` installs all dependencies including test tooling and pre-commit. `pre-commit install` registers the hooks so they run automatically on every commit.
|
|
21
|
+
|
|
22
|
+
## Making changes
|
|
23
|
+
|
|
24
|
+
1. Create a branch from `main`.
|
|
25
|
+
2. Make your changes.
|
|
26
|
+
3. Add or update tests — coverage must remain at or above 90%. Code must run on Python 3.8, and the core package must not gain a runtime dependency.
|
|
27
|
+
4. Commit. Pre-commit hooks run automatically and will block the commit if any check fails.
|
|
28
|
+
|
|
29
|
+
If a hook auto-fixes files (ruff lint/format), stage the changes and commit again.
|
|
30
|
+
|
|
31
|
+
## Pre-commit hooks
|
|
32
|
+
|
|
33
|
+
| Hook | What it checks |
|
|
34
|
+
|---|---|
|
|
35
|
+
| File hygiene | Large files (>1800 KB), trailing whitespace, merge conflicts, private keys, debug statements, BOM removal |
|
|
36
|
+
| `ruff` | Linting and import sorting (auto-fix); includes `ANN` rules that enforce PEP 484 annotations |
|
|
37
|
+
| `ruff-format` | Code formatting (auto-fix) |
|
|
38
|
+
| `pyright` | Static type checking (pylance engine) — config in `[tool.pyright]` in `pyproject.toml` |
|
|
39
|
+
| `pytest` | Full test suite with ≥ 90% coverage |
|
|
40
|
+
|
|
41
|
+
Run all hooks manually without committing:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pre-commit run --all-files
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Code style
|
|
48
|
+
|
|
49
|
+
- **Formatter / linter:** ruff (`line-length = 120`, Python 3.8 target).
|
|
50
|
+
- **Type hints:** all functions (public and private) must have complete PEP 484 type annotations. Enforced at lint time by ruff (`ANN` rules) and statically by pyright. `Any` is allowed for genuinely dynamic types; explicit `Any` is preferred over `object` when methods will be called on a value.
|
|
51
|
+
- **Docstrings:** NumPy style for all public functions, classes, and modules.
|
|
52
|
+
- **Comments:** only where the _why_ is non-obvious. No inline narration of what the code does.
|
|
53
|
+
|
|
54
|
+
See [CLAUDE.md](CLAUDE.md) for the full standard.
|
|
55
|
+
|
|
56
|
+
## Tests
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pytest # run all tests with coverage report
|
|
60
|
+
pytest tests/test_version.py # run a specific file
|
|
61
|
+
pytest -k "version" # run tests matching a pattern
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Tests live in `tests/` and mirror the package structure. The coverage threshold (90%) is enforced both by `pytest` directly and by the pre-commit hook.
|
|
65
|
+
|
|
66
|
+
## Pull requests
|
|
67
|
+
|
|
68
|
+
- Keep PRs focused — one logical change per PR.
|
|
69
|
+
- A change to the progress line format must update `docs/design.md` and the protocol fixtures in the same PR.
|
|
70
|
+
- Write a clear description of what changed and why.
|
|
71
|
+
- All pre-commit hooks must pass before requesting review.
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
By contributing, you agree that your contributions are licensed under the Apache License, Version 2.0.
|
|
76
|
+
|
|
77
|
+
## Code of Conduct
|
|
78
|
+
|
|
79
|
+
This project follows the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md).
|