validators.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. """Event validators."""
  2. from __future__ import annotations
  3. import pathlib
  4. import warnings
  5. from typing import Any
  6. import jsonschema
  7. from jsonschema import Draft7Validator, ValidationError
  8. from referencing import Registry
  9. from referencing.jsonschema import DRAFT7
  10. from . import yaml
  11. from .utils import JupyterEventsVersionWarning
  12. draft7_format_checker = (
  13. Draft7Validator.FORMAT_CHECKER
  14. if hasattr(Draft7Validator, "FORMAT_CHECKER")
  15. else jsonschema.draft7_format_checker
  16. )
  17. METASCHEMA_PATH = pathlib.Path(__file__).parent.joinpath("schemas")
  18. EVENT_METASCHEMA_FILEPATH = METASCHEMA_PATH.joinpath("event-metaschema.yml")
  19. EVENT_METASCHEMA = yaml.load(EVENT_METASCHEMA_FILEPATH)
  20. EVENT_CORE_SCHEMA_FILEPATH = METASCHEMA_PATH.joinpath("event-core-schema.yml")
  21. EVENT_CORE_SCHEMA = yaml.load(EVENT_CORE_SCHEMA_FILEPATH)
  22. PROPERTY_METASCHEMA_FILEPATH = METASCHEMA_PATH.joinpath("property-metaschema.yml")
  23. PROPERTY_METASCHEMA = yaml.load(PROPERTY_METASCHEMA_FILEPATH)
  24. SCHEMA_STORE = {
  25. EVENT_METASCHEMA["$id"]: EVENT_METASCHEMA,
  26. PROPERTY_METASCHEMA["$id"]: PROPERTY_METASCHEMA,
  27. EVENT_CORE_SCHEMA["$id"]: EVENT_CORE_SCHEMA,
  28. }
  29. resources = [
  30. DRAFT7.create_resource(each)
  31. for each in (EVENT_METASCHEMA, PROPERTY_METASCHEMA, EVENT_CORE_SCHEMA)
  32. ]
  33. METASCHEMA_REGISTRY: Registry[Any] = resources @ Registry()
  34. JUPYTER_EVENTS_SCHEMA_VALIDATOR = Draft7Validator(
  35. schema=EVENT_METASCHEMA,
  36. registry=METASCHEMA_REGISTRY,
  37. format_checker=draft7_format_checker,
  38. )
  39. JUPYTER_EVENTS_CORE_VALIDATOR = Draft7Validator(
  40. schema=EVENT_CORE_SCHEMA,
  41. registry=METASCHEMA_REGISTRY,
  42. format_checker=draft7_format_checker,
  43. )
  44. def validate_schema(schema: dict[str, Any]) -> None:
  45. """Validate a schema dict."""
  46. try:
  47. # If the `version` attribute is an integer, coerce to string.
  48. # TODO: remove this in a future version.
  49. if "version" in schema and isinstance(schema["version"], int):
  50. schema["version"] = str(schema["version"])
  51. msg = (
  52. "The `version` property of an event schema must be a string. "
  53. "It has been type coerced, but in a future version of this "
  54. "library, it will fail to validate. Please update schema: "
  55. f"{schema['$id']}"
  56. )
  57. warnings.warn(JupyterEventsVersionWarning(msg), stacklevel=2)
  58. # Validate the schema against Jupyter Events metaschema.
  59. JUPYTER_EVENTS_SCHEMA_VALIDATOR.validate(schema)
  60. except ValidationError as err:
  61. reserved_property_msg = " does not match '^(?!__.*)'"
  62. if reserved_property_msg in str(err):
  63. idx = str(err).find(reserved_property_msg)
  64. bad_property = str(err)[:idx].strip()
  65. msg = (
  66. f"{bad_property} is an invalid property name because it "
  67. "starts with `__`. Properties starting with 'dunder' "
  68. "are reserved as special meta-fields for Jupyter Events to use."
  69. )
  70. raise ValidationError(msg) from err
  71. raise err