cli.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. """The cli for jupyter events."""
  2. from __future__ import annotations
  3. import json
  4. import pathlib
  5. import platform
  6. import click
  7. from jsonschema import ValidationError
  8. from rich.console import Console
  9. from rich.json import JSON
  10. from rich.markup import escape
  11. from rich.padding import Padding
  12. from rich.style import Style
  13. from jupyter_events.schema import EventSchema, EventSchemaFileAbsent, EventSchemaLoadingError
  14. WIN = platform.system() == "Windows"
  15. class RC:
  16. """Return code enum."""
  17. OK = 0
  18. INVALID = 1
  19. UNPARSABLE = 2
  20. NOT_FOUND = 3
  21. class EMOJI:
  22. """Terminal emoji enum"""
  23. X = "XX" if WIN else "\u274c"
  24. OK = "OK" if WIN else "\u2714"
  25. console = Console()
  26. error_console = Console(stderr=True)
  27. @click.group()
  28. @click.version_option()
  29. def main() -> None:
  30. """A simple CLI tool to quickly validate JSON schemas against
  31. Jupyter Event's custom validator.
  32. You can see Jupyter Event's meta-schema here:
  33. https://raw.githubusercontent.com/jupyter/jupyter_events/main/jupyter_events/schemas/event-metaschema.yml
  34. """
  35. @click.command()
  36. @click.argument("schema")
  37. @click.pass_context
  38. def validate(ctx: click.Context, schema: str) -> int:
  39. """Validate a SCHEMA against Jupyter Event's meta schema.
  40. SCHEMA can be a JSON/YAML string or filepath to a schema.
  41. """
  42. console.rule("Validating the following schema", style=Style(color="blue"))
  43. _schema = None
  44. try:
  45. # attempt to read schema as a serialized string
  46. _schema = EventSchema._load_schema(schema)
  47. except EventSchemaLoadingError:
  48. # pass here to avoid printing traceback of this exception if next block
  49. # excepts
  50. pass
  51. # if not a serialized schema string, try to interpret it as a path to schema file
  52. if _schema is None:
  53. schema_path = pathlib.Path(schema)
  54. try:
  55. _schema = EventSchema._load_schema(schema_path)
  56. except (EventSchemaLoadingError, EventSchemaFileAbsent) as e:
  57. # no need for full tracestack for user error exceptions. just print
  58. # the error message and return
  59. error_console.print(f"[bold red]ERROR[/]: {e}")
  60. return ctx.exit(RC.UNPARSABLE)
  61. # Print what was found.
  62. schema_json = JSON(json.dumps(_schema))
  63. console.print(Padding(schema_json, (1, 0, 1, 4)))
  64. # Now validate this schema against the meta-schema.
  65. try:
  66. EventSchema(_schema)
  67. console.rule("Results", style=Style(color="green"))
  68. out = Padding(f"[green]{EMOJI.OK}[white] Nice work! This schema is valid.", (1, 0, 1, 0))
  69. console.print(out)
  70. return ctx.exit(RC.OK)
  71. except ValidationError as err:
  72. error_console.rule("Results", style=Style(color="red"))
  73. error_console.print(f"[red]{EMOJI.X} [white]The schema failed to validate.")
  74. error_console.print("\nWe found the following error with your schema:")
  75. out = escape(str(err)) # type:ignore[assignment]
  76. error_console.print(Padding(out, (1, 0, 1, 4)))
  77. return ctx.exit(RC.INVALID)
  78. main.add_command(validate)