import pointblank as pb
import json
orders_contract = pb.Contract(
name="orders",
description="One row per customer order.",
schema=pb.Schema(order_id="Int64", status="String", amount="Float64"),
steps=[
pb.Step("col_vals_not_null", columns=["order_id", "status"]),
pb.Step("rows_distinct", columns_subset="order_id"),
pb.Step("col_vals_in_set", columns="status", set=["placed", "shipped", "returned"]),
pb.Step("col_vals_between", columns="amount", left=0, right=10000),
],
)export_contract()
Export a Pointblank validation or contract to another format.
Usage
export_contract(
validation_or_contract,
destination=None,
*,
format,
**kwargs,
)Data quality rules written in Pointblank are often useful to tools that don’t run Pointblank. An API team may want a JSON Schema, an analytics engineering team may want the tests in a dbt schema.yml file, and a data catalog may accept Open Data Contract Standard (ODCS) documents. export_contract() translates a Contract or a Validate object into one of these formats, so a single set of rules can be published wherever it’s needed.
Contracts export most completely because they carry a name, a description, and a Schema with column types. A Validate object is exported from the steps in its plan; it doesn’t need to be interrogated first. The translation is lossy by nature: only checks that have an equivalent in the target format are written, and the rest are left out without a warning (see the What Gets Exported section). To bring a definition back into Pointblank, use import_contract().
Parameters
validation_or_contract: Anydestination: str | None = None-
A file path to write the exported document to. Parent directories are created as needed. If
None(the default), nothing is written and the document is only returned. format: str-
The name of the adapter to use:
"json_schema","frictionless","dbt", or"odcs"for the built-in formats, or the name of a custom adapter. This argument is required and must be given by keyword. **kwargs: Any- Options for the adapter. None of the built-in adapters currently take any options.
Returns
str | dict-
The exported document. The built-in adapters return it as a dictionary (whether or not it’s also written to
destination=), which can be serialized withjson.dumps()oryaml.dump().
Raises
What Gets Exported
The column types come from the contract’s schema, and each built-in adapter translates these checks:
- JSON Schema (
"json_schema"): a draft 2020-12 object schema whosetitleanddescriptioncome from the contract.col_vals_not_null()adds the column torequired;col_vals_ge()/col_vals_le()becomeminimum/maximum,col_vals_gt()/col_vals_lt()becomeexclusiveMinimum/exclusiveMaximum, andcol_vals_between()sets bothminimumandmaximum.col_vals_in_set()becomesenum,col_vals_regex()becomespattern,col_vals_eq()becomesconst, andcol_vals_within_spec()becomes aformatfor theemail,url,ipv4, andipv6specs. - Frictionless Table Schema (
"frictionless"): afieldslist with field constraints.col_vals_not_null()becomesrequired,rows_distinct()becomesunique,col_vals_ge()/col_vals_le()becomeminimum/maximum,col_vals_in_set()becomesenum, andcol_vals_regex()becomespattern. - dbt (
"dbt"): aversion: 2document with one model named after the contract.col_vals_not_null(),rows_distinct(), andcol_vals_in_set()become the generic testsnot_null,unique, andaccepted_values. - Open Data Contract Standard (
"odcs"): aDataContractdocument with aninfoblock and one dataset table.col_vals_not_null()setsisNullable: false,rows_distinct()setsisUnique,col_vals_in_set()becomesenum,col_vals_regex()becomespattern, andcol_vals_ge()/col_vals_le()becomeminimum/maximum.
Any other step, along with thresholds, actions, and owner information, isn’t exported.
When destination= is given, the JSON Schema and Frictionless documents are written as JSON and the dbt document as YAML. ODCS documents are written as JSON if the path ends in .json and as YAML otherwise.
Examples
Let’s start with a Contract for a table of orders. It has a schema with column types, a description, and a mix of checks.
Exporting it as a JSON Schema gives us a dictionary, which we can print with json.dumps().
orders_json_schema = pb.export_contract(orders_contract, format="json_schema")
print(json.dumps(orders_json_schema, indent=2)){
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"title": "orders",
"description": "One row per customer order.",
"properties": {
"order_id": {
"type": "integer"
},
"status": {
"type": "string",
"enum": [
"placed",
"shipped",
"returned"
]
},
"amount": {
"type": "number",
"minimum": 0,
"maximum": 10000
}
},
"required": [
"order_id",
"status"
]
}
The Pointblank types became JSON types, the not-null check filled the required list, the set of statuses became an enum, and the range check became minimum and maximum values. The rows_distinct() step was left out because JSON Schema has no way to say that a value must be unique across records.
The same contract can be written out as a dbt schema.yml file. Here we write it to a models directory (which is created if it doesn’t exist) and print the file’s contents.
from pathlib import Path
pb.export_contract(orders_contract, "models/schema.yml", format="dbt")
print(Path("models/schema.yml").read_text())version: 2
models:
- name: orders
description: One row per customer order.
columns:
- name: order_id
data_type: integer
data_tests:
- not_null
- unique
- name: status
data_type: string
data_tests:
- not_null
- accepted_values:
values:
- placed
- shipped
- returned
- name: amount
data_type: float
data_tests: []
This time the uniqueness check is kept (as dbt’s unique test) but the range check is not, since dbt has no generic test for it. The amount column is still listed, with its type and an empty list of tests.
An exported document can be imported again with import_contract(). Doing so is a quick way to see which rules survive the round trip.
print(pb.import_contract(orders_json_schema).summary())Contract Import Summary
Format: json_schema
Format version: https://json-schema.org/draft/2020-12/schema
Columns detected: 3
Constraints mapped: 5
Coverage: 100%
All five constraints in the JSON Schema were mapped back, so the coverage is 100%. The range check returns as two steps (one each for minimum and maximum), and the uniqueness check is gone because it was dropped on export. Coverage measures how much of the document was understood, not how much of the original contract survived. To find what an export loses, compare the steps before and after.
See Also
- import_contract()
-
Import an external definition into Pointblank.
- list_adapters()
-
List the formats available for import and export.
- register_adapter()
-
Add support for another format.
- Contract
-
The declarative contract that exports most completely.