You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
3.0 KiB
Python
103 lines
3.0 KiB
Python
"""The cli for jupyter events."""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import pathlib
|
|
import platform
|
|
|
|
import click
|
|
from jsonschema import ValidationError
|
|
from rich.console import Console
|
|
from rich.json import JSON
|
|
from rich.markup import escape
|
|
from rich.padding import Padding
|
|
from rich.style import Style
|
|
|
|
from jupyter_events.schema import EventSchema, EventSchemaFileAbsent, EventSchemaLoadingError
|
|
|
|
WIN = platform.system() == "Windows"
|
|
|
|
|
|
class RC:
|
|
"""Return code enum."""
|
|
|
|
OK = 0
|
|
INVALID = 1
|
|
UNPARSABLE = 2
|
|
NOT_FOUND = 3
|
|
|
|
|
|
class EMOJI:
|
|
"""Terminal emoji enum"""
|
|
|
|
X = "XX" if WIN else "\u274c"
|
|
OK = "OK" if WIN else "\u2714"
|
|
|
|
|
|
console = Console()
|
|
error_console = Console(stderr=True)
|
|
|
|
|
|
@click.group()
|
|
@click.version_option()
|
|
def main() -> None:
|
|
"""A simple CLI tool to quickly validate JSON schemas against
|
|
Jupyter Event's custom validator.
|
|
|
|
You can see Jupyter Event's meta-schema here:
|
|
|
|
https://raw.githubusercontent.com/jupyter/jupyter_events/main/jupyter_events/schemas/event-metaschema.yml
|
|
"""
|
|
|
|
|
|
@click.command()
|
|
@click.argument("schema")
|
|
@click.pass_context
|
|
def validate(ctx: click.Context, schema: str) -> int:
|
|
"""Validate a SCHEMA against Jupyter Event's meta schema.
|
|
|
|
SCHEMA can be a JSON/YAML string or filepath to a schema.
|
|
"""
|
|
console.rule("Validating the following schema", style=Style(color="blue"))
|
|
|
|
_schema = None
|
|
try:
|
|
# attempt to read schema as a serialized string
|
|
_schema = EventSchema._load_schema(schema)
|
|
except EventSchemaLoadingError:
|
|
# pass here to avoid printing traceback of this exception if next block
|
|
# excepts
|
|
pass
|
|
|
|
# if not a serialized schema string, try to interpret it as a path to schema file
|
|
if _schema is None:
|
|
schema_path = pathlib.Path(schema)
|
|
try:
|
|
_schema = EventSchema._load_schema(schema_path)
|
|
except (EventSchemaLoadingError, EventSchemaFileAbsent) as e:
|
|
# no need for full tracestack for user error exceptions. just print
|
|
# the error message and return
|
|
error_console.print(f"[bold red]ERROR[/]: {e}")
|
|
return ctx.exit(RC.UNPARSABLE)
|
|
|
|
# Print what was found.
|
|
schema_json = JSON(json.dumps(_schema))
|
|
console.print(Padding(schema_json, (1, 0, 1, 4)))
|
|
# Now validate this schema against the meta-schema.
|
|
try:
|
|
EventSchema(_schema)
|
|
console.rule("Results", style=Style(color="green"))
|
|
out = Padding(f"[green]{EMOJI.OK}[white] Nice work! This schema is valid.", (1, 0, 1, 0))
|
|
console.print(out)
|
|
return ctx.exit(RC.OK)
|
|
except ValidationError as err:
|
|
error_console.rule("Results", style=Style(color="red"))
|
|
error_console.print(f"[red]{EMOJI.X} [white]The schema failed to validate.")
|
|
error_console.print("\nWe found the following error with your schema:")
|
|
out = escape(str(err)) # type:ignore[assignment]
|
|
error_console.print(Padding(out, (1, 0, 1, 4)))
|
|
return ctx.exit(RC.INVALID)
|
|
|
|
|
|
main.add_command(validate)
|