qedro 0.2.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.
- qedro-0.2.0/CHANGELOG.md +158 -0
- qedro-0.2.0/CONTRIBUTING.md +146 -0
- qedro-0.2.0/LICENSE +202 -0
- qedro-0.2.0/MANIFEST.in +33 -0
- qedro-0.2.0/PKG-INFO +557 -0
- qedro-0.2.0/README.md +528 -0
- qedro-0.2.0/SECURITY.md +75 -0
- qedro-0.2.0/demo/README.md +208 -0
- qedro-0.2.0/demo/activities.yaml +39 -0
- qedro-0.2.0/demo/lineage/airflow.ndjson +84 -0
- qedro-0.2.0/demo/lineage/dbt.ndjson +90 -0
- qedro-0.2.0/demo/lineage/spark.ndjson +42 -0
- qedro-0.2.0/demo/lineage-declared/airflow.ndjson +84 -0
- qedro-0.2.0/demo/lineage-declared/dbt.ndjson +90 -0
- qedro-0.2.0/demo/lineage-declared/spark.ndjson +42 -0
- qedro-0.2.0/demo/qedro.yaml +49 -0
- qedro-0.2.0/docs/art30-facet.md +146 -0
- qedro-0.2.0/docs/compatibility.md +110 -0
- qedro-0.2.0/docs/evidence/README.md +33 -0
- qedro-0.2.0/docs/evidence/deployer-view.md +160 -0
- qedro-0.2.0/docs/evidence/fraud-event.trimmed.json +39 -0
- qedro-0.2.0/docs/evidence/fraud-events.captured.ndjson +2 -0
- qedro-0.2.0/docs/wordmark.svg +33 -0
- qedro-0.2.0/pyproject.toml +81 -0
- qedro-0.2.0/schemas/openlineage-art30-processing-facet.json +44 -0
- qedro-0.2.0/setup.cfg +4 -0
- qedro-0.2.0/src/qedro/__init__.py +9 -0
- qedro-0.2.0/src/qedro/__main__.py +505 -0
- qedro-0.2.0/src/qedro/config.py +248 -0
- qedro-0.2.0/src/qedro/declared.py +110 -0
- qedro-0.2.0/src/qedro/deployer.py +414 -0
- qedro-0.2.0/src/qedro/document.py +87 -0
- qedro-0.2.0/src/qedro/errors.py +37 -0
- qedro-0.2.0/src/qedro/events.py +257 -0
- qedro-0.2.0/src/qedro/mark.py +44 -0
- qedro-0.2.0/src/qedro/provenance.py +478 -0
- qedro-0.2.0/src/qedro/quality.py +438 -0
- qedro-0.2.0/src/qedro/render.py +973 -0
- qedro-0.2.0/src/qedro/ropa.py +561 -0
- qedro-0.2.0/src/qedro/scope.py +119 -0
- qedro-0.2.0/src/qedro/sources.py +443 -0
- qedro-0.2.0/src/qedro/vocabularies/__init__.py +0 -0
- qedro-0.2.0/src/qedro/vocabularies/dsgvo.yaml +62 -0
- qedro-0.2.0/src/qedro/vocabulary.py +169 -0
- qedro-0.2.0/src/qedro/words.py +31 -0
- qedro-0.2.0/src/qedro/xlsx.py +763 -0
- qedro-0.2.0/src/qedro.egg-info/PKG-INFO +557 -0
- qedro-0.2.0/src/qedro.egg-info/SOURCES.txt +78 -0
- qedro-0.2.0/src/qedro.egg-info/dependency_links.txt +1 -0
- qedro-0.2.0/src/qedro.egg-info/entry_points.txt +2 -0
- qedro-0.2.0/src/qedro.egg-info/requires.txt +8 -0
- qedro-0.2.0/src/qedro.egg-info/top_level.txt +1 -0
- qedro-0.2.0/tests/__init__.py +0 -0
- qedro-0.2.0/tests/conftest.py +27 -0
- qedro-0.2.0/tests/fixtures/airflow-3.3.1/README.md +30 -0
- qedro-0.2.0/tests/fixtures/airflow-3.3.1/events.jsonl +7 -0
- qedro-0.2.0/tests/fixtures/dbt-1.53/README.md +12 -0
- qedro-0.2.0/tests/fixtures/dbt-1.53/events.jsonl +10 -0
- qedro-0.2.0/tests/fixtures/events/README.md +11 -0
- qedro-0.2.0/tests/fixtures/events/pipeline-runtime.ndjson +4 -0
- qedro-0.2.0/tests/fixtures/spark-4.2.0/README.md +30 -0
- qedro-0.2.0/tests/fixtures/spark-4.2.0/events.jsonl +25 -0
- qedro-0.2.0/tests/lineage_api.py +87 -0
- qedro-0.2.0/tests/test_cli.py +596 -0
- qedro-0.2.0/tests/test_config.py +117 -0
- qedro-0.2.0/tests/test_demo.py +426 -0
- qedro-0.2.0/tests/test_deployer.py +368 -0
- qedro-0.2.0/tests/test_events.py +166 -0
- qedro-0.2.0/tests/test_facet_schema.py +126 -0
- qedro-0.2.0/tests/test_mark.py +37 -0
- qedro-0.2.0/tests/test_provenance.py +421 -0
- qedro-0.2.0/tests/test_quality.py +370 -0
- qedro-0.2.0/tests/test_render.py +538 -0
- qedro-0.2.0/tests/test_ropa.py +632 -0
- qedro-0.2.0/tests/test_sources.py +444 -0
- qedro-0.2.0/tests/test_vocabulary.py +130 -0
- qedro-0.2.0/tests/test_xlsx.py +207 -0
- qedro-0.2.0/tools/architecture_pdf.py +127 -0
- qedro-0.2.0/tools/facet_schema.py +193 -0
- qedro-0.2.0/tools/seed.py +624 -0
qedro-0.2.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes, in the words of somebody deciding whether to upgrade.
|
|
4
|
+
|
|
5
|
+
`0.x` may change interfaces between releases. The entries below say which, and
|
|
6
|
+
output formats and the `qedro.yaml` schema are the parts most likely to move
|
|
7
|
+
before `1.0`.
|
|
8
|
+
|
|
9
|
+
## 0.2.0 — 2026-09-18
|
|
10
|
+
|
|
11
|
+
> **First public release**, and the first version on PyPI.
|
|
12
|
+
>
|
|
13
|
+
> **0.1.0 was never released.** It was tagged locally on 2026-08-22 and deliberately
|
|
14
|
+
> not published, so that Qedro could first be run against real work. That run found
|
|
15
|
+
> the orchestration-parent, guessed-domain, Art. 30(1), file-naming and read-only
|
|
16
|
+
> changes below, and publishing 0.1.0 would have shipped a record that overstated
|
|
17
|
+
> what it covers. Everything that was in 0.1.0 is included in this entry, and there
|
|
18
|
+
> is no separate 0.1.0 to upgrade from.
|
|
19
|
+
|
|
20
|
+
First release. Three projections over OpenLineage, four output formats, and no
|
|
21
|
+
adoption of anything required beyond lineage a platform already emits.
|
|
22
|
+
|
|
23
|
+
### The reader
|
|
24
|
+
|
|
25
|
+
- `qedro events` reads a directory of `.json`, `.ndjson` or `.jsonl`, walking
|
|
26
|
+
nested directories, or any Marquez-compatible HTTP API. Any spelling of the
|
|
27
|
+
address works — a bare host, `.../api/v1`, or the full endpoint.
|
|
28
|
+
- The window is passed to the backend **and applied again to what comes back**,
|
|
29
|
+
so a backend that ignores it produces a correct result rather than a quietly
|
|
30
|
+
wider one.
|
|
31
|
+
- Reading stops after 10,000 events and says so, rather than returning a prefix
|
|
32
|
+
of the history that looks like all of it.
|
|
33
|
+
- Malformed records are skipped and counted, never raised on, and the count
|
|
34
|
+
reaches the completeness decision.
|
|
35
|
+
- Read-only by construction: `GET` is the only verb the code can build. A token
|
|
36
|
+
comes from `QEDRO_API_TOKEN` and never from the command line, where it would
|
|
37
|
+
land in shell history and in `ps`.
|
|
38
|
+
|
|
39
|
+
### `qedro ropa` — the Art. 30 record
|
|
40
|
+
|
|
41
|
+
- One activity per job, with the purpose and lawful basis that job declared and
|
|
42
|
+
**where each field came from**. A value from an emitted facet is evidence; the
|
|
43
|
+
same string from `qedro.yaml` is an assertion, and the two never merge.
|
|
44
|
+
- A facet always beats the mapping file. A config that could silently rewrite
|
|
45
|
+
emitted evidence would make the record unfalsifiable.
|
|
46
|
+
- `qedro.yaml` — controller, domains and job patterns — read as YAML, JSON or
|
|
47
|
+
TOML, with no behaviour depending on which.
|
|
48
|
+
- The Art. 30 processing facet is published as a spec in
|
|
49
|
+
`schemas/openlineage-art30-processing-facet.json`, generated from
|
|
50
|
+
`pipeline_runtime.descriptor.Processing` rather than written beside it.
|
|
51
|
+
- **Orchestration parents are not listed as activities.** A job whose run is named
|
|
52
|
+
as a parent through the standard `ParentRunFacet`, and which read and wrote nothing
|
|
53
|
+
and carries no `processing` facet, is named in the scope statement instead. The job
|
|
54
|
+
count still includes it, and it does not withhold the mark. Found against real dbt
|
|
55
|
+
lineage, where the invocation job showed up as an extra activity.
|
|
56
|
+
- **Activities that read datasets and wrote none are named** on a `read only` line in
|
|
57
|
+
the Art. 30 scope statement, and in JSON as `scope.read_only`. They stay listed, and
|
|
58
|
+
the mark is not withheld for them. Found against real Spark lineage, where the
|
|
59
|
+
integration emits a job for each schema-reading action.
|
|
60
|
+
- **File datasets print as a URI**, `file:///data/raw/orders.csv`, instead of
|
|
61
|
+
`file//data/raw/orders.csv`. The Airflow provider and the Spark integration emit
|
|
62
|
+
the bare namespace `file` where the OpenLineage naming conventions give
|
|
63
|
+
`file://{host}`, and an absolute path as the name; a name that starts with `/` is
|
|
64
|
+
no longer given a second one, which also covers HDFS. No other dataset key
|
|
65
|
+
changes, and `provenance --dataset` accepts the new spelling, the file name alone,
|
|
66
|
+
or the old spelling.
|
|
67
|
+
- **Real Airflow and Spark lineage as test fixtures**, beside the dbt capture:
|
|
68
|
+
Airflow 3.3.1 with the OpenLineage provider 2.20.1, and Spark 4.2.0 with
|
|
69
|
+
openlineage-spark 1.53.0. The parent rule leaves out the Airflow DAG run and the
|
|
70
|
+
Spark application run. `docs/compatibility.md` now names the captures, and no
|
|
71
|
+
longer says dbt, Airflow and Spark emit `sourceCodeLocation`, which none of the
|
|
72
|
+
captures carries.
|
|
73
|
+
- **The Art. 30 record states which Art. 30(1) items it has fields for**, on every
|
|
74
|
+
run including one that earns the mark: (a) the controller and (b) the purposes, and
|
|
75
|
+
none for (c) to (g). It is a scope line rather than a reason to withhold the mark,
|
|
76
|
+
because it would otherwise fire on every run. JSON carries `scope.art30` as lists of
|
|
77
|
+
covered and not covered letters. The deployer view does not repeat it.
|
|
78
|
+
- The text scope statement wraps long values under the value instead of running past
|
|
79
|
+
the terminal width.
|
|
80
|
+
- **A guessed domain is said to be guessed.** When `domains:` is set, the scope
|
|
81
|
+
statement names the domains guessed from the job namespace and those set by a
|
|
82
|
+
rule's `domain:`, and the silent-domain reason names the guess and the override.
|
|
83
|
+
JSON carries `domains_guessed`, `domains_mapped`, and a `domain_source` per
|
|
84
|
+
activity. Found against real dbt lineage, whose default namespace `dbt` made a
|
|
85
|
+
declared domain look silent.
|
|
86
|
+
- `--activities` reads a separate document of **declared activities**, meaning
|
|
87
|
+
processing that emits no lineage. They appear beside the evidenced activities with
|
|
88
|
+
every field marked `declared` and `no lineage` where the events would have spoken,
|
|
89
|
+
are counted on their own line in the scope statement without changing the evidenced
|
|
90
|
+
counts, and withhold the mark. The out-of-view sentence is reworded so that it is
|
|
91
|
+
true whether or not anything is declared.
|
|
92
|
+
|
|
93
|
+
### `qedro ropa --view deployer` — the same record, per AI use case
|
|
94
|
+
|
|
95
|
+
- For each activity whose runs reported a model version: purpose and legal basis,
|
|
96
|
+
the model versions with their run counts and dates, the inputs the latest run read,
|
|
97
|
+
and the span of run records in view.
|
|
98
|
+
- Built from the Art. 30 record object, so purpose and legal basis are the record's
|
|
99
|
+
own entries with their provenance, not a second reading of the events.
|
|
100
|
+
- The model version is read from the standard OpenLineage `tags` run facet (key
|
|
101
|
+
`model_version`) or from `cordata_provenance.step_params`, where `pipeline-runtime`
|
|
102
|
+
puts it.
|
|
103
|
+
- Art. 26 of the AI Act is cited from the consolidated text (CELEX
|
|
104
|
+
02024R1689-20260727) with its condition: Annex III high-risk systems, from
|
|
105
|
+
2 December 2027. The view does not decide whether a use case is high-risk.
|
|
106
|
+
- Retention is reported as the span of run records in view, never as a retention
|
|
107
|
+
policy, and a short span does not withhold the mark.
|
|
108
|
+
- Declared activities that name a `model` are listed as AI use cases, marked
|
|
109
|
+
`declared, no lineage`, and withhold the mark.
|
|
110
|
+
|
|
111
|
+
### `qedro quality` — the assertion history
|
|
112
|
+
|
|
113
|
+
- What was checked about each dataset and when, from the standard OpenLineage
|
|
114
|
+
`dataQualityAssertions` facet that Great Expectations already emits.
|
|
115
|
+
- **Datasets with no assertions are half the artefact**, not a footnote: a
|
|
116
|
+
dataset with no failures and a dataset with no checks look identical in every
|
|
117
|
+
summary anybody writes, and they are opposites.
|
|
118
|
+
- A *failing* expectation does not withhold the mark. The mark says this account
|
|
119
|
+
of what was checked is complete, not that the data is good.
|
|
120
|
+
- `--domain` narrows it, repeatably; `--days` is shorthand for the window. The scope
|
|
121
|
+
statement names the filter and how many datasets it left out, in which domains, and
|
|
122
|
+
whether each domain was guessed from the job namespace. A filter that leaves out
|
|
123
|
+
every dataset says so, instead of reporting that no datasets were found.
|
|
124
|
+
|
|
125
|
+
### `qedro provenance` — the chain back to a commit
|
|
126
|
+
|
|
127
|
+
- Walks backwards from a dataset through the runs that produced it, the code
|
|
128
|
+
each run executed and the commit it was at, using the standard
|
|
129
|
+
`sourceCodeLocation` facet that dbt, Airflow and Spark all emit.
|
|
130
|
+
- **Unknown is not unsigned.** OpenLineage has no standard place for whether a
|
|
131
|
+
commit was signed, so silence is a third state: it withholds the mark exactly
|
|
132
|
+
as a failed signature would and is never rendered as one. `json` emits `null`,
|
|
133
|
+
not `false`.
|
|
134
|
+
- A chain ends for three different reasons — a genuine source, a producing run
|
|
135
|
+
outside the window, or the depth limit — and each is counted and named.
|
|
136
|
+
- An ambiguous `--dataset` is answered with the candidates rather than a guess.
|
|
137
|
+
|
|
138
|
+
### Output
|
|
139
|
+
|
|
140
|
+
- `text`, `markdown`, `json` and `xlsx`. The format follows `--out`'s filename
|
|
141
|
+
unless `--format` says otherwise.
|
|
142
|
+
- **xlsx puts the verdict above the table on the first sheet.** A caveat below
|
|
143
|
+
the data is a caveat nobody reads.
|
|
144
|
+
- Every format states the scope of what was looked at, on every run including
|
|
145
|
+
one that earns the mark.
|
|
146
|
+
|
|
147
|
+
### The mark
|
|
148
|
+
|
|
149
|
+
- `∎` is printed only when an artefact stands on its own evidence, and the
|
|
150
|
+
reasons for withholding it are always printed with it. `--no-symbol`, or
|
|
151
|
+
`QEDRO_NO_SYMBOL=1`, for terminals with no glyph for U+220E.
|
|
152
|
+
|
|
153
|
+
### Also
|
|
154
|
+
|
|
155
|
+
- `demo/` — three weeks of synthetic lineage from dbt, Airflow and Spark, with
|
|
156
|
+
no AWS anywhere in it, in two estates that differ only in whether the
|
|
157
|
+
pipelines declare their Art. 30 fields.
|
|
158
|
+
- Apache-2.0. Python 3.12, 3.13 and 3.14.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Issues and pull requests are welcome. Most of this page is about scope and about
|
|
4
|
+
four commitments that look like details and are not, because the quickest way to
|
|
5
|
+
waste an afternoon here is to send something good that cannot be merged.
|
|
6
|
+
|
|
7
|
+
## What this repo is for
|
|
8
|
+
|
|
9
|
+
Producing compliance artefacts from evidence a platform already emits, and being
|
|
10
|
+
honest about the parts it cannot evidence. That second half decides most of what
|
|
11
|
+
follows.
|
|
12
|
+
|
|
13
|
+
## Especially welcome
|
|
14
|
+
|
|
15
|
+
**A case where the tombstone is printed and should not be.** The mark means *this
|
|
16
|
+
artefact stands on its own evidence*. If you can construct a run that earns it
|
|
17
|
+
while something is unproven, that is the most valuable bug report this project
|
|
18
|
+
can receive, and a failing test is worth more than a fix.
|
|
19
|
+
|
|
20
|
+
**Lineage from an emitter we have not seen.** Production events disagree with the
|
|
21
|
+
spec in ways nobody predicts. A file that the reader mishandles — or handles
|
|
22
|
+
while quietly skipping records — is exactly what `tests/fixtures/` is for.
|
|
23
|
+
|
|
24
|
+
**A projection's output read by somebody who does the job.** A DPO, an auditor, a
|
|
25
|
+
data steward saying *this column is not what I would ask for* is worth more than
|
|
26
|
+
any amount of internal reasoning about it.
|
|
27
|
+
|
|
28
|
+
**A source adapter.** `sources.read()` dispatches on scheme behind one entry
|
|
29
|
+
point. A new source yields `Event` objects and stores nothing.
|
|
30
|
+
|
|
31
|
+
## Out of scope
|
|
32
|
+
|
|
33
|
+
- **Any write path into a system Qedro reads.** See below; this is not
|
|
34
|
+
negotiable.
|
|
35
|
+
- **A server, a database, a UI.** Not because they are bad ideas — there is a
|
|
36
|
+
design for them — but because they are not this repository. It stays a CLI
|
|
37
|
+
that reads evidence and writes a file.
|
|
38
|
+
- **Resolving a commit or a signature over the network.** `provenance` reports
|
|
39
|
+
what an emitter reported. A projection that phoned a forge would have acquired
|
|
40
|
+
a credential and a network dependency, and the tool would no longer be
|
|
41
|
+
read-only by construction.
|
|
42
|
+
- **Modelling more facets as types.** Core event fields are typed; facets stay
|
|
43
|
+
raw dictionaries. See the rule below.
|
|
44
|
+
|
|
45
|
+
## Four commitments that must not erode
|
|
46
|
+
|
|
47
|
+
Each of these has been argued out, and each will look like an unnecessary
|
|
48
|
+
constraint at some point.
|
|
49
|
+
|
|
50
|
+
**Classification is source-neutral.** LakeFormation is one adapter beside GCP
|
|
51
|
+
Data Catalog policy tags, Purview classifications and OpenMetadata terms — never
|
|
52
|
+
the shape of the model, and never a name in the code. The acceptance test is
|
|
53
|
+
that `qedro ropa` runs against a Marquez instance with no AWS anywhere
|
|
54
|
+
(`tests/test_cli.py::TestRopaAgainstAnApi`).
|
|
55
|
+
|
|
56
|
+
**The vocabulary is data the tool loads, never types it is compiled against.**
|
|
57
|
+
`src/qedro/vocabularies/dsgvo.yaml` is a starting point, not a standard. Point
|
|
58
|
+
`--vocabulary` at your own and the shipped one is replaced wholesale. Nothing in
|
|
59
|
+
the code knows what a lawful basis is.
|
|
60
|
+
|
|
61
|
+
**The tombstone is earned, not printed.** It is withheld whenever any part of an
|
|
62
|
+
artefact rests on assertion rather than evidence, and **withholding is the
|
|
63
|
+
signal** — a mark printed on every successful run says nothing. Do not make it
|
|
64
|
+
unconditional, and do not add a flag that forces it.
|
|
65
|
+
|
|
66
|
+
**Read-only by construction.** No write path in any handler, and no credential
|
|
67
|
+
that could acquire one. This is a property of what the code is able to reach,
|
|
68
|
+
not a policy it follows. The HTTP source can only build `GET`.
|
|
69
|
+
|
|
70
|
+
## Ground rules
|
|
71
|
+
|
|
72
|
+
**Facets stay raw dictionaries.** OpenLineage has a long tail of them and every
|
|
73
|
+
emitter populates a different subset, so a projection asks for the facet it
|
|
74
|
+
wants and copes with absence. Typing them means breaking whenever one moves.
|
|
75
|
+
|
|
76
|
+
**Parsing never raises.** A directory of production lineage contains events that
|
|
77
|
+
disagree with the spec, and a reader that dies on the first is useless. Bad
|
|
78
|
+
records are skipped and **counted**, and the count reaches the completeness
|
|
79
|
+
decision. That chain is relied upon, not diagnostics.
|
|
80
|
+
|
|
81
|
+
**What the user wrote does raise.** A config typo silently produces an artefact
|
|
82
|
+
with a hole in it, which is worse than a traceback. `errors.py` is the line
|
|
83
|
+
between the two.
|
|
84
|
+
|
|
85
|
+
**Every artefact states its own scope**, on every run, including one that earns
|
|
86
|
+
the mark. A scope statement that appeared only when coverage was poor would
|
|
87
|
+
teach a reader that its absence means full coverage. See
|
|
88
|
+
[#3](https://github.com/cordata-tech/qedro/issues/3).
|
|
89
|
+
|
|
90
|
+
**Two files are generated and CI checks both.** `schemas/openlineage-art30-processing-facet.json`
|
|
91
|
+
comes from `pipeline_runtime.descriptor.Processing` via `tools/facet_schema.py`,
|
|
92
|
+
and `demo/` comes from `tools/seed.py`. Editing either by hand looks like it
|
|
93
|
+
worked until the next regeneration reverts it.
|
|
94
|
+
|
|
95
|
+
## Running things
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install -e ".[dev]"
|
|
99
|
+
pytest -q
|
|
100
|
+
ruff check . && ruff format --check .
|
|
101
|
+
python tools/facet_schema.py --check
|
|
102
|
+
python tools/seed.py --check
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Python 3.12 is the floor; CI runs 3.12, 3.13 and 3.14.
|
|
106
|
+
|
|
107
|
+
Two test modules import `pipeline_runtime`, which is not on PyPI — the Art. 30
|
|
108
|
+
facet schema is generated from its `Processing` model and a test fails when the
|
|
109
|
+
two diverge. Install it the way CI does, or skip those two:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pip install --no-deps "cordata-pipeline-runtime @ git+https://github.com/cordata-tech/pipeline-runtime@main"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The import is unconditional on purpose. A skip when the module is absent would
|
|
116
|
+
mean CI silently stops checking the thing the test exists for.
|
|
117
|
+
|
|
118
|
+
## Commits and pull requests
|
|
119
|
+
|
|
120
|
+
**Explain the reasoning, not the diff.** The diff is in the diff. A commit
|
|
121
|
+
message here is for the person who finds this code in a year and wants to know
|
|
122
|
+
why it is shaped this way — especially when the answer is *we tried the obvious
|
|
123
|
+
thing and it was wrong*.
|
|
124
|
+
|
|
125
|
+
**Sign your commits.** `git config commit.gpgsign true` with an SSH or GPG key.
|
|
126
|
+
A project about provenance that accepts unsigned commits is making a point it
|
|
127
|
+
would rather not.
|
|
128
|
+
|
|
129
|
+
**Tests that read as sentences.** Test names in this repository describe the
|
|
130
|
+
behaviour being protected, not the function being called, and the ones guarding a
|
|
131
|
+
commitment say which commitment. Follow that; it is why the suite is readable.
|
|
132
|
+
|
|
133
|
+
## Releases
|
|
134
|
+
|
|
135
|
+
`0.x` versions may change interfaces between releases, and the changelog says
|
|
136
|
+
which. Output formats and the `qedro.yaml` schema are the parts most likely to
|
|
137
|
+
move before `1.0`.
|
|
138
|
+
|
|
139
|
+
Releases happen when something is worth releasing rather than on a calendar, and
|
|
140
|
+
each one is a tag, a changelog entry and a PyPI artefact. `main` is expected to
|
|
141
|
+
be releasable at any point: CI runs on every push, and a red `main` is a bug
|
|
142
|
+
before it is anything else.
|
|
143
|
+
|
|
144
|
+
Pushing a `v*` tag is what publishes. The workflow refuses to ship if the tag
|
|
145
|
+
disagrees with `qedro.__version__`, or if the changelog entry for that version
|
|
146
|
+
is missing or still marked unreleased.
|
qedro-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
qedro-0.2.0/MANIFEST.in
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# What goes in the source distribution.
|
|
2
|
+
#
|
|
3
|
+
# The wheel is the code. The sdist is the code plus everything needed to check
|
|
4
|
+
# that the code does what it says — which for this project is not a nicety. A
|
|
5
|
+
# README that argues claims should be verifiable, shipped in an artefact where
|
|
6
|
+
# they cannot be, would be making the opposite point.
|
|
7
|
+
#
|
|
8
|
+
# So `pytest` on an unpacked sdist runs the same 401 tests CI does — verified,
|
|
9
|
+
# not assumed. `demo/` is present because `tests/test_demo.py` asserts against
|
|
10
|
+
# those exact bytes, and `tools/` because two test modules import it. Twenty-
|
|
11
|
+
# seven of those tests also need `pipeline_runtime`, which is a dev dependency
|
|
12
|
+
# installed from git; without it the other 374 still run.
|
|
13
|
+
|
|
14
|
+
include CHANGELOG.md CONTRIBUTING.md SECURITY.md
|
|
15
|
+
graft tests
|
|
16
|
+
graft tools
|
|
17
|
+
graft schemas
|
|
18
|
+
graft docs
|
|
19
|
+
|
|
20
|
+
# Three weeks of synthetic lineage, and the reason it is worth the megabyte:
|
|
21
|
+
# without it the shipped tests cannot run and the README's demo is a promise
|
|
22
|
+
# the artefact does not keep.
|
|
23
|
+
graft demo
|
|
24
|
+
|
|
25
|
+
# `docs/architecture.md` is a proposal, not a description, and is deliberately
|
|
26
|
+
# untracked until the target architecture is agreed. It sits in the working
|
|
27
|
+
# tree, which is exactly where `graft docs` would find it — so it is excluded
|
|
28
|
+
# by name rather than by hoping nobody builds an sdist from a dirty checkout.
|
|
29
|
+
exclude docs/architecture.md
|
|
30
|
+
exclude docs/architecture.pdf
|
|
31
|
+
|
|
32
|
+
global-exclude *.py[cod] __pycache__/* .DS_Store
|
|
33
|
+
prune **/__pycache__
|