about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/jsonschema/cli.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/jsonschema/cli.py')
-rw-r--r--.venv/lib/python3.12/site-packages/jsonschema/cli.py296
1 files changed, 296 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/jsonschema/cli.py b/.venv/lib/python3.12/site-packages/jsonschema/cli.py
new file mode 100644
index 00000000..cf6298eb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/jsonschema/cli.py
@@ -0,0 +1,296 @@
+"""
+The ``jsonschema`` command line.
+"""
+
+from importlib import metadata
+from json import JSONDecodeError
+from textwrap import dedent
+import argparse
+import json
+import sys
+import traceback
+import warnings
+
+try:
+    from pkgutil import resolve_name
+except ImportError:
+    from pkgutil_resolve_name import resolve_name  # type: ignore[no-redef]
+
+from attrs import define, field
+
+from jsonschema.exceptions import SchemaError
+from jsonschema.validators import _RefResolver, validator_for
+
+warnings.warn(
+    (
+        "The jsonschema CLI is deprecated and will be removed in a future "
+        "version. Please use check-jsonschema instead, which can be installed "
+        "from https://pypi.org/project/check-jsonschema/"
+    ),
+    DeprecationWarning,
+    stacklevel=2,
+)
+
+
+class _CannotLoadFile(Exception):
+    pass
+
+
+@define
+class _Outputter:
+
+    _formatter = field()
+    _stdout = field()
+    _stderr = field()
+
+    @classmethod
+    def from_arguments(cls, arguments, stdout, stderr):
+        if arguments["output"] == "plain":
+            formatter = _PlainFormatter(arguments["error_format"])
+        elif arguments["output"] == "pretty":
+            formatter = _PrettyFormatter()
+        return cls(formatter=formatter, stdout=stdout, stderr=stderr)
+
+    def load(self, path):
+        try:
+            file = open(path)  # noqa: SIM115, PTH123
+        except FileNotFoundError as error:
+            self.filenotfound_error(path=path, exc_info=sys.exc_info())
+            raise _CannotLoadFile() from error
+
+        with file:
+            try:
+                return json.load(file)
+            except JSONDecodeError as error:
+                self.parsing_error(path=path, exc_info=sys.exc_info())
+                raise _CannotLoadFile() from error
+
+    def filenotfound_error(self, **kwargs):
+        self._stderr.write(self._formatter.filenotfound_error(**kwargs))
+
+    def parsing_error(self, **kwargs):
+        self._stderr.write(self._formatter.parsing_error(**kwargs))
+
+    def validation_error(self, **kwargs):
+        self._stderr.write(self._formatter.validation_error(**kwargs))
+
+    def validation_success(self, **kwargs):
+        self._stdout.write(self._formatter.validation_success(**kwargs))
+
+
+@define
+class _PrettyFormatter:
+
+    _ERROR_MSG = dedent(
+        """\
+        ===[{type}]===({path})===
+
+        {body}
+        -----------------------------
+        """,
+    )
+    _SUCCESS_MSG = "===[SUCCESS]===({path})===\n"
+
+    def filenotfound_error(self, path, exc_info):
+        return self._ERROR_MSG.format(
+            path=path,
+            type="FileNotFoundError",
+            body=f"{path!r} does not exist.",
+        )
+
+    def parsing_error(self, path, exc_info):
+        exc_type, exc_value, exc_traceback = exc_info
+        exc_lines = "".join(
+            traceback.format_exception(exc_type, exc_value, exc_traceback),
+        )
+        return self._ERROR_MSG.format(
+            path=path,
+            type=exc_type.__name__,
+            body=exc_lines,
+        )
+
+    def validation_error(self, instance_path, error):
+        return self._ERROR_MSG.format(
+            path=instance_path,
+            type=error.__class__.__name__,
+            body=error,
+        )
+
+    def validation_success(self, instance_path):
+        return self._SUCCESS_MSG.format(path=instance_path)
+
+
+@define
+class _PlainFormatter:
+
+    _error_format = field()
+
+    def filenotfound_error(self, path, exc_info):
+        return f"{path!r} does not exist.\n"
+
+    def parsing_error(self, path, exc_info):
+        return "Failed to parse {}: {}\n".format(
+            "<stdin>" if path == "<stdin>" else repr(path),
+            exc_info[1],
+        )
+
+    def validation_error(self, instance_path, error):
+        return self._error_format.format(file_name=instance_path, error=error)
+
+    def validation_success(self, instance_path):
+        return ""
+
+
+def _resolve_name_with_default(name):
+    if "." not in name:
+        name = "jsonschema." + name
+    return resolve_name(name)
+
+
+parser = argparse.ArgumentParser(
+    description="JSON Schema Validation CLI",
+)
+parser.add_argument(
+    "-i", "--instance",
+    action="append",
+    dest="instances",
+    help="""
+        a path to a JSON instance (i.e. filename.json) to validate (may
+        be specified multiple times). If no instances are provided via this
+        option, one will be expected on standard input.
+    """,
+)
+parser.add_argument(
+    "-F", "--error-format",
+    help="""
+        the format to use for each validation error message, specified
+        in a form suitable for str.format. This string will be passed
+        one formatted object named 'error' for each ValidationError.
+        Only provide this option when using --output=plain, which is the
+        default. If this argument is unprovided and --output=plain is
+        used, a simple default representation will be used.
+    """,
+)
+parser.add_argument(
+    "-o", "--output",
+    choices=["plain", "pretty"],
+    default="plain",
+    help="""
+        an output format to use. 'plain' (default) will produce minimal
+        text with one line for each error, while 'pretty' will produce
+        more detailed human-readable output on multiple lines.
+    """,
+)
+parser.add_argument(
+    "-V", "--validator",
+    type=_resolve_name_with_default,
+    help="""
+        the fully qualified object name of a validator to use, or, for
+        validators that are registered with jsonschema, simply the name
+        of the class.
+    """,
+)
+parser.add_argument(
+    "--base-uri",
+    help="""
+        a base URI to assign to the provided schema, even if it does not
+        declare one (via e.g. $id). This option can be used if you wish to
+        resolve relative references to a particular URI (or local path)
+    """,
+)
+parser.add_argument(
+    "--version",
+    action="version",
+    version=metadata.version("jsonschema"),
+)
+parser.add_argument(
+    "schema",
+    help="the path to a JSON Schema to validate with (i.e. schema.json)",
+)
+
+
+def parse_args(args):  # noqa: D103
+    arguments = vars(parser.parse_args(args=args or ["--help"]))
+    if arguments["output"] != "plain" and arguments["error_format"]:
+        raise parser.error(
+            "--error-format can only be used with --output plain",
+        )
+    if arguments["output"] == "plain" and arguments["error_format"] is None:
+        arguments["error_format"] = "{error.instance}: {error.message}\n"
+    return arguments
+
+
+def _validate_instance(instance_path, instance, validator, outputter):
+    invalid = False
+    for error in validator.iter_errors(instance):
+        invalid = True
+        outputter.validation_error(instance_path=instance_path, error=error)
+
+    if not invalid:
+        outputter.validation_success(instance_path=instance_path)
+    return invalid
+
+
+def main(args=sys.argv[1:]):  # noqa: D103
+    sys.exit(run(arguments=parse_args(args=args)))
+
+
+def run(arguments, stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdin):  # noqa: D103
+    outputter = _Outputter.from_arguments(
+        arguments=arguments,
+        stdout=stdout,
+        stderr=stderr,
+    )
+
+    try:
+        schema = outputter.load(arguments["schema"])
+    except _CannotLoadFile:
+        return 1
+
+    Validator = arguments["validator"]
+    if Validator is None:
+        Validator = validator_for(schema)
+
+    try:
+        Validator.check_schema(schema)
+    except SchemaError as error:
+        outputter.validation_error(
+            instance_path=arguments["schema"],
+            error=error,
+        )
+        return 1
+
+    if arguments["instances"]:
+        load, instances = outputter.load, arguments["instances"]
+    else:
+        def load(_):
+            try:
+                return json.load(stdin)
+            except JSONDecodeError as error:
+                outputter.parsing_error(
+                    path="<stdin>", exc_info=sys.exc_info(),
+                )
+                raise _CannotLoadFile() from error
+        instances = ["<stdin>"]
+
+    resolver = _RefResolver(
+        base_uri=arguments["base_uri"],
+        referrer=schema,
+    ) if arguments["base_uri"] is not None else None
+
+    validator = Validator(schema, resolver=resolver)
+    exit_code = 0
+    for each in instances:
+        try:
+            instance = load(each)
+        except _CannotLoadFile:
+            exit_code = 1
+        else:
+            exit_code |= _validate_instance(
+                instance_path=each,
+                instance=instance,
+                validator=validator,
+                outputter=outputter,
+            )
+
+    return exit_code