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.
Files changed (80) hide show
  1. qedro-0.2.0/CHANGELOG.md +158 -0
  2. qedro-0.2.0/CONTRIBUTING.md +146 -0
  3. qedro-0.2.0/LICENSE +202 -0
  4. qedro-0.2.0/MANIFEST.in +33 -0
  5. qedro-0.2.0/PKG-INFO +557 -0
  6. qedro-0.2.0/README.md +528 -0
  7. qedro-0.2.0/SECURITY.md +75 -0
  8. qedro-0.2.0/demo/README.md +208 -0
  9. qedro-0.2.0/demo/activities.yaml +39 -0
  10. qedro-0.2.0/demo/lineage/airflow.ndjson +84 -0
  11. qedro-0.2.0/demo/lineage/dbt.ndjson +90 -0
  12. qedro-0.2.0/demo/lineage/spark.ndjson +42 -0
  13. qedro-0.2.0/demo/lineage-declared/airflow.ndjson +84 -0
  14. qedro-0.2.0/demo/lineage-declared/dbt.ndjson +90 -0
  15. qedro-0.2.0/demo/lineage-declared/spark.ndjson +42 -0
  16. qedro-0.2.0/demo/qedro.yaml +49 -0
  17. qedro-0.2.0/docs/art30-facet.md +146 -0
  18. qedro-0.2.0/docs/compatibility.md +110 -0
  19. qedro-0.2.0/docs/evidence/README.md +33 -0
  20. qedro-0.2.0/docs/evidence/deployer-view.md +160 -0
  21. qedro-0.2.0/docs/evidence/fraud-event.trimmed.json +39 -0
  22. qedro-0.2.0/docs/evidence/fraud-events.captured.ndjson +2 -0
  23. qedro-0.2.0/docs/wordmark.svg +33 -0
  24. qedro-0.2.0/pyproject.toml +81 -0
  25. qedro-0.2.0/schemas/openlineage-art30-processing-facet.json +44 -0
  26. qedro-0.2.0/setup.cfg +4 -0
  27. qedro-0.2.0/src/qedro/__init__.py +9 -0
  28. qedro-0.2.0/src/qedro/__main__.py +505 -0
  29. qedro-0.2.0/src/qedro/config.py +248 -0
  30. qedro-0.2.0/src/qedro/declared.py +110 -0
  31. qedro-0.2.0/src/qedro/deployer.py +414 -0
  32. qedro-0.2.0/src/qedro/document.py +87 -0
  33. qedro-0.2.0/src/qedro/errors.py +37 -0
  34. qedro-0.2.0/src/qedro/events.py +257 -0
  35. qedro-0.2.0/src/qedro/mark.py +44 -0
  36. qedro-0.2.0/src/qedro/provenance.py +478 -0
  37. qedro-0.2.0/src/qedro/quality.py +438 -0
  38. qedro-0.2.0/src/qedro/render.py +973 -0
  39. qedro-0.2.0/src/qedro/ropa.py +561 -0
  40. qedro-0.2.0/src/qedro/scope.py +119 -0
  41. qedro-0.2.0/src/qedro/sources.py +443 -0
  42. qedro-0.2.0/src/qedro/vocabularies/__init__.py +0 -0
  43. qedro-0.2.0/src/qedro/vocabularies/dsgvo.yaml +62 -0
  44. qedro-0.2.0/src/qedro/vocabulary.py +169 -0
  45. qedro-0.2.0/src/qedro/words.py +31 -0
  46. qedro-0.2.0/src/qedro/xlsx.py +763 -0
  47. qedro-0.2.0/src/qedro.egg-info/PKG-INFO +557 -0
  48. qedro-0.2.0/src/qedro.egg-info/SOURCES.txt +78 -0
  49. qedro-0.2.0/src/qedro.egg-info/dependency_links.txt +1 -0
  50. qedro-0.2.0/src/qedro.egg-info/entry_points.txt +2 -0
  51. qedro-0.2.0/src/qedro.egg-info/requires.txt +8 -0
  52. qedro-0.2.0/src/qedro.egg-info/top_level.txt +1 -0
  53. qedro-0.2.0/tests/__init__.py +0 -0
  54. qedro-0.2.0/tests/conftest.py +27 -0
  55. qedro-0.2.0/tests/fixtures/airflow-3.3.1/README.md +30 -0
  56. qedro-0.2.0/tests/fixtures/airflow-3.3.1/events.jsonl +7 -0
  57. qedro-0.2.0/tests/fixtures/dbt-1.53/README.md +12 -0
  58. qedro-0.2.0/tests/fixtures/dbt-1.53/events.jsonl +10 -0
  59. qedro-0.2.0/tests/fixtures/events/README.md +11 -0
  60. qedro-0.2.0/tests/fixtures/events/pipeline-runtime.ndjson +4 -0
  61. qedro-0.2.0/tests/fixtures/spark-4.2.0/README.md +30 -0
  62. qedro-0.2.0/tests/fixtures/spark-4.2.0/events.jsonl +25 -0
  63. qedro-0.2.0/tests/lineage_api.py +87 -0
  64. qedro-0.2.0/tests/test_cli.py +596 -0
  65. qedro-0.2.0/tests/test_config.py +117 -0
  66. qedro-0.2.0/tests/test_demo.py +426 -0
  67. qedro-0.2.0/tests/test_deployer.py +368 -0
  68. qedro-0.2.0/tests/test_events.py +166 -0
  69. qedro-0.2.0/tests/test_facet_schema.py +126 -0
  70. qedro-0.2.0/tests/test_mark.py +37 -0
  71. qedro-0.2.0/tests/test_provenance.py +421 -0
  72. qedro-0.2.0/tests/test_quality.py +370 -0
  73. qedro-0.2.0/tests/test_render.py +538 -0
  74. qedro-0.2.0/tests/test_ropa.py +632 -0
  75. qedro-0.2.0/tests/test_sources.py +444 -0
  76. qedro-0.2.0/tests/test_vocabulary.py +130 -0
  77. qedro-0.2.0/tests/test_xlsx.py +207 -0
  78. qedro-0.2.0/tools/architecture_pdf.py +127 -0
  79. qedro-0.2.0/tools/facet_schema.py +193 -0
  80. qedro-0.2.0/tools/seed.py +624 -0
@@ -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.
@@ -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__