pyrig 1.1.22__tar.gz → 2.0.30__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.
- pyrig-2.0.30/PKG-INFO +856 -0
- pyrig-2.0.30/README.md +837 -0
- pyrig-2.0.30/pyproject.toml +103 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/artifacts/builder/base/base.py +32 -8
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/base/base.py +101 -16
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/git/gitignore.py +8 -4
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/git/pre_commit.py +8 -25
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/pyproject.py +106 -124
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/python/builder.py +2 -2
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/python/configs.py +2 -2
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/python/experiment.py +6 -1
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/python/main.py +13 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/python/resources_init.py +2 -2
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/python/src_init.py +2 -2
- pyrig-2.0.30/pyrig/dev/configs/python/subcommands.py +15 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/readme.py +6 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/testing/conftest.py +2 -1
- pyrig-2.0.30/pyrig/dev/configs/testing/fixtures/fixture.py +15 -0
- pyrig-2.0.30/pyrig/dev/configs/testing/fixtures/scopes/class_.py +15 -0
- pyrig-2.0.30/pyrig/dev/configs/testing/fixtures/scopes/function.py +15 -0
- pyrig-2.0.30/pyrig/dev/configs/testing/fixtures/scopes/module.py +15 -0
- pyrig-2.0.30/pyrig/dev/configs/testing/fixtures/scopes/package.py +15 -0
- pyrig-2.0.30/pyrig/dev/configs/testing/fixtures/scopes/session.py +15 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/testing/main_test.py +1 -1
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/testing/zero_test.py +2 -2
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/workflows/base/base.py +45 -57
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/workflows/health_check.py +33 -1
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/workflows/publish.py +3 -3
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/workflows/release.py +15 -13
- pyrig-2.0.30/pyrig/dev/tests/conftest.py +33 -0
- {pyrig-1.1.22/pyrig/dev/tests/base → pyrig-2.0.30/pyrig/dev/tests}/fixtures/fixture.py +4 -3
- {pyrig-1.1.22/pyrig/dev/tests/base → pyrig-2.0.30/pyrig/dev/tests}/fixtures/scopes/class_.py +6 -5
- pyrig-2.0.30/pyrig/dev/tests/fixtures/scopes/function.py +8 -0
- {pyrig-1.1.22/pyrig/dev/tests/base → pyrig-2.0.30/pyrig/dev/tests}/fixtures/scopes/module.py +6 -5
- pyrig-2.0.30/pyrig/dev/tests/fixtures/scopes/package.py +8 -0
- pyrig-2.0.30/pyrig/dev/tests/fixtures/scopes/session.py +388 -0
- pyrig-1.1.22/pyrig/src/testing/fixtures.py → pyrig-2.0.30/pyrig/dev/tests/utils/decorators.py +15 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/main.py +1 -1
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/git/github/github.py +18 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/git/github/repo/protect.py +9 -7
- pyrig-2.0.30/pyrig/src/graph.py +91 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/iterate.py +22 -1
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/modules/class_.py +83 -31
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/modules/module.py +27 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/modules/package.py +6 -6
- pyrig-2.0.30/pyrig/src/project/init.py +55 -0
- pyrig-1.1.22/pyrig/src/project/poetry/poetry.py → pyrig-2.0.30/pyrig/src/project/mgt.py +16 -16
- {pyrig-1.1.22/pyrig/src/project/poetry → pyrig-2.0.30/pyrig/src/project}/versions.py +11 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/testing/create_tests.py +0 -21
- pyrig-1.1.22/PKG-INFO +0 -989
- pyrig-1.1.22/README.md +0 -963
- pyrig-1.1.22/pyproject.toml +0 -80
- pyrig-1.1.22/pyrig/dev/configs/python/subcommands.py +0 -23
- pyrig-1.1.22/pyrig/dev/tests/base/fixtures/scopes/function.py +0 -7
- pyrig-1.1.22/pyrig/dev/tests/base/fixtures/scopes/package.py +0 -7
- pyrig-1.1.22/pyrig/dev/tests/base/fixtures/scopes/session.py +0 -271
- pyrig-1.1.22/pyrig/dev/tests/base/utils/utils.py +0 -1
- pyrig-1.1.22/pyrig/dev/tests/conftest.py +0 -39
- pyrig-1.1.22/pyrig/src/project/init.py +0 -37
- pyrig-1.1.22/pyrig/src/project/poetry/dev_deps.py +0 -23
- pyrig-1.1.22/pyrig/src/testing/skip.py +0 -19
- {pyrig-1.1.22 → pyrig-2.0.30}/LICENSE +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/artifacts/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/artifacts/build.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/artifacts/builder/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/artifacts/builder/base/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/artifacts/builder/builder.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/artifacts/resources/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/cli/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/cli/cli.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/cli/subcommands.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/base/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/configs.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/dot_env.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/dot_python_version.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/git/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/licence.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/py_typed.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/python/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/dev/configs/testing/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/dev/configs/workflows → pyrig-2.0.30/pyrig/dev/configs/testing/fixtures}/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/dev/configs/workflows/base → pyrig-2.0.30/pyrig/dev/configs/testing/fixtures/scopes}/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/dev/tests → pyrig-2.0.30/pyrig/dev/configs/workflows}/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/dev/tests → pyrig-2.0.30/pyrig/dev/configs/workflows}/base/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/dev/tests/base/fixtures → pyrig-2.0.30/pyrig/dev/tests}/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/dev/tests/base/fixtures/scopes → pyrig-2.0.30/pyrig/dev/tests/fixtures}/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/dev/tests/base/utils → pyrig-2.0.30/pyrig/dev/tests/fixtures/scopes}/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/src/git → pyrig-2.0.30/pyrig/dev/tests/utils}/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/py.typed +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/src/git/github → pyrig-2.0.30/pyrig/src/git}/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/src/git/github/repo → pyrig-2.0.30/pyrig/src/git/github}/__init__.py +0 -0
- {pyrig-1.1.22/pyrig/src/modules → pyrig-2.0.30/pyrig/src/git/github/repo}/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/git/github/repo/repo.py +0 -0
- {pyrig-1.1.22/pyrig/src/os → pyrig-2.0.30/pyrig/src/modules}/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/modules/function.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/modules/inspection.py +0 -0
- {pyrig-1.1.22/pyrig/src/project → pyrig-2.0.30/pyrig/src/os}/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/os/os.py +0 -0
- {pyrig-1.1.22/pyrig/src/project/poetry → pyrig-2.0.30/pyrig/src/project}/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/project/create_root.py +0 -0
- {pyrig-1.1.22/pyrig/dev/artifacts/resources → pyrig-2.0.30/pyrig/src}/resource.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/string.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/testing/__init__.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/testing/assertions.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/testing/convention.py +0 -0
- {pyrig-1.1.22 → pyrig-2.0.30}/pyrig/src/testing/utils.py +0 -0
pyrig-2.0.30/PKG-INFO
ADDED
|
@@ -0,0 +1,856 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyrig
|
|
3
|
+
Version: 2.0.30
|
|
4
|
+
Summary: A dev kit that standardizes configurations and testing
|
|
5
|
+
Author: Winipedia
|
|
6
|
+
Author-email: Winipedia <win.steveker@gmx.de>
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: dotenv
|
|
9
|
+
Requires-Dist: packaging
|
|
10
|
+
Requires-Dist: pathspec
|
|
11
|
+
Requires-Dist: pillow
|
|
12
|
+
Requires-Dist: pygithub
|
|
13
|
+
Requires-Dist: pyyaml
|
|
14
|
+
Requires-Dist: setuptools
|
|
15
|
+
Requires-Dist: tomlkit
|
|
16
|
+
Requires-Dist: typer
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# pyrig
|
|
21
|
+
|
|
22
|
+
**pyrig** is a Python development toolkit that helps you **rig up** your Python projects by standardizing project configurations and automating testing workflows. It eliminates boilerplate setup work by providing opinionated, best-practice configurations for linting, type checking, testing, and CI/CD—allowing you to focus on writing code instead of configuring tools.
|
|
23
|
+
|
|
24
|
+
Built for Python 3.12+ projects using uv and GitHub, pyrig automatically generates project structure, creates test skeletons that mirror your source code, and maintains configuration files for tools like ruff, mypy, pytest, and pre-commit hooks.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Table of Contents
|
|
29
|
+
|
|
30
|
+
- [Features](#features)
|
|
31
|
+
- [Requirements](#requirements)
|
|
32
|
+
- [Installation](#installation)
|
|
33
|
+
- [Quick Start](#quick-start)
|
|
34
|
+
- [Initialization](#initialization)
|
|
35
|
+
- [Configuration Files](#configuration-files)
|
|
36
|
+
- [CLI Commands](#cli-commands)
|
|
37
|
+
- [Repository Protection](#repository-protection)
|
|
38
|
+
- [Testing](#testing)
|
|
39
|
+
- [Building Artifacts](#building-artifacts)
|
|
40
|
+
- [Examples](#examples)
|
|
41
|
+
- [Troubleshooting](#troubleshooting)
|
|
42
|
+
- [Contributing](#contributing)
|
|
43
|
+
- [License](#license)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- **Zero-Configuration Setup**: Opinionated, best-practice configurations for Python development tools
|
|
50
|
+
- **ConfigFile Machinery**: Automated system for discovering, creating, validating, and updating all configuration files
|
|
51
|
+
- **Automatic Test Generation**: Creates test skeletons that mirror your source code structure
|
|
52
|
+
- **Intelligent Fixture System**: Automatic discovery and loading of pytest fixtures across all packages
|
|
53
|
+
- **Strict Type Checking**: Enforces mypy strict mode with comprehensive type coverage
|
|
54
|
+
- **Code Quality Tools**: Pre-configured ruff (linting + formatting), mypy, bandit (security)
|
|
55
|
+
- **CI/CD Workflows**: GitHub Actions workflows for health checks, releases, and publishing
|
|
56
|
+
- **Repository Protection**: Automated GitHub branch protection and security settings
|
|
57
|
+
- **Dependency Management**: Automatic dependency updates with uv
|
|
58
|
+
- **Pre-commit Hooks**: Automated code quality checks before every commit with automatic installation
|
|
59
|
+
- **Artifact Building**: Extensible build system with PyInstaller support
|
|
60
|
+
- **Custom CLI**: Automatically generates CLI commands from your functions
|
|
61
|
+
- **Cross-Platform Testing**: Matrix testing across multiple OS and Python versions
|
|
62
|
+
- **Multi-Package Architecture**: Automatic discovery of configs, builders, fixtures, and resources across all packages depending on pyrig
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Requirements
|
|
67
|
+
|
|
68
|
+
- **Python**: 3.12 or higher
|
|
69
|
+
- **uv**: Package and dependency manager
|
|
70
|
+
- **Git**: Version control
|
|
71
|
+
- **GitHub**: For full CI/CD and repository protection features (optional but recommended)
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Installation
|
|
76
|
+
|
|
77
|
+
### From PyPI
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install pyrig
|
|
81
|
+
# or
|
|
82
|
+
uv add pyrig
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Note**: pyrig should be added as a regular dependency, not a dev dependency, because the CLI and utility functions require runtime availability. While pyrig manages dev dependencies for tools like ruff, mypy, and pytest, it keeps itself as a regular dependency to ensure full functionality in all environments.
|
|
86
|
+
|
|
87
|
+
### From Source
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/winipedia/pyrig.git
|
|
91
|
+
cd pyrig
|
|
92
|
+
uv sync
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Quick Start
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Create a new GitHub repository
|
|
101
|
+
# add REPO_TOKEN and PYPI_TOKEN to secrets as needed (more info below)
|
|
102
|
+
# Clone it locally
|
|
103
|
+
git clone https://github.com/your-username/your-project.git
|
|
104
|
+
cd your-project
|
|
105
|
+
|
|
106
|
+
# Initialize uv project
|
|
107
|
+
uv init --python 3.12 # 3.12 is the minimum supported version
|
|
108
|
+
|
|
109
|
+
# Add pyrig
|
|
110
|
+
uv add pyrig
|
|
111
|
+
|
|
112
|
+
# Initialize pyrig (creates all config files, tests, and runs setup)
|
|
113
|
+
uv run pyrig init
|
|
114
|
+
|
|
115
|
+
# Commit and push
|
|
116
|
+
git add .
|
|
117
|
+
git commit -m "chore: init project with pyrig"
|
|
118
|
+
git push
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Initialization
|
|
124
|
+
|
|
125
|
+
### Prerequisites
|
|
126
|
+
|
|
127
|
+
1. **Create a GitHub repository** for your project (e.g., `your-project`)
|
|
128
|
+
|
|
129
|
+
2. **Configure GitHub Secrets**
|
|
130
|
+
- `REPO_TOKEN`: GitHub Fine-Grained Personal Access Token with permissions:
|
|
131
|
+
- `contents:read and write` (needed to commit after release)
|
|
132
|
+
- `administration:read and write` (needed to protect the repo)
|
|
133
|
+
- `PYPI_TOKEN`: PyPI token for your project (only needed if publishing to PyPI)
|
|
134
|
+
|
|
135
|
+
### Setup Steps
|
|
136
|
+
|
|
137
|
+
1. **Clone the repository**
|
|
138
|
+
```bash
|
|
139
|
+
git clone https://github.com/your-username/your-project.git
|
|
140
|
+
cd your-project
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
2. **Install uv** (if not already installed)
|
|
144
|
+
```bash
|
|
145
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
3. **Initialize uv project**
|
|
149
|
+
```bash
|
|
150
|
+
uv init --python 3.12
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
4. **Add pyrig as a dependency**
|
|
154
|
+
```bash
|
|
155
|
+
uv add pyrig
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
5. **Run pyrig initialization**
|
|
159
|
+
```bash
|
|
160
|
+
uv run pyrig init
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Note**: This will delete the root-level `main.py` created by `uv init` and replace it with a properly structured `your_project/main.py` inside the package.
|
|
164
|
+
|
|
165
|
+
6. **Commit and push changes**
|
|
166
|
+
```bash
|
|
167
|
+
git add .
|
|
168
|
+
git commit -m "chore: init project with pyrig"
|
|
169
|
+
git push
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## ConfigFile Machinery
|
|
175
|
+
|
|
176
|
+
The ConfigFile Machinery is pyrig's automated system for managing all configuration files. All configuration files are subclasses of `pyrig.dev.configs.base.base.ConfigFile` and are automatically discovered from `pkg/dev/configs/**` directories across all packages depending on pyrig.
|
|
177
|
+
|
|
178
|
+
**Key Features**:
|
|
179
|
+
|
|
180
|
+
- **Automatic Discovery**: Scans all `dev/configs/` directories across all packages depending on pyrig
|
|
181
|
+
- **Automatic Initialization**: Creates missing config files based on their class definitions
|
|
182
|
+
- **Automatic Validation**: Checks existing config files against their expected content
|
|
183
|
+
- **Automatic Updates**: Updates config files when their definitions change
|
|
184
|
+
- **Intelligent Subclass Discovery**: Only executes the most specific (leaf) implementations. If you subclass an existing config, only your custom subclass will be executed, preventing duplicates and ensuring your customizations take precedence.
|
|
185
|
+
|
|
186
|
+
**Note**: To prevent pyrig from managing a specific config file, make the file empty (the file must exist).
|
|
187
|
+
|
|
188
|
+
### Extending Configuration Files
|
|
189
|
+
|
|
190
|
+
The ConfigFile Machinery uses a **subset validation algorithm** that allows you to extend configuration files with custom settings while maintaining pyrig's required settings.
|
|
191
|
+
|
|
192
|
+
**Subset Validation Rules**:
|
|
193
|
+
|
|
194
|
+
- **Dictionaries**: All required keys must exist, but you can add additional keys
|
|
195
|
+
- **Lists**: All required items must exist (order doesn't matter), but you can add additional items
|
|
196
|
+
- **Values**: Required values must match exactly (unless they are nested structures)
|
|
197
|
+
|
|
198
|
+
**Example**:
|
|
199
|
+
|
|
200
|
+
If pyrig requires:
|
|
201
|
+
```toml
|
|
202
|
+
[tool.mypy]
|
|
203
|
+
strict = true
|
|
204
|
+
warn_redundant_casts = true
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
You can extend it with:
|
|
208
|
+
```toml
|
|
209
|
+
[tool.mypy]
|
|
210
|
+
strict = true
|
|
211
|
+
warn_redundant_casts = true
|
|
212
|
+
exclude = ["tests/fixtures/"] # Your custom setting
|
|
213
|
+
plugins = ["pydantic.mypy"] # Your custom setting
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Recommended Approach**: Subclass existing configs (e.g., `PyprojectConfigFile`) in your own `dev/configs/python/pyproject.py` file. When you subclass an existing config, only your subclass executes, ensuring your customizations take full control.
|
|
217
|
+
|
|
218
|
+
### ConfigFile Types
|
|
219
|
+
|
|
220
|
+
- **`ConfigFile`**: Base class for all config files
|
|
221
|
+
- **`CopyModuleConfigFile`**: Copies entire module content to a config file
|
|
222
|
+
- **`CopyModuleOnlyDocstringConfigFile`**: Copies only the docstring from a module
|
|
223
|
+
- **`PythonConfigFile`**: For Python source files
|
|
224
|
+
- **`YamlConfigFile`**: For YAML configuration files
|
|
225
|
+
- **`TomlConfigFile`**: For TOML configuration files
|
|
226
|
+
|
|
227
|
+
### Configuration Files Managed by the Machinery
|
|
228
|
+
|
|
229
|
+
The ConfigFile Machinery automatically manages the following configuration files:
|
|
230
|
+
|
|
231
|
+
#### `pyproject.toml`
|
|
232
|
+
|
|
233
|
+
Stores project metadata and dependencies. Automatically adds essential dev dependencies (ruff, mypy, pytest, pre-commit) with strict settings.
|
|
234
|
+
|
|
235
|
+
- **Automatic Version Stripping**: By default, pyrig strips version constraints from all dependencies (e.g., `requests>=2.0` becomes `requests`). This ensures you always get the latest compatible versions when running `uv lock --upgrade`.
|
|
236
|
+
- **When Updates Happen**: Dependencies are upgraded during `pyrig init` and via the `assert_dependencies_are_up_to_date` autouse session fixture (runs `uv sync`, then `uv lock --upgrade`).
|
|
237
|
+
- **Disabling Version Stripping**: To keep specific version constraints, subclass `PyprojectConfigFile` and override `should_remove_version_from_dep()` to return `False`:
|
|
238
|
+
```python
|
|
239
|
+
# your_project/dev/configs/pyproject.py
|
|
240
|
+
from pyrig.dev.configs.pyproject import PyprojectConfigFile as BasePyprojectConfigFile
|
|
241
|
+
|
|
242
|
+
class PyprojectConfigFile(BasePyprojectConfigFile):
|
|
243
|
+
@classmethod
|
|
244
|
+
def should_remove_version_from_dep(cls) -> bool:
|
|
245
|
+
return False
|
|
246
|
+
```
|
|
247
|
+
- Enforces that GitHub repo name and cwd name are equal; hyphens in repo names are converted to underscores in package names
|
|
248
|
+
|
|
249
|
+
#### `pkg/py.typed`
|
|
250
|
+
|
|
251
|
+
Indicates that the package supports type checking.
|
|
252
|
+
|
|
253
|
+
#### `README.md`
|
|
254
|
+
|
|
255
|
+
Must start with `# <project_name>`. The rest of the content is up to you.
|
|
256
|
+
|
|
257
|
+
#### `LICENCE`
|
|
258
|
+
|
|
259
|
+
Empty file for you to add your own license.
|
|
260
|
+
|
|
261
|
+
#### `.experiment.py`
|
|
262
|
+
|
|
263
|
+
Empty file for experimentation. Git-ignored, not for production code.
|
|
264
|
+
|
|
265
|
+
#### `.python-version`
|
|
266
|
+
|
|
267
|
+
Set to the lowest supported Python version. Used by pyenv.
|
|
268
|
+
|
|
269
|
+
#### `.pre-commit-config.yaml`
|
|
270
|
+
|
|
271
|
+
Configured with the following hooks:
|
|
272
|
+
|
|
273
|
+
- `lint-code`: ruff check --fix
|
|
274
|
+
- `format-code`: ruff format
|
|
275
|
+
- `check-static-types`: mypy --exclude-gitignore
|
|
276
|
+
- `check-security`: bandit -c pyproject.toml -r .
|
|
277
|
+
|
|
278
|
+
Automatically installed during test session via autouse fixture.
|
|
279
|
+
|
|
280
|
+
**Note**: Heavy operations like `install-dependencies` and `create-root` run as autouse session fixtures during test execution, not as pre-commit hooks, to keep commits fast.
|
|
281
|
+
|
|
282
|
+
#### `.gitignore`
|
|
283
|
+
|
|
284
|
+
Pulls the latest [github/python.gitignore](https://github.com/github/gitignore/blob/main/Python.gitignore) and adds project-specific ignores.
|
|
285
|
+
|
|
286
|
+
#### `.env`
|
|
287
|
+
|
|
288
|
+
Empty file for environment variables. Git-ignored, used by python-dotenv.
|
|
289
|
+
|
|
290
|
+
#### `.github/workflows/health_check.yaml`
|
|
291
|
+
|
|
292
|
+
- **Triggers**: workflow_dispatch, pull_request, push to main, schedule (daily with staggered cron)
|
|
293
|
+
- **Purpose**: Matrix testing across different OS and Python versions
|
|
294
|
+
- **Staggered Cron Schedule**: To avoid test failures when multiple packages release on the same schedule, the cron time is staggered based on the package's position in the dependency chain. Packages with fewer dependencies on pyrig run earlier (starting at 1 AM UTC), with each additional dependency level adding 1 hour. This ensures that when dependencies release, dependent packages have time to pick up the new versions before their health checks run.
|
|
295
|
+
|
|
296
|
+
#### `.github/workflows/release.yaml`
|
|
297
|
+
|
|
298
|
+
- **Triggers**: workflow_dispatch, workflow_run (when health_check completes successfully on main)
|
|
299
|
+
- **Process**: Creates tag and changelog, creates GitHub release, builds and uploads artifacts, commits updates
|
|
300
|
+
- **Synchronization**: Keeps tags, package version, and PyPI in sync
|
|
301
|
+
- **Note**: Only runs after health check passes on main branch, so health check steps are not duplicated
|
|
302
|
+
|
|
303
|
+
#### `.github/workflows/publish.yaml`
|
|
304
|
+
|
|
305
|
+
- **Trigger**: Successful completion of release workflow
|
|
306
|
+
- **Purpose**: Publishes the package to PyPI
|
|
307
|
+
- **Note**: Empty the file to disable PyPI publishing
|
|
308
|
+
|
|
309
|
+
#### `pkg/main.py`
|
|
310
|
+
|
|
311
|
+
Main entry point for your application. Used by PyInstaller for building executables.
|
|
312
|
+
|
|
313
|
+
- **Usage**: `uv run your-pkg-name main` or `python -m your-pkg-name`
|
|
314
|
+
|
|
315
|
+
#### `pkg/src/`
|
|
316
|
+
|
|
317
|
+
Subfolder for organizing your source code, separating it from development infrastructure (`dev/`). Automatically created with an `__init__.py` file.
|
|
318
|
+
|
|
319
|
+
#### `pkg/dev/cli/subcommands.py`
|
|
320
|
+
|
|
321
|
+
Define custom CLI subcommands. Any function in this file is automatically added as a subcommand.
|
|
322
|
+
|
|
323
|
+
**Example**:
|
|
324
|
+
```python
|
|
325
|
+
def hello(name: str = "World") -> None:
|
|
326
|
+
"""Say hello to someone."""
|
|
327
|
+
print(f"Hello, {name}!")
|
|
328
|
+
|
|
329
|
+
# Run with: uv run your-pkg-name hello --name Alice
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### `pkg/dev/configs/configs.py`
|
|
333
|
+
|
|
334
|
+
Define custom configuration files. Any subclass of `ConfigFile` is automatically discovered and initialized. Configs can be defined in any file in the `pkg/dev/configs` folder.
|
|
335
|
+
|
|
336
|
+
**Subclass Behavior**: If you subclass an existing `ConfigFile`, only your most specific subclass will be executed, preventing duplicates.
|
|
337
|
+
|
|
338
|
+
#### `pkg/dev/artifacts/builder/builder.py`
|
|
339
|
+
|
|
340
|
+
Define build scripts. Any subclass of `Builder` is automatically discovered and executed. Builders can be defined in any file in the `pkg/dev/artifacts/builder` folder.
|
|
341
|
+
|
|
342
|
+
**Subclass Behavior**: If you subclass an existing `Builder`, only your most specific subclass will be executed, preventing duplicate builds.
|
|
343
|
+
|
|
344
|
+
#### `pkg/dev/artifacts/resources/`
|
|
345
|
+
|
|
346
|
+
Directory for storing static resources (images, data files, etc.). Automatically included in PyInstaller builds.
|
|
347
|
+
|
|
348
|
+
- **Resource Access**: Use `get_resource_path()` to access resources at runtime
|
|
349
|
+
- **Automatic Discovery**: Resources from all packages depending on pyrig are automatically included
|
|
350
|
+
|
|
351
|
+
**Example**:
|
|
352
|
+
```python
|
|
353
|
+
from pyrig.src.resource import get_resource_path
|
|
354
|
+
import your_project.dev.artifacts.resources as resources
|
|
355
|
+
|
|
356
|
+
config_path = get_resource_path("config.json", resources)
|
|
357
|
+
data = config_path.read_text()
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
#### `pkg/dev/configs/python/resources_init.py`
|
|
361
|
+
|
|
362
|
+
Manages the `resources/__init__.py` file to ensure proper package initialization.
|
|
363
|
+
|
|
364
|
+
#### `pkg/dev/configs/testing/fixtures/`
|
|
365
|
+
|
|
366
|
+
Configuration files that manage the fixture system structure (fixture.py, scopes/session.py, scopes/package.py, scopes/module.py, scopes/class_.py, scopes/function.py).
|
|
367
|
+
|
|
368
|
+
#### `tests/conftest.py`
|
|
369
|
+
|
|
370
|
+
Automatically discovers and loads fixtures from all packages depending on pyrig. Fixtures are globally available across all tests without manual imports.
|
|
371
|
+
|
|
372
|
+
#### `tests/test_zero.py`
|
|
373
|
+
|
|
374
|
+
Empty test file to ensure pytest doesn't complain about missing tests during initial setup.
|
|
375
|
+
|
|
376
|
+
#### `tests/test_<pkg>/test_main.py`
|
|
377
|
+
|
|
378
|
+
Auto-generated test file that verifies the CLI entry point works correctly by running `uv run <pkg-name> --help`.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
## CLI Commands
|
|
384
|
+
|
|
385
|
+
pyrig provides the following CLI commands:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
pyrig --help
|
|
389
|
+
|
|
390
|
+
Usage: pyrig [OPTIONS] COMMAND [ARGS]...
|
|
391
|
+
|
|
392
|
+
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────╮
|
|
393
|
+
│ --install-completion Install completion for the current shell. │
|
|
394
|
+
│ --show-completion Show completion for the current shell, to copy it or customize the │
|
|
395
|
+
│ installation. │
|
|
396
|
+
│ --help Show this message and exit. │
|
|
397
|
+
╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
398
|
+
╭─ Commands ──────────────────────────────────────────────────────────────────────────────────────────╮
|
|
399
|
+
│ create-root Creates the root of the project. │
|
|
400
|
+
│ create-tests Create all test files for the project. │
|
|
401
|
+
│ init Set up the project. │
|
|
402
|
+
│ build Build all artifacts. │
|
|
403
|
+
│ protect-repo Protect the repository. │
|
|
404
|
+
╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Repository Protection
|
|
410
|
+
|
|
411
|
+
pyrig automatically configures comprehensive GitHub repository protection measures to enforce code quality, security, and collaboration best practices. The protection is applied via the `pyrig protect-repo` command, which is automatically run in CI/CD workflows.
|
|
412
|
+
|
|
413
|
+
### Repository Settings
|
|
414
|
+
|
|
415
|
+
The following repository-level settings are automatically configured:
|
|
416
|
+
|
|
417
|
+
| Setting | Configuration | Purpose |
|
|
418
|
+
|---------|--------------|---------|
|
|
419
|
+
| **Default Branch** | `main` | Standard default branch |
|
|
420
|
+
| **Delete Branch on Merge** | Enabled | Automatically deletes head branches after pull requests are merged |
|
|
421
|
+
| **Allow Update Branch** | Enabled | Allows updating pull request branches with the base branch |
|
|
422
|
+
| **Merge Commit** | Disabled | Prevents merge commits to maintain clean history |
|
|
423
|
+
| **Rebase Merge** | Enabled | Allows rebase and merge strategy |
|
|
424
|
+
| **Squash Merge** | Enabled | Allows squash and merge strategy |
|
|
425
|
+
|
|
426
|
+
### Branch Protection Rules
|
|
427
|
+
|
|
428
|
+
A comprehensive ruleset named "main protection" is applied to the default branch with the following protections:
|
|
429
|
+
|
|
430
|
+
#### Branch Modification Protections
|
|
431
|
+
|
|
432
|
+
- **Deletion Protection**: Prevents deletion of the protected branch
|
|
433
|
+
- **Non-Fast-Forward Protection**: Prevents force pushes and history rewrites
|
|
434
|
+
- **Creation Protection**: Controls branch creation patterns
|
|
435
|
+
- **Update Protection**: Restricts direct updates to the branch
|
|
436
|
+
|
|
437
|
+
#### Pull Request Requirements
|
|
438
|
+
|
|
439
|
+
- **Required Approving Reviews**: At least 1 approval required before merging
|
|
440
|
+
- **Dismiss Stale Reviews**: Automatically dismisses approvals when new commits are pushed
|
|
441
|
+
- **Code Owner Review**: Requires review from code owners (if CODEOWNERS file exists)
|
|
442
|
+
- **Last Push Approval**: Requires approval from someone other than the last person to push
|
|
443
|
+
- **Review Thread Resolution**: All review comments must be resolved before merging
|
|
444
|
+
- **Allowed Merge Methods**: Only squash and rebase merges are permitted
|
|
445
|
+
|
|
446
|
+
#### Code Quality Requirements
|
|
447
|
+
|
|
448
|
+
- **Required Linear History**: Enforces a linear commit history
|
|
449
|
+
- **Required Commit Signatures**: Requires commits to be signed (GPG/SSH signatures)
|
|
450
|
+
- **Required Status Checks**: All CI/CD checks must pass before merging
|
|
451
|
+
- **Strict Status Checks**: Branch must be up-to-date with base branch before merging
|
|
452
|
+
- **Required Workflow**: The `health_check.yaml` workflow must pass successfully
|
|
453
|
+
|
|
454
|
+
#### Bypass Actors
|
|
455
|
+
|
|
456
|
+
- Repository owner can bypass all protection rules when necessary
|
|
457
|
+
|
|
458
|
+
### Manual Application
|
|
459
|
+
|
|
460
|
+
The repository protection is automatically applied during CI/CD workflows, but you can also manually apply it:
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
# Set the REPO_TOKEN environment variable with a GitHub Personal Access Token
|
|
464
|
+
export REPO_TOKEN=your_github_token # or add it to your .env file; pyrig picks it up automatically
|
|
465
|
+
|
|
466
|
+
# Run the protection command
|
|
467
|
+
uv run pyrig protect-repo
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
**Required Token Permissions**:
|
|
471
|
+
- `contents:read and write`
|
|
472
|
+
- `administration:read and write`
|
|
473
|
+
|
|
474
|
+
### CI/CD Integration
|
|
475
|
+
|
|
476
|
+
The `protect-repo` step is automatically included in both the `health_check.yaml` and `release.yaml` workflows, ensuring that protection rules are consistently applied and up-to-date with every CI/CD run.
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
## Testing
|
|
482
|
+
|
|
483
|
+
pyrig uses pytest as the test framework, which is automatically added as a dev dependency and configured in pyproject.toml.
|
|
484
|
+
|
|
485
|
+
### Test Structure
|
|
486
|
+
|
|
487
|
+
pyrig generates test skeletons that mirror your source code structure:
|
|
488
|
+
|
|
489
|
+
- **Module Level**: Each source module has a corresponding test module
|
|
490
|
+
- **Class Level**: Each source class has a corresponding test class
|
|
491
|
+
- **Function Level**: Each source function/method has a corresponding test function
|
|
492
|
+
|
|
493
|
+
### Automatic Test Generation
|
|
494
|
+
|
|
495
|
+
1. **Manual**: Run `uv run pyrig create-tests`
|
|
496
|
+
2. **Automatic**: An autouse session fixture creates missing tests when you run `pytest`
|
|
497
|
+
|
|
498
|
+
#### Fixture System
|
|
499
|
+
|
|
500
|
+
pyrig automatically discovers and loads fixtures from all packages depending on pyrig.
|
|
501
|
+
|
|
502
|
+
**Built-in Fixtures**:
|
|
503
|
+
- `config_file_factory` - Factory for creating test config file classes
|
|
504
|
+
- `builder_factory` - Factory for creating test builder classes
|
|
505
|
+
|
|
506
|
+
**Creating Custom Fixtures**:
|
|
507
|
+
|
|
508
|
+
Add a `pkg/dev/tests/fixtures/` directory:
|
|
509
|
+
|
|
510
|
+
```
|
|
511
|
+
your_project/dev/tests/fixtures/
|
|
512
|
+
├── __init__.py
|
|
513
|
+
├── fixture.py # Custom fixtures
|
|
514
|
+
└── scopes/
|
|
515
|
+
├── __init__.py
|
|
516
|
+
├── session.py # Session-level autouse fixtures
|
|
517
|
+
└── ...
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Notes**:
|
|
521
|
+
- All fixtures are automatically discovered - no manual imports needed
|
|
522
|
+
- Autouse fixtures must be decorated with `@pytest.fixture(autouse=True)` or `@autouse_session_fixture`
|
|
523
|
+
- Fixtures from all packages are available to all tests
|
|
524
|
+
|
|
525
|
+
### Autouse Session Fixtures
|
|
526
|
+
|
|
527
|
+
Autouse session fixtures automatically enforce code quality and project conventions. These fixtures run once per test session before any tests execute.
|
|
528
|
+
|
|
529
|
+
#### `assert_root_is_correct`
|
|
530
|
+
|
|
531
|
+
Verifies and fixes all configuration files managed by the ConfigFile Machinery. If any config file is missing or incorrect, it automatically creates or corrects it. When running in GitHub Actions, it also ensures `.experiment.py` exists.
|
|
532
|
+
|
|
533
|
+
#### `assert_no_namespace_packages`
|
|
534
|
+
|
|
535
|
+
Ensures all packages have `__init__.py` files. If namespace packages are found (directories without `__init__.py`), they are automatically created. This prevents import issues and ensures proper package structure.
|
|
536
|
+
|
|
537
|
+
#### `assert_all_src_code_in_one_package`
|
|
538
|
+
|
|
539
|
+
Verifies that all source code is in a single package alongside the `tests` package. Also ensures the source package only contains the expected structure: `src/` and `dev/` subpackages, and a `main.py` module. This enforces a clean project structure.
|
|
540
|
+
|
|
541
|
+
#### `assert_src_package_correctly_named`
|
|
542
|
+
|
|
543
|
+
Checks that the source package name matches the project name defined in `pyproject.toml`. Hyphens in the project name are converted to underscores for the package name.
|
|
544
|
+
|
|
545
|
+
#### `assert_all_modules_tested`
|
|
546
|
+
|
|
547
|
+
Creates missing test modules, classes, and functions. For every module in the source package, it ensures a corresponding test module exists in the `tests` directory. Missing test skeletons are automatically generated.
|
|
548
|
+
|
|
549
|
+
#### `assert_no_unit_test_package_usage`
|
|
550
|
+
|
|
551
|
+
Prevents usage of the `unittest` package by scanning all Python files for `unittest` imports or usage. This enforces pytest as the sole testing framework for consistency.
|
|
552
|
+
|
|
553
|
+
#### `assert_dependencies_are_up_to_date`
|
|
554
|
+
|
|
555
|
+
Keeps dependencies current by running `uv self update` (when not in CI), `uv lock --upgrade`, and `uv sync`. This ensures you always have the latest compatible versions of all dependencies.
|
|
556
|
+
|
|
557
|
+
#### `assert_pre_commit_is_installed`
|
|
558
|
+
|
|
559
|
+
Ensures pre-commit hooks are installed by running `pre-commit install`. This guarantees that code quality checks run automatically before every commit.
|
|
560
|
+
|
|
561
|
+
#### `assert_src_runs_without_dev_deps`
|
|
562
|
+
|
|
563
|
+
Verifies that the source code runs correctly without dev dependencies installed. This fixture creates a temporary environment with only production dependencies, imports all source modules, and confirms they load successfully. This catches accidental imports of dev-only packages (like `pytest`) in production code.
|
|
564
|
+
|
|
565
|
+
### Running Tests
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
# Run all tests
|
|
569
|
+
uv run pytest
|
|
570
|
+
|
|
571
|
+
# Run specific test file
|
|
572
|
+
uv run pytest tests/test_your_project/test_calculator.py
|
|
573
|
+
|
|
574
|
+
# Run with coverage
|
|
575
|
+
uv run pytest --cov=your_project
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### Disabling Tests
|
|
579
|
+
|
|
580
|
+
To disable tests for a specific module, empty the test file:
|
|
581
|
+
```bash
|
|
582
|
+
echo "" > tests/test_your_project/test_src/test_calculator.py
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### Testing Utilities
|
|
586
|
+
|
|
587
|
+
pyrig provides several testing utilities in `pyrig.src.testing`:
|
|
588
|
+
|
|
589
|
+
**Assertions** (`pyrig.src.testing.assertions`):
|
|
590
|
+
- `assert_with_msg(expr, msg)` - Assert with a custom error message
|
|
591
|
+
- `assert_with_info(expr, expected, actual, msg)` - Assert with expected/actual values in the message
|
|
592
|
+
- `assert_isabstrct_method(method)` - Assert that a method is abstract
|
|
593
|
+
|
|
594
|
+
**Skip Decorators** (`pyrig.dev.tests.utils.decorators`):
|
|
595
|
+
- `@skip_fixture_test` - Skip tests for fixtures (cannot be called directly)
|
|
596
|
+
- `@skip_in_github_actions` - Skip tests that cannot run in GitHub Actions
|
|
597
|
+
|
|
598
|
+
**Fixture Scope Decorators** (`pyrig.dev.tests.utils.decorators`):
|
|
599
|
+
- `@function_fixture`, `@class_fixture`, `@module_fixture`, `@package_fixture`, `@session_fixture`
|
|
600
|
+
- `@autouse_function_fixture`, `@autouse_class_fixture`, `@autouse_module_fixture`, `@autouse_package_fixture`, `@autouse_session_fixture`
|
|
601
|
+
|
|
602
|
+
**Example**:
|
|
603
|
+
```python
|
|
604
|
+
from pyrig.src.testing.assertions import assert_with_msg
|
|
605
|
+
from pyrig.dev.tests.utils.decorators import autouse_session_fixture, skip_in_github_actions
|
|
606
|
+
|
|
607
|
+
@autouse_session_fixture
|
|
608
|
+
def my_fixture() -> str:
|
|
609
|
+
return "test data"
|
|
610
|
+
|
|
611
|
+
@skip_in_github_actions
|
|
612
|
+
def test_local_only() -> None:
|
|
613
|
+
assert_with_msg(True, "This should pass")
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
## Building Artifacts
|
|
619
|
+
|
|
620
|
+
pyrig provides an extensible build system. All builders are subclasses of `Builder`. When you run `pyrig build`, the system automatically discovers all `Builder` subclasses. If you subclass an existing builder, only your most specific subclass executes.
|
|
621
|
+
|
|
622
|
+
### Basic Builder
|
|
623
|
+
|
|
624
|
+
Create custom builders by subclassing `Builder` in `pkg/dev/artifacts/builder/builder.py`:
|
|
625
|
+
|
|
626
|
+
```python
|
|
627
|
+
from pathlib import Path
|
|
628
|
+
from pyrig.dev.artifacts.builder.base.base import Builder
|
|
629
|
+
|
|
630
|
+
class MyBuilder(Builder):
|
|
631
|
+
"""Custom builder for creating artifacts."""
|
|
632
|
+
|
|
633
|
+
@classmethod
|
|
634
|
+
def create_artifacts(cls, temp_artifacts_dir: Path) -> None:
|
|
635
|
+
"""Build the project."""
|
|
636
|
+
# Create your artifacts in temp_artifacts_dir
|
|
637
|
+
artifact_path = temp_artifacts_dir / "my_artifact.txt"
|
|
638
|
+
artifact_path.write_text("Hello, World!")
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**Build artifacts**:
|
|
642
|
+
```bash
|
|
643
|
+
uv run pyrig build
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
Artifacts are placed in the `dist/` directory with platform-specific naming (e.g., `my_artifact-Linux.txt`, `my_artifact-Windows.txt`).
|
|
647
|
+
|
|
648
|
+
### PyInstaller Builder
|
|
649
|
+
|
|
650
|
+
pyrig includes a `PyInstallerBuilder` class for creating standalone executables.
|
|
651
|
+
|
|
652
|
+
1. **Implement your main function** in `your_project/main.py`
|
|
653
|
+
2. **Create an icon.png file** at `your_project/dev/artifacts/resources/icon.png` (256x256 recommended)
|
|
654
|
+
3. **Add resources** to `your_project/dev/artifacts/resources/` (optional)
|
|
655
|
+
4. **Subclass PyInstallerBuilder** in `your_project/dev/artifacts/builder/builder.py`:
|
|
656
|
+
```python
|
|
657
|
+
from types import ModuleType
|
|
658
|
+
from pyrig.dev.artifacts.builder.base.base import PyInstallerBuilder
|
|
659
|
+
|
|
660
|
+
class MyAppBuilder(PyInstallerBuilder):
|
|
661
|
+
"""Build standalone executable with PyInstaller."""
|
|
662
|
+
|
|
663
|
+
@classmethod
|
|
664
|
+
def get_additional_resource_pkgs(cls) -> list[ModuleType]:
|
|
665
|
+
"""Return additional resource packages to include in the build."""
|
|
666
|
+
return [] # Add your custom resource packages here
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
5. **Build**:
|
|
670
|
+
```bash
|
|
671
|
+
uv run pyrig build
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
The builder automatically:
|
|
675
|
+
- Creates a single executable file
|
|
676
|
+
- Converts icon.png to platform-specific format
|
|
677
|
+
- Auto-discovers and includes resources from all packages depending on pyrig
|
|
678
|
+
- Names output with platform suffix (e.g., `your-project-Windows.exe`)
|
|
679
|
+
|
|
680
|
+
### Accessing Resources in Built Executables
|
|
681
|
+
|
|
682
|
+
Use `get_resource_path()` to access resources:
|
|
683
|
+
|
|
684
|
+
```python
|
|
685
|
+
from pyrig.dev.artifacts.resources.resource import get_resource_path
|
|
686
|
+
import your_project.dev.artifacts.resources as resources
|
|
687
|
+
|
|
688
|
+
config_path = get_resource_path("config.json", resources)
|
|
689
|
+
data = json.loads(config_path.read_text())
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### Multi-Package Architecture
|
|
693
|
+
|
|
694
|
+
pyrig supports multi-package architecture where multiple packages can depend on pyrig and automatically share configurations, builders, fixtures, and resources.
|
|
695
|
+
|
|
696
|
+
**Automatic Discovery**:
|
|
697
|
+
|
|
698
|
+
When you run pyrig commands or tests, it discovers components from all packages depending on pyrig:
|
|
699
|
+
|
|
700
|
+
1. **ConfigFile Machinery**: All `ConfigFile` subclasses from all `dev/configs/` directories
|
|
701
|
+
2. **Builders**: All `Builder` subclasses from all `dev/artifacts/builder/` directories
|
|
702
|
+
3. **Fixtures**: All pytest fixtures from all `dev/tests/fixtures/` directories
|
|
703
|
+
4. **Resources**: All files from all `dev/artifacts/resources/` directories
|
|
704
|
+
|
|
705
|
+
**Intelligent Subclass Discovery**:
|
|
706
|
+
|
|
707
|
+
- Only the most specific (leaf) subclasses are executed for configs and builders
|
|
708
|
+
- If you subclass a config or builder, only your subclass runs (not the parent)
|
|
709
|
+
- Fixtures use a different mechanism that loads all fixture modules without filtering
|
|
710
|
+
|
|
711
|
+
This enables:
|
|
712
|
+
- **Modular development**: Split your project into multiple packages with shared infrastructure
|
|
713
|
+
- **Reusable components**: Share configs, builders, fixtures, and resources across projects
|
|
714
|
+
- **Zero configuration**: Everything works automatically through dependency discovery
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
## Examples
|
|
721
|
+
|
|
722
|
+
### Example 1: Complete Project Structure
|
|
723
|
+
|
|
724
|
+
After running `pyrig init`:
|
|
725
|
+
|
|
726
|
+
```
|
|
727
|
+
your-project/
|
|
728
|
+
├── .env, .experiment.py, .gitignore, .pre-commit-config.yaml, .python-version
|
|
729
|
+
├── LICENSE, uv.lock, pyproject.toml, README.md
|
|
730
|
+
├── .github/workflows/
|
|
731
|
+
│ ├── health_check.yaml, publish.yaml, release.yaml
|
|
732
|
+
├── your_project/
|
|
733
|
+
│ ├── __init__.py, main.py, py.typed
|
|
734
|
+
│ ├── src/
|
|
735
|
+
│ └── dev/
|
|
736
|
+
│ ├── artifacts/builder/, artifacts/resources/
|
|
737
|
+
│ ├── cli/subcommands.py
|
|
738
|
+
│ ├── configs/configs.py
|
|
739
|
+
│ └── tests/fixtures/
|
|
740
|
+
└── tests/
|
|
741
|
+
├── conftest.py, test_zero.py
|
|
742
|
+
└── test_your_project/
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
### Example 2: Adding a Custom Config File
|
|
746
|
+
|
|
747
|
+
```python
|
|
748
|
+
from pathlib import Path
|
|
749
|
+
from pyrig.dev.configs.base.base import YamlConfigFile
|
|
750
|
+
|
|
751
|
+
class MyConfigFile(YamlConfigFile):
|
|
752
|
+
@classmethod
|
|
753
|
+
def get_filename(cls) -> str:
|
|
754
|
+
return "myconfig"
|
|
755
|
+
|
|
756
|
+
@classmethod
|
|
757
|
+
def get_parent_path(cls) -> Path:
|
|
758
|
+
return Path("config")
|
|
759
|
+
|
|
760
|
+
@classmethod
|
|
761
|
+
def get_configs(cls) -> dict[str, Any]:
|
|
762
|
+
return {"setting1": "value1", "setting2": "value2"}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
### Example 3: Custom CLI Command
|
|
766
|
+
|
|
767
|
+
```python
|
|
768
|
+
def deploy(environment: str = "staging") -> None:
|
|
769
|
+
"""Deploy the application."""
|
|
770
|
+
print(f"Deploying to {environment}...")
|
|
771
|
+
|
|
772
|
+
# Run with: uv run your-project deploy --environment production
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
---
|
|
776
|
+
|
|
777
|
+
## Troubleshooting
|
|
778
|
+
|
|
779
|
+
### Common Issues
|
|
780
|
+
|
|
781
|
+
#### `uv run pyrig` command not found
|
|
782
|
+
```bash
|
|
783
|
+
uv sync
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
#### Pre-commit hooks failing
|
|
787
|
+
```bash
|
|
788
|
+
uv run pre-commit install
|
|
789
|
+
uv run pre-commit run --all-files
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
#### Tests not being generated automatically
|
|
793
|
+
```bash
|
|
794
|
+
uv run pyrig create-tests
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
#### GitHub Actions permission errors
|
|
798
|
+
Ensure `REPO_TOKEN` secret has: `contents:read and write`, `administration:read and write`
|
|
799
|
+
|
|
800
|
+
#### MyPy errors
|
|
801
|
+
Add type hints:
|
|
802
|
+
```python
|
|
803
|
+
def add(a: int, b: int) -> int:
|
|
804
|
+
return a + b
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
#### Dependency conflicts
|
|
808
|
+
```bash
|
|
809
|
+
uv lock --upgrade && uv sync
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
#### PyInstaller build fails
|
|
813
|
+
1. Ensure `main.py` exists and has `main()` function implemented
|
|
814
|
+
2. Ensure `icon.png` exists at `your_project/dev/artifacts/resources/icon.png`
|
|
815
|
+
|
|
816
|
+
#### Resources not found in built executable
|
|
817
|
+
Use `get_resource_path()`:
|
|
818
|
+
```python
|
|
819
|
+
from pyrig.dev.artifacts.resources.resource import get_resource_path
|
|
820
|
+
import your_project.dev.artifacts.resources as resources
|
|
821
|
+
path = get_resource_path("config.json", resources)
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
---
|
|
825
|
+
|
|
826
|
+
## Contributing
|
|
827
|
+
|
|
828
|
+
1. **Report Issues**: [Open an issue](https://github.com/winipedia/pyrig/issues)
|
|
829
|
+
2. **Suggest Features**: [Start a discussion](https://github.com/winipedia/pyrig/discussions)
|
|
830
|
+
3. **Submit Pull Requests**: Fork, create feature branch, make changes, run tests, commit, push, open PR
|
|
831
|
+
|
|
832
|
+
### Development Setup
|
|
833
|
+
|
|
834
|
+
```bash
|
|
835
|
+
git clone https://github.com/winipedia/pyrig.git
|
|
836
|
+
cd pyrig
|
|
837
|
+
uv sync
|
|
838
|
+
uv run pytest
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
---
|
|
842
|
+
|
|
843
|
+
## License
|
|
844
|
+
|
|
845
|
+
pyrig is licensed under the MIT License. See [LICENSE](LICENSE) for more information.
|
|
846
|
+
|
|
847
|
+
Copyright (c) 2025 Winipedia
|
|
848
|
+
|
|
849
|
+
---
|
|
850
|
+
|
|
851
|
+
## Links
|
|
852
|
+
|
|
853
|
+
- **Repository**: [github.com/winipedia/pyrig](https://github.com/winipedia/pyrig)
|
|
854
|
+
- **PyPI**: [pypi.org/project/pyrig](https://pypi.org/project/pyrig/)
|
|
855
|
+
|
|
856
|
+
---
|