prefector 0.1.0__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.
- prefector-0.1.0/.github/workflows/lint.yml +37 -0
- prefector-0.1.0/.github/workflows/pypi-publish.yml +51 -0
- prefector-0.1.0/.github/workflows/test.yml +30 -0
- prefector-0.1.0/.gitignore +12 -0
- prefector-0.1.0/.pre-commit-config.yaml +32 -0
- prefector-0.1.0/.talismanrc +5 -0
- prefector-0.1.0/PKG-INFO +285 -0
- prefector-0.1.0/README.md +266 -0
- prefector-0.1.0/poetry.lock +4494 -0
- prefector-0.1.0/pyproject.toml +55 -0
- prefector-0.1.0/src/prefector/__init__.py +5 -0
- prefector-0.1.0/src/prefector/blocks/__init__.py +0 -0
- prefector-0.1.0/src/prefector/blocks/base.py +137 -0
- prefector-0.1.0/src/prefector/blocks/cli.py +190 -0
- prefector-0.1.0/src/prefector/blocks/options.py +39 -0
- prefector-0.1.0/src/prefector/blocks/sources.py +237 -0
- prefector-0.1.0/src/prefector/cli.py +20 -0
- prefector-0.1.0/src/prefector/deployments/__init__.py +0 -0
- prefector-0.1.0/src/prefector/deployments/base.py +160 -0
- prefector-0.1.0/src/prefector/deployments/cli.py +177 -0
- prefector-0.1.0/src/prefector/deployments/options.py +65 -0
- prefector-0.1.0/src/prefector/errors.py +21 -0
- prefector-0.1.0/src/prefector/prefect_connection/__init__.py +0 -0
- prefector-0.1.0/src/prefector/prefect_connection/connection.py +107 -0
- prefector-0.1.0/src/prefector/prefect_connection/options.py +63 -0
- prefector-0.1.0/tests/blocks/__init__.py +0 -0
- prefector-0.1.0/tests/blocks/test_block_sources.py +358 -0
- prefector-0.1.0/tests/blocks/test_blocks_base.py +89 -0
- prefector-0.1.0/tests/blocks/test_blocks_cli.py +67 -0
- prefector-0.1.0/tests/cli/__init__.py +0 -0
- prefector-0.1.0/tests/cli/test_blocks_cli.py +23 -0
- prefector-0.1.0/tests/cli/test_cli.py +78 -0
- prefector-0.1.0/tests/cli/test_prefect_options.py +138 -0
- prefector-0.1.0/tests/conftest.py +88 -0
- prefector-0.1.0/tests/deployments/__init__.py +0 -0
- prefector-0.1.0/tests/deployments/test_deployments_base.py +242 -0
- prefector-0.1.0/tests/deployments/test_deployments_cli.py +113 -0
- prefector-0.1.0/tests/prefect_connection/__init__.py +0 -0
- prefector-0.1.0/tests/prefect_connection/test_prefect_connection.py +239 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Lint
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
ruff:
|
|
11
|
+
name: Ruff
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Check out repository
|
|
16
|
+
uses: actions/checkout@v6
|
|
17
|
+
|
|
18
|
+
- name: Run ruff check
|
|
19
|
+
uses: astral-sh/ruff-action@v3
|
|
20
|
+
|
|
21
|
+
- name: Run ruff format
|
|
22
|
+
uses: astral-sh/ruff-action@v3
|
|
23
|
+
with:
|
|
24
|
+
args: "format --check"
|
|
25
|
+
|
|
26
|
+
shellcheck:
|
|
27
|
+
name: Shellcheck
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
|
|
30
|
+
steps:
|
|
31
|
+
- name: Check out repository
|
|
32
|
+
uses: actions/checkout@v6
|
|
33
|
+
|
|
34
|
+
- name: Run Shellcheck
|
|
35
|
+
uses: ludeeus/action-shellcheck@2.0.0
|
|
36
|
+
env:
|
|
37
|
+
SHELLCHECK_OPTS: --severity=warning --external-sources
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
release-build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.x"
|
|
20
|
+
|
|
21
|
+
- name: Build release distributions
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install build
|
|
24
|
+
python -m build
|
|
25
|
+
|
|
26
|
+
- name: Upload distributions
|
|
27
|
+
uses: actions/upload-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: release-dists
|
|
30
|
+
path: dist/
|
|
31
|
+
|
|
32
|
+
pypi-publish:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
needs:
|
|
35
|
+
- release-build
|
|
36
|
+
permissions:
|
|
37
|
+
id-token: write
|
|
38
|
+
environment:
|
|
39
|
+
name: pypi
|
|
40
|
+
# Add pyPI URL when known
|
|
41
|
+
steps:
|
|
42
|
+
- name: Retrieve release distributions
|
|
43
|
+
uses: actions/download-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: release-dists
|
|
46
|
+
path: dist/
|
|
47
|
+
|
|
48
|
+
- name: Publish release distributions to PyPI
|
|
49
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
50
|
+
with:
|
|
51
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
pytest:
|
|
11
|
+
name: Pytest
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Check out repository
|
|
16
|
+
uses: actions/checkout@v6
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.13"
|
|
22
|
+
|
|
23
|
+
- name: Install Poetry
|
|
24
|
+
uses: snok/install-poetry@v1
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: poetry install
|
|
28
|
+
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: poetry run pytest
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
default_language_version:
|
|
2
|
+
python: python3.13
|
|
3
|
+
repos:
|
|
4
|
+
- repo: local
|
|
5
|
+
hooks:
|
|
6
|
+
- id: poetry-lock
|
|
7
|
+
name: "Poetry lock"
|
|
8
|
+
entry: bash -c 'poetry lock'
|
|
9
|
+
language: system
|
|
10
|
+
always_run: true
|
|
11
|
+
- repo: https://github.com/thoughtworks/talisman
|
|
12
|
+
rev: "v1.32.0"
|
|
13
|
+
hooks:
|
|
14
|
+
- id: talisman-commit
|
|
15
|
+
name: "Talisman (secrets detection)"
|
|
16
|
+
entry: cmd --githook pre-commit
|
|
17
|
+
- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
|
18
|
+
rev: 3.0.0
|
|
19
|
+
hooks:
|
|
20
|
+
- id: shellcheck
|
|
21
|
+
name: "Shellcheck (shell script checker)"
|
|
22
|
+
args:
|
|
23
|
+
- --severity=warning
|
|
24
|
+
- --external-sources
|
|
25
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
26
|
+
rev: v0.15.8
|
|
27
|
+
hooks:
|
|
28
|
+
- id: ruff
|
|
29
|
+
name: "Ruff (Python linting and import sorting)"
|
|
30
|
+
args: ["--fix"]
|
|
31
|
+
- id: ruff-format
|
|
32
|
+
name: "Ruff (Python formatter)"
|
prefector-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: prefector
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reusable Prefect block and deployment CLI.
|
|
5
|
+
Requires-Python: <3.14,>=3.11
|
|
6
|
+
Requires-Dist: click-option-group<0.6.0,>=0.5.9
|
|
7
|
+
Requires-Dist: prefect-aws<0.8.0,>=0.6.9
|
|
8
|
+
Requires-Dist: prefect<4,>=3
|
|
9
|
+
Requires-Dist: pydantic-settings
|
|
10
|
+
Requires-Dist: pydantic>=2
|
|
11
|
+
Requires-Dist: pyyaml~=6.0.3
|
|
12
|
+
Requires-Dist: requests~=2.33.1
|
|
13
|
+
Requires-Dist: rich
|
|
14
|
+
Provides-Extra: all
|
|
15
|
+
Requires-Dist: keeper-secrets-manager-core; extra == 'all'
|
|
16
|
+
Provides-Extra: keeper
|
|
17
|
+
Requires-Dist: keeper-secrets-manager-core; extra == 'keeper'
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# Prefector
|
|
21
|
+
|
|
22
|
+
[](https://github.com/sanger-pathogens/prefector/actions/workflows/test.yml)
|
|
23
|
+
|
|
24
|
+
Reusable CLI helpers for deploying Prefect blocks and deployments from downstream
|
|
25
|
+
project specs.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
Install `prefector` into the same Python environment as the block specs, flow
|
|
30
|
+
modules, and Prefect collection packages it needs to import.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install prefector
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
prefector blocks list --blocks-dir path/to/block/specs
|
|
40
|
+
prefector blocks deploy --blocks-dir path/to/block/specs --api-url "$PREFECT_API_URL"
|
|
41
|
+
|
|
42
|
+
prefector deployments list --deployments-dir path/to/deployment/specs
|
|
43
|
+
|
|
44
|
+
prefector deployments deploy \
|
|
45
|
+
--deployments-dir path/to/deployment/specs \
|
|
46
|
+
--images-manifest path/to/images.yaml \
|
|
47
|
+
--api-url "$PREFECT_API_URL" \
|
|
48
|
+
--work-pool default \
|
|
49
|
+
--image-prefix ghcr.io/example
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Block spec modules must expose `BLOCKS: list[prefector.BlockSpec]`.
|
|
53
|
+
Deployment specs are YAML files loaded as `prefector.DeploymentSpec`.
|
|
54
|
+
|
|
55
|
+
## Block specs
|
|
56
|
+
|
|
57
|
+
Each block spec is a Python module in the `--blocks-dir` directory. A module
|
|
58
|
+
must expose a `BLOCKS` list of `BlockSpec` objects, each pairing a
|
|
59
|
+
`pydantic_settings.BaseSettings` subclass (which reads field values from the
|
|
60
|
+
environment) with a Prefect `Block` subclass.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# blocks/trino.py
|
|
64
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
65
|
+
from prefect_sqlalchemy import DatabaseCredentials, SyncDriver
|
|
66
|
+
from prefector.blocks.base import BlockSpec
|
|
67
|
+
|
|
68
|
+
class TrinoSettings(BaseSettings):
|
|
69
|
+
model_config = SettingsConfigDict(env_prefix="TRINO_")
|
|
70
|
+
user: str
|
|
71
|
+
password: str
|
|
72
|
+
host: str
|
|
73
|
+
port: int = 8080
|
|
74
|
+
|
|
75
|
+
class TrinoBlock(DatabaseCredentials):
|
|
76
|
+
...
|
|
77
|
+
|
|
78
|
+
BLOCKS = [BlockSpec(name="trino-credentials", settings_cls=TrinoSettings, block_cls=TrinoBlock)]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
When `prefector blocks deploy` runs, it instantiates `TrinoSettings()` — which
|
|
82
|
+
reads `TRINO_USER`, `TRINO_PASSWORD`, etc. from the environment — and passes the
|
|
83
|
+
values to the block.
|
|
84
|
+
|
|
85
|
+
## Block sources
|
|
86
|
+
|
|
87
|
+
A `block-sources.yaml` file lets different teams use the same block spec modules
|
|
88
|
+
while sourcing secret values from different backends (environment variables or
|
|
89
|
+
Keeper Secrets Manager) and with different field naming conventions.
|
|
90
|
+
|
|
91
|
+
**It is not required.** Blocks that already define their own `settings_cls` with
|
|
92
|
+
the right env prefix will continue to work exactly as before. Only add a
|
|
93
|
+
block-sources entry when you need to override where values come from.
|
|
94
|
+
|
|
95
|
+
### Loading
|
|
96
|
+
|
|
97
|
+
Provide the file explicitly:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
prefector blocks deploy --blocks-dir path/to/specs --sources path/to/block-sources.yaml
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Or place it at `block-sources.yaml` inside `--blocks-dir` and it will be picked
|
|
104
|
+
up automatically with no extra flags needed.
|
|
105
|
+
|
|
106
|
+
### File format
|
|
107
|
+
|
|
108
|
+
Three equivalent YAML shapes are accepted:
|
|
109
|
+
|
|
110
|
+
**Flat mapping** (simplest):
|
|
111
|
+
```yaml
|
|
112
|
+
trino-credentials:
|
|
113
|
+
source: env
|
|
114
|
+
env_var_prefix: TRINO_
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**List** (useful when ordering matters or you prefer the list style):
|
|
118
|
+
```yaml
|
|
119
|
+
- trino-credentials:
|
|
120
|
+
source: env
|
|
121
|
+
env_var_prefix: TRINO_
|
|
122
|
+
- other-block:
|
|
123
|
+
source: keeper
|
|
124
|
+
record_title: trino-credentials
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**`blocks:` wrapper** (same list, under an explicit key):
|
|
128
|
+
```yaml
|
|
129
|
+
blocks:
|
|
130
|
+
- trino-credentials:
|
|
131
|
+
source: env
|
|
132
|
+
env_var_prefix: TRINO_
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Environment variable source
|
|
136
|
+
|
|
137
|
+
Reads block field values from environment variables.
|
|
138
|
+
|
|
139
|
+
```yaml
|
|
140
|
+
trino-credentials:
|
|
141
|
+
source: env
|
|
142
|
+
env_var_prefix: TRINO_ # env vars are read as <prefix><field>
|
|
143
|
+
fields: # optional: override individual field names
|
|
144
|
+
user: USERNAME # reads TRINO_USERNAME into field `user`
|
|
145
|
+
password: PASSWORD # reads TRINO_PASSWORD into field `password`
|
|
146
|
+
# unlisted fields use the field name as-is: `host` -> TRINO_host
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The `fields` mapping is optional. Without it, each block field is read from
|
|
150
|
+
`<env_var_prefix><field_name>` (case-insensitive). Only add `fields` entries
|
|
151
|
+
when the env var suffix differs from the field name.
|
|
152
|
+
|
|
153
|
+
If a required env var is missing, the command exits with a clear error naming
|
|
154
|
+
the variable that needs to be set.
|
|
155
|
+
|
|
156
|
+
### Keeper Secrets Manager source
|
|
157
|
+
|
|
158
|
+
Reads block field values from a record in Keeper Secrets Manager.
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
trino-credentials:
|
|
162
|
+
source: keeper
|
|
163
|
+
record_title: trino-credentials # required: base record title
|
|
164
|
+
record_prefix: dlh # optional: prepended before title
|
|
165
|
+
record_suffix: ${ENVIRONMENT} # optional: appended after title
|
|
166
|
+
separator: ":" # optional: joins the parts (default: ":"); must be quoted in YAML
|
|
167
|
+
ksm_token: ${KSM_TOKEN} # optional: one-time token; falls back to KSM_CONFIG env var
|
|
168
|
+
fields: # optional: map block field -> KSM field title
|
|
169
|
+
user: login # reads KSM field "login" into block field `user`
|
|
170
|
+
# unlisted fields use the field name as-is
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The full record title is assembled as
|
|
174
|
+
`<record_prefix><separator><record_title><separator><record_suffix>`, with any
|
|
175
|
+
absent components skipped cleanly (no leading or trailing separator):
|
|
176
|
+
|
|
177
|
+
The Keeper SDK (`keeper-secrets-manager-core`) must be installed to read values from Keeper. The
|
|
178
|
+
extra `prefector[keeper]` provides this.
|
|
179
|
+
|
|
180
|
+
### Environment variable substitution
|
|
181
|
+
|
|
182
|
+
Any string value in `block-sources.yaml` may use `${VAR_NAME}` syntax.
|
|
183
|
+
Substitution happens at build time (when `prefector blocks deploy` runs), so
|
|
184
|
+
you can parameterise record names, prefixes, or tokens from CI environment
|
|
185
|
+
variables:
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
trino-credentials:
|
|
189
|
+
source: keeper
|
|
190
|
+
record_title: trino-credentials
|
|
191
|
+
record_suffix: ${ENVIRONMENT} # e.g. resolves to "prod" or "staging"
|
|
192
|
+
ksm_token: ${KSM_TOKEN}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
All referenced variables must be set at deploy time, or the command will exit
|
|
196
|
+
with an error naming the missing variable.
|
|
197
|
+
|
|
198
|
+
## Deployment spec
|
|
199
|
+
|
|
200
|
+
Each deployment is a YAML file. All fields except `name`, `flow`, and `image_key` are optional.
|
|
201
|
+
|
|
202
|
+
```yaml
|
|
203
|
+
name: my_deployment
|
|
204
|
+
flow: flows.my_module:my_flow # <module>:<function> format
|
|
205
|
+
image_key: flow_runtime # key from images manifest
|
|
206
|
+
|
|
207
|
+
cron: "0 6 * * *" # standard cron expression
|
|
208
|
+
tags:
|
|
209
|
+
- project_name
|
|
210
|
+
- bronze
|
|
211
|
+
parameters:
|
|
212
|
+
retries: 3
|
|
213
|
+
bucket:
|
|
214
|
+
block: my-s3-bucket # load a Prefect block by name at run time
|
|
215
|
+
env:
|
|
216
|
+
ENVIRONMENT: ${ENVIRONMENT} # resolved from the environment at deploy time
|
|
217
|
+
LOG_LEVEL: INFO
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Environment variable substitution
|
|
221
|
+
|
|
222
|
+
Values in the form `${VAR_NAME}` are replaced with the corresponding environment
|
|
223
|
+
variable when the spec is loaded. This happens at deploy time (e.g. in CI), not
|
|
224
|
+
at flow run time.
|
|
225
|
+
|
|
226
|
+
```yaml
|
|
227
|
+
env:
|
|
228
|
+
COMMIT_SHA: ${CI_COMMIT_SHORT_SHA}
|
|
229
|
+
PROJECT: ${PROJECT_NAME}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
All referenced variables must be set when `prefector deployments deploy` runs, or
|
|
233
|
+
the command will exit with an error naming the missing variable.
|
|
234
|
+
|
|
235
|
+
**Using environment variables in the deployment spec:**
|
|
236
|
+
|
|
237
|
+
- Only `${VAR}` brace syntax is supported. A bare `$VAR` is left as-is.
|
|
238
|
+
- Substitution happens on the raw text before YAML parsing. If a variable value
|
|
239
|
+
contains YAML special characters (`:`, `{`, `}`, `#`), it can produce invalid
|
|
240
|
+
YAML. Quote the value to be safe:
|
|
241
|
+
```yaml
|
|
242
|
+
env:
|
|
243
|
+
LABEL: "${MY_LABEL}"
|
|
244
|
+
```
|
|
245
|
+
- Resolved values are stored in Prefect as `job_variables` and are visible in the
|
|
246
|
+
Prefect UI. Avoid substituting secrets this way; use Prefect blocks instead.
|
|
247
|
+
- Environment variables are resolved only for deployments that are actually being
|
|
248
|
+
deployed. Untargeted deployments (filtered by `--target`) and the `list`
|
|
249
|
+
command do not require any variables to be set.
|
|
250
|
+
|
|
251
|
+
## Development
|
|
252
|
+
|
|
253
|
+
Setup local environment
|
|
254
|
+
|
|
255
|
+
Install project dependencies:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
poetry env use 3.12
|
|
259
|
+
source .venv/bin/activate
|
|
260
|
+
poetry install --with dev
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Set up pre-commit hooks and linting:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
pre-commit install
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
This will run pre-commit hooks on every commit. To run pre-commit manually, use
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
pre commit run -a
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Run tests with:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
pytest
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
With coverage:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
pytest --cov=src/prefector
|
|
285
|
+
```
|