Skip to content

Commit 23913f5

Browse files
authored
ci: validate simple extension examples in docs (#885)
1 parent cbf7ed7 commit 23913f5

20 files changed

Lines changed: 207 additions & 139 deletions

.github/workflows/pr.yml

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,31 +54,13 @@ jobs:
5454
runs-on: ubuntu-latest
5555
steps:
5656
- uses: actions/checkout@v5
57-
- uses: actions/setup-node@v6
57+
- uses: actions/setup-python@v6
5858
with:
59-
node-version: "20"
60-
- run: npm install -g ajv-cli
61-
- run: |
62-
set -euo pipefail
63-
for i in $(ls *.yaml);
64-
do
65-
ajv validate -s ../text/simple_extensions_schema.yaml --strict=true --spec=draft2020 -d "$i"
66-
done
67-
working-directory: ./extensions
68-
- run: |
69-
set -euo pipefail
70-
for i in $(ls *.yaml);
71-
do
72-
ajv validate -s ../../text/dialect_schema.yaml --strict=true --spec=draft2020 -d "$i"
73-
done
74-
working-directory: ./dialects/tests
75-
- run: |
76-
set -euo pipefail
77-
for i in $(ls *.yaml);
78-
do
79-
ajv validate -s ../text/dialect_schema.yaml --strict=true --spec=draft2020 -d "$i"
80-
done
81-
working-directory: ./dialects
59+
python-version: '3.13'
60+
- run: pip install check-jsonschema
61+
- run: check-jsonschema --schemafile text/simple_extensions_schema.yaml extensions/*.yaml
62+
- run: check-jsonschema --schemafile text/simple_extensions_schema.yaml site/examples/**/*.yaml
63+
- run: check-jsonschema --schemafile text/dialect_schema.yaml dialects/tests/*.yaml
8264
dry_run_release:
8365
name: Dry-run release
8466
runs-on: ubuntu-latest

CONTRIBUTING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ There's no formal set of dependencies for Substrait, but here are some that are
1010
* [`protoc`](https://grpc.io/docs/protoc-installation/), used by `buf` and usable independent of `buf`
1111
* A Python environment with [the website's `requirements.txt`](https://github.com/substrait-io/substrait/blob/main/site/requirements.txt) dependencies installed if you want to see changes to the website locally
1212

13+
## Documentation Examples
14+
15+
When adding examples to the documentation, please use external example files instead of inline code blocks. This ensures examples are validated against schemas in CI/CD and prevents documentation drift.
16+
17+
See [`site/examples/README.md`](site/examples/README.md) for complete instructions on creating and including validated examples.
18+
19+
Quick example:
20+
21+
```markdown
22+
```yaml
23+
--8<-- "examples/extensions/my_example.yaml"
24+
```
25+
```
26+
1327
## Commit Conventions
1428

1529
Substrait follows [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit message structure. You can use [`pre-commit`](https://pre-commit.com/) to check your messages for you, but note that you must install pre-commit using `pre-commit install --hook-type commit-msg` for this to work. CI will also lint your commit messages. Please also ensure that your PR title and initial comment together form a valid commit message; that will save us some work formatting the merge commit message when we merge your PR.

site/docs/expressions/user_defined_functions.md

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,6 @@ Here's an example function that doubles its input:
77
!!! info inline end "Implementation Note"
88
This implementation is only defined on 32-bit floats and integers but could be defined on all numbers (and even lists and strings). The user of the implementation can specify what happens when the resulting value falls outside of the valid range for a 32-bit float (either return NAN or raise an error).
99

10-
``` yaml
11-
%YAML 1.2
12-
---
13-
scalar_functions:
14-
-
15-
name: "double"
16-
description: "Double the value"
17-
impls:
18-
- args:
19-
- name: x
20-
value: fp32
21-
options:
22-
on_domain_error:
23-
values: [ NAN, ERROR ]
24-
return: fp32
25-
- args:
26-
- name: x
27-
value: i32
28-
options:
29-
on_domain_error:
30-
values: [ NAN, ERROR ]
31-
return: i32
10+
```yaml
11+
--8<-- "examples/extensions/double_function.yaml"
3212
```

site/docs/extensions/index.md

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,7 @@ A YAML file can also reference types and type variations defined in another YAML
3535
For example, if the extension with extension URN `extension:io.substrait:extension_types` defines a type called `point`, a different YAML file can use the type in a function declaration as follows:
3636

3737
```yaml
38-
urn: extension:example:distance_functions
39-
dependencies:
40-
ext: extension:io.substrait:extension_types
41-
scalar_functions:
42-
- name: distance
43-
description: The distance between two points.
44-
impls:
45-
- args:
46-
- name: a
47-
value: ext.point
48-
- name: b
49-
value: ext.point
50-
return: f64
38+
--8<-- "examples/extensions/distance_functions.yaml"
5139
```
5240

5341
Here, the choice for the name `ext` is arbitrary, as long as it does not conflict with anything else in the YAML file.
@@ -125,29 +113,13 @@ A function signature uniquely identifies a function implementation within a sing
125113
### Any Types
126114

127115
```yaml
128-
scalar_functions:
129-
- name: foo
130-
impls:
131-
- args:
132-
- name: a
133-
value: any
134-
- name: b
135-
value: any
136-
return: int64
116+
--8<-- "examples/extensions/any_type_function.yaml"
137117
```
138118

139119
The `any` type indicates that the argument can take any possible type. In the `foo` function above, arguments `a` and `b` can be of any type, even different ones in the same function invocation.
140120

141121
```yaml
142-
scalar_functions:
143-
- name: bar
144-
impls:
145-
- args:
146-
- name: a
147-
value: any1
148-
- name: b
149-
value: any1
150-
return: int64
122+
--8<-- "examples/extensions/any1_type_function.yaml"
151123
```
152124
The `any[\d]` types (i.e. `any1`, `any2`, ..., `any9`) impose an additional restriction. Within a single function invocation, all any types with same numeric suffix _must_ be of the same type. In the `bar` function above, arguments `a` and `b` can have any type as long as both types are the same.
153125

site/docs/types/type_classes.md

Lines changed: 9 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,7 @@ User-defined type classes are defined as part of [simple extensions](../extensio
5353
For example, the following declares a type named `point` (namespaced to the associated YAML file) and two scalar functions that operate on it.
5454

5555
```yaml
56-
types:
57-
- name: "point"
58-
59-
scalar_functions:
60-
- name: "lat"
61-
impls:
62-
- args:
63-
- name: p
64-
- value: u!point
65-
return: fp64
66-
- name: "lon"
67-
impls:
68-
- args:
69-
- name: p
70-
- value: u!point
71-
return: fp64
56+
--8<-- "examples/types/user_defined_point.yaml"
7257
```
7358

7459
### Handling User-Defined Types
@@ -85,18 +70,13 @@ Specifiers of user-defined types may provide additional structure information fo
8570
For example, the following declares a `point` type with two `i32` values named longitude and latitude:
8671

8772
```yaml
88-
types:
89-
- name: point
90-
structure:
91-
longitude: i32
92-
latitude: i32
73+
--8<-- "examples/types/point_with_structure.yaml"
9374
```
9475

9576
The name-type object notation used above is syntactic sugar for `NSTRUCT<longitude: i32, latitude: i32>`. The following means the same thing:
9677

9778
```yaml
98-
name: point
99-
structure: "NSTRUCT<longitude: i32, latitude: i32>"
79+
--8<-- "examples/types/point_with_nstruct.yaml"
10080
```
10181

10282
The structure field of a type is only intended to inform systems that don't have built-in support for the type about how they can create and transfer values of that type to systems that do support the type.
@@ -116,74 +96,41 @@ Literals for user-defined types can be represented in one of two ways:
11696
User-defined types may be turned into compound types by requiring parameters to be passed to them. The supported "meta-types" for parameters are data types (like those used in `LIST`, `MAP`, and `STRUCT`), booleans, integers, enumerations, and strings. Using parameters, we could redefine "point" with different types of coordinates. For example:
11797

11898
```yaml
119-
name: point
120-
parameters:
121-
- name: T
122-
description: |
123-
The type used for the longitude and latitude
124-
components of the point.
125-
type: dataType
99+
--8<-- "examples/types/point_with_datatype_param.yaml"
126100
```
127101

128102
or:
129103

130104
```yaml
131-
name: point
132-
parameters:
133-
- name: coordinate_type
134-
type: enumeration
135-
options:
136-
- integer
137-
- double
105+
--8<-- "examples/types/point_with_enum_param.yaml"
138106
```
139107

140108
or:
141109

142110
```yaml
143-
name: point
144-
parameters:
145-
- name: LONG
146-
type: dataType
147-
- name: LAT
148-
type: dataType
111+
--8<-- "examples/types/point_with_two_params.yaml"
149112
```
150113

151114
We can't specify the internal structure in this case, because there is currently no support for derived types in the structure.
152115

153116
The allowed range can be limited for integer parameters. For example:
154117

155118
```yaml
156-
name: vector
157-
parameters:
158-
- name: T
159-
type: dataType
160-
- name: dimensions
161-
type: integer
162-
min: 2
163-
max: 3
119+
--8<-- "examples/types/vector_with_constraints.yaml"
164120
```
165121

166122
This specifies a vector that can be either 2- or 3-dimensional. Note however that it's not currently possible to put constraints on data type, string, or (technically) boolean parameters.
167123

168124
Similar to function arguments, the last parameter may be specified to be variadic, allowing it to be specified one or more times instead of only once. For example:
169125

170126
```yaml
171-
name: union
172-
parameters:
173-
- name: T
174-
type: dataType
175-
variadic: true
127+
--8<-- "examples/types/union_variadic.yaml"
176128
```
177129

178130
This defines a type that can be parameterized with one or more other data types, for example `union<i32, i64>` but also `union<bool>`. Zero or more is also possible, by making the last argument optional:
179131

180132
```yaml
181-
name: tuple
182-
parameters:
183-
- name: T
184-
type: dataType
185-
optional: true
186-
variadic: true
133+
--8<-- "examples/types/tuple_optional_variadic.yaml"
187134
```
188135

189136
This would also allow for `tuple<>`, to define a zero-tuple.

site/examples/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Documentation Examples
2+
3+
This directory contains example files that are included in the Substrait documentation.
4+
5+
By storing examples as separate files instead of inline in markdown, we can easily validate against schemas via CI/CD.
6+
7+
## Directory Structure
8+
9+
```
10+
examples/
11+
├── extensions/ # Extension function examples (e.g., any types)
12+
├── types/ # User-defined type examples
13+
└── README.md # This file
14+
```
15+
16+
All examples are validated against `text/simple_extensions_schema.yaml` in CI/CD.
17+
18+
## Including Examples in Markdown
19+
20+
Use the pymdownx.snippets syntax to include example files:
21+
22+
````markdown
23+
```yaml
24+
--8<-- "examples/extensions/distance_functions.yaml"
25+
```
26+
````
27+
28+
The snippet will be rendered with syntax highlighting and the actual file content.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Example showing the 'any1' type - arguments must be of the same type
2+
urn: extension:example:any1_type
3+
scalar_functions:
4+
- name: bar
5+
impls:
6+
- args:
7+
- name: a
8+
value: any1
9+
- name: b
10+
value: any1
11+
return: int64
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Example showing the 'any' type - arguments can be of any type
2+
urn: extension:example:any_type
3+
scalar_functions:
4+
- name: foo
5+
impls:
6+
- args:
7+
- name: a
8+
value: any
9+
- name: b
10+
value: any
11+
return: int64
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
urn: extension:example:distance_functions
2+
dependencies:
3+
ext: extension:io.substrait:extension_types
4+
scalar_functions:
5+
- name: distance
6+
description: The distance between two points.
7+
impls:
8+
- args:
9+
- name: a
10+
value: ext.point
11+
- name: b
12+
value: ext.point
13+
return: f64
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
%YAML 1.2
2+
---
3+
urn: extension:example:double_function
4+
scalar_functions:
5+
-
6+
name: "double"
7+
description: "Double the value"
8+
impls:
9+
- args:
10+
- name: x
11+
value: fp32
12+
options:
13+
on_domain_error:
14+
values: [ NAN, ERROR ]
15+
return: fp32
16+
- args:
17+
- name: x
18+
value: i32
19+
options:
20+
on_domain_error:
21+
values: [ NAN, ERROR ]
22+
return: i32

0 commit comments

Comments
 (0)