Skip to content

Commit 6d22b7d

Browse files
authored
Merge pull request #9 from Zsailer/cli
Add a simple CLI tool for validating event schemas
2 parents fe79540 + 0101fcb commit 6d22b7d

File tree

6 files changed

+146
-7
lines changed

6 files changed

+146
-7
lines changed

docs/user_guide/defining-schema.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,73 @@ required:
3939
- thing
4040
- user
4141
```
42+
43+
## Checking if a schema is valid
44+
45+
When authoring a schema, how do you check if you schema is following the expected form? Jupyter Events offers a simple command line tool to validate your schema against its Jupyter Events metaschema.
46+
47+
First, install the CLI:
48+
49+
```
50+
pip install jupyter_events[cli]
51+
```
52+
53+
Then, run the CLI against your schema:
54+
55+
```
56+
jupyter events validate path/to/my_schema.json
57+
```
58+
59+
The output will look like this, if it passes:
60+
61+
```
62+
──────────────────────────────────── Validating the following schema ────────────────────────────────────
63+
64+
{
65+
"$id": "http://event.jupyter.org/test",
66+
"version": 1,
67+
"title": "Simple Test Schema",
68+
"description": "A simple schema for testing\n",
69+
"type": "object",
70+
"properties": {
71+
"prop": {
72+
"title": "Test Property",
73+
"description": "Test property.",
74+
"type": "string"
75+
}
76+
}
77+
}
78+
79+
──────────────────────────────────────────────── Results ────────────────────────────────────────────────
80+
81+
✔ Nice work! This schema is valid.
82+
```
83+
84+
or this if fails:
85+
86+
```
87+
──────────────────────────────────── Validating the following schema ────────────────────────────────────
88+
89+
{
90+
"$id": "http://event.jupyter.org/test",
91+
"version": 1,
92+
"title": "Simple Test Schema",
93+
"description": "A simple schema for testing\n",
94+
"type": "object",
95+
"properties": {
96+
"__badName": {
97+
"title": "Test Property",
98+
"description": "Test property.",
99+
"type": "string"
100+
}
101+
}
102+
}
103+
104+
──────────────────────────────────────────────── Results ────────────────────────────────────────────────
105+
❌ The schema failed to validate.
106+
107+
We found the following error with your schema:
108+
109+
'__badName' is an invalid property name because it starts with `__`. Properties starting with
110+
'dunder' are reserved as special meta-fields for Jupyter Events to use.
111+
```

jupyter_events/cli.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import json
2+
import pathlib
3+
4+
import click
5+
from jsonschema import ValidationError
6+
from rich.console import Console
7+
from rich.json import JSON
8+
from rich.markup import escape
9+
from rich.padding import Padding
10+
from rich.style import Style
11+
12+
from jupyter_events.schema import EventSchema, EventSchemaLoadingError
13+
14+
console = Console()
15+
16+
17+
@click.group()
18+
def main():
19+
"""A simple CLI tool to quickly validate JSON schemas against
20+
Jupyter Event's custom validator.
21+
22+
You can see Jupyter Event's meta-schema here:
23+
24+
https://raw.githubusercontent.com/jupyter/jupyter_events/main/jupyter_events/schemas/event-metaschema.yml
25+
"""
26+
pass
27+
28+
29+
@click.command()
30+
@click.argument("schema")
31+
def validate(schema):
32+
"""Validate a SCHEMA against Jupyter Event's meta schema.
33+
34+
SCHEMA can be a JSON/YAML string or filepath to a schema.
35+
"""
36+
console.rule("Validating the following schema", style=Style(color="blue"))
37+
# Soft load the schema without validating.
38+
try:
39+
_schema = EventSchema._load_schema(schema)
40+
except EventSchemaLoadingError:
41+
schema_path = pathlib.Path(schema)
42+
_schema = EventSchema._load_schema(schema_path)
43+
# Print what was found.
44+
schema_json = JSON(json.dumps(_schema))
45+
console.print(Padding(schema_json, (1, 0, 1, 4)))
46+
# Now validate this schema against the meta-schema.
47+
try:
48+
EventSchema(_schema)
49+
console.rule("Results", style=Style(color="green"))
50+
out = Padding(
51+
"[green]\u2714[white] Nice work! This schema is valid.", (1, 0, 1, 0)
52+
)
53+
console.print(out)
54+
except ValidationError as err:
55+
console.rule("Results", style=Style(color="red"))
56+
console.print("[red]\u274c [white]The schema failed to validate.\n")
57+
console.print("We found the following error with your schema:")
58+
out = escape(str(err))
59+
console.print(Padding(out, (1, 0, 1, 4)))
60+
61+
62+
main.add_command(validate)

jupyter_events/logger.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,11 @@ def add_listener(
245245
246246
Parameters
247247
----------
248+
modified: bool
249+
If True (default), listens to the data after it has been mutated/modified
250+
by the list of modifiers.
248251
schema_id: str
249252
$id of the schema
250-
version: str
251-
The schema version
252253
listener: Callable
253254
A callable function/method that executes when the named event occurs.
254255
"""

jupyter_events/schema.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ def __init__(
4949
self._schema = _schema
5050

5151
def __repr__(self):
52-
out = f"Validator class: {self._validator.__class__.__name__}\n"
53-
out += f"Schema: {json.dumps(self._schema, indent=2)}"
54-
return out
52+
return json.dumps(self._schema, indent=2)
5553

5654
@staticmethod
5755
def _load_schema(schema: Union[dict, str, PurePath]) -> dict:

jupyter_events/validators.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ def validate_schema(schema: dict):
2727
except ValidationError as err:
2828
reserved_property_msg = " does not match '^(?!__.*)'"
2929
if reserved_property_msg in str(err):
30-
bad_property = str(err)[: -(len(reserved_property_msg))]
30+
idx = str(err).find(reserved_property_msg)
31+
bad_property = str(err)[:idx].strip()
3132
raise ValidationError(
3233
f"{bad_property} is an invalid property name because it "
3334
"starts with `__`. Properties starting with 'dunder' "
34-
"are reserved for Jupyter Events."
35+
"are reserved as special meta-fields for Jupyter Events to use."
3536
)
3637
raise err

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,20 @@ file = 'COPYING.md'
4040
[project.urls]
4141
Homepage = "http://jupyter.org"
4242

43+
[project.scripts]
44+
jupyter-events = "jupyter_events.cli:main"
45+
4346
[project.optional-dependencies]
4447
test = [
4548
"coverage",
4649
"pre-commit",
4750
"pytest-cov",
4851
"pytest>=6.0",
4952
]
53+
cli = [
54+
"click",
55+
"rich"
56+
]
5057

5158
[tool.hatch.version]
5259
path = "jupyter_events/_version.py"

0 commit comments

Comments
 (0)