1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
"""JSONSchema spec paths module."""
import warnings
from contextlib import contextmanager
from pathlib import Path
from typing import Any
from typing import Iterator
from typing import Optional
from typing import Type
from typing import TypeVar
from pathable.paths import AccessorPath
from referencing import Specification
from referencing._core import Resolved
from referencing.jsonschema import DRAFT202012
from jsonschema_path.accessors import SchemaAccessor
from jsonschema_path.handlers import default_handlers
from jsonschema_path.handlers.protocols import SupportsRead
from jsonschema_path.readers import FilePathReader
from jsonschema_path.readers import FileReader
from jsonschema_path.readers import PathReader
from jsonschema_path.typing import ResolverHandlers
from jsonschema_path.typing import Schema
TSpec = TypeVar("TSpec", bound="SchemaPath")
SPEC_SEPARATOR = "#"
class SchemaPath(AccessorPath):
def __init__(self, accessor: SchemaAccessor, *args: Any, **kwargs: Any):
super().__init__(accessor, *args, **kwargs)
self._resolved_cached: Optional[Resolved[Any]] = None
@classmethod
def from_dict(
cls: Type[TSpec],
data: Schema,
*args: Any,
separator: str = SPEC_SEPARATOR,
specification: Specification[Schema] = DRAFT202012,
base_uri: str = "",
handlers: ResolverHandlers = default_handlers,
spec_url: Optional[str] = None,
ref_resolver_handlers: Optional[ResolverHandlers] = None,
) -> TSpec:
if spec_url is not None:
warnings.warn(
"spec_url parameter is deprecated. " "Use base_uri instead.",
DeprecationWarning,
)
base_uri = spec_url
if ref_resolver_handlers is not None:
warnings.warn(
"ref_resolver_handlers parameter is deprecated. "
"Use handlers instead.",
DeprecationWarning,
)
handlers = ref_resolver_handlers
accessor: SchemaAccessor = SchemaAccessor.from_schema(
data,
specification=specification,
base_uri=base_uri,
handlers=handlers,
)
return cls(accessor, *args, separator=separator)
@classmethod
def from_path(
cls: Type[TSpec],
path: Path,
) -> TSpec:
reader = PathReader(path)
data, base_uri = reader.read()
return cls.from_dict(data, base_uri=base_uri)
@classmethod
def from_file_path(
cls: Type[TSpec],
file_path: str,
) -> TSpec:
reader = FilePathReader(file_path)
data, base_uri = reader.read()
return cls.from_dict(data, base_uri=base_uri)
@classmethod
def from_file(
cls: Type[TSpec],
fileobj: SupportsRead,
base_uri: str = "",
spec_url: Optional[str] = None,
) -> TSpec:
reader = FileReader(fileobj)
data, _ = reader.read()
return cls.from_dict(data, base_uri=base_uri, spec_url=spec_url)
def contents(self) -> Any:
with self.open() as d:
return d
def exists(self) -> bool:
try:
self.contents()
except KeyError:
return False
else:
return True
def as_uri(self) -> str:
return f"#/{str(self)}"
@contextmanager
def open(self) -> Any:
"""Open the path."""
# Cached path content
with self.resolve() as resolved:
yield resolved.contents
@contextmanager
def resolve(self) -> Iterator[Resolved[Any]]:
"""Resolve the path."""
# Cached path content
if self._resolved_cached is None:
self._resolved_cached = self._get_resolved()
yield self._resolved_cached
def _get_resolved(self) -> Resolved[Any]:
assert isinstance(self.accessor, SchemaAccessor)
with self.accessor.resolve(self.parts) as resolved:
return resolved
|