Skip to content

Commit

Permalink
Merge pull request #24 from camptocamp/better-message
Browse files Browse the repository at this point in the history
Get more understandable error message on deep structure
  • Loading branch information
sbrunner committed Jun 7, 2021
2 parents 290b9c5 + 1c89fce commit e18a48d
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 57 deletions.
47 changes: 39 additions & 8 deletions jsonschema_gentypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Generate the type structure based on the Type class from the JSON schema file.
"""

import textwrap
from abc import abstractmethod
from typing import Callable, Dict, Iterator, List, Optional, Set, Tuple, Union, cast

Expand Down Expand Up @@ -34,9 +35,12 @@ def imports(self) -> List[Tuple[str, str]]: # pylint: disable=no-self-use
"""
return []

def definition(self) -> List[str]: # pylint: disable=no-self-use
def definition(self, line_length: Optional[int] = None) -> List[str]: # pylint: disable=no-self-use
"""
Return the type declaration.
Arguments:
line_length: the maximum line length
"""
return []

Expand Down Expand Up @@ -237,12 +241,14 @@ def depends_on(self) -> List[Type]:
"""
return [self.sub_type]

def definition(self) -> List[str]:
def definition(self, line_length: Optional[int] = None) -> List[str]:
"""
Return the type declaration.
"""
result = ["", ""]
result += ["# " + d for d in self.descriptions]
result += [
"# " + d for d in split_comment(self.descriptions, line_length - 2 if line_length else None)
]
result.append(f"{self._name} = {self.sub_type.name()}")
return result

Expand Down Expand Up @@ -273,12 +279,14 @@ def depends_on(self) -> List["Type"]:
"""
return [self.sub_type]

def definition(self) -> List[str]:
def definition(self, line_length: Optional[int] = None) -> List[str]:
"""
Return the type declaration.
"""
result = ["", ""]
result += ["# " + d for d in self.descriptions]
result += [
"# " + d for d in split_comment(self.descriptions, line_length - 2 if line_length else None)
]
result.append(f"{self._name} = {self.sub_type.name()}")
result += ["# The values for the enum"]
for value in self.values:
Expand Down Expand Up @@ -319,12 +327,14 @@ def depends_on(self) -> List[Type]:
result += self.struct.values()
return result

def definition(self) -> List[str]:
def definition(self, line_length: Optional[int] = None) -> List[str]:
"""
Get the definition based on a dict.
"""
result = ["", ""]
result += ["# " + d for d in self.descriptions]
result += [
"# " + d for d in split_comment(self.descriptions, line_length - 2 if line_length else None)
]
result.append(f"{self._name} = TypedDict('{self._name}', " + "{")
for property_, type_obj in self.struct.items():
for comment in type_obj.comments():
Expand All @@ -334,6 +344,27 @@ def definition(self) -> List[str]:
return result


def split_comment(text: List[str], line_length: Optional[int]) -> List[str]:
"""
Split the text at line length
Arguments:
text: the lines to split
line_length: the maximum line length
"""
if not line_length:
return text

result = []
for line in text:
# Don't remove empty lines
if line:
result += textwrap.wrap(line, width=line_length, break_long_words=False)
else:
result.append(line)
return result


def char_range(char1: str, char2: str) -> Iterator[str]:
"""
Generate the characters range from `char1` to `char2`, inclusive.
Expand Down Expand Up @@ -433,7 +464,7 @@ def get_description(schema: jsonschema.JSONSchemaItem) -> List[str]:
if key in schema:
if result:
result.append("")
result.append(schema[key]) # type: ignore
result += schema[key].split("\n") # type: ignore
first = True
for key, value in schema.items():
if (
Expand Down
5 changes: 3 additions & 2 deletions jsonschema_gentypes/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ def add_type(
if (
isinstance(type_, jsonschema_gentypes.NamedType)
and type_.unescape_name() in types
and type_.definition() == types[type_.unescape_name()].definition()
and type_.definition(config.get("lineLength"))
== types[type_.unescape_name()].definition(config.get("lineLength"))
):
return
name_mapping = gen.get("name_mapping", {})
Expand Down Expand Up @@ -119,7 +120,7 @@ def add_type(
lines.append(f'from {imp} import {", ".join(names)}')

for type_ in sorted(types.values(), key=lambda type_: type_.name()):
lines += type_.definition()
lines += type_.definition(config.get("lineLength"))

with open(gen["destination"], "w") as destination_file:
headers = config.get("headers")
Expand Down
1 change: 1 addition & 0 deletions jsonschema_gentypes/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"properties": {
"headers": { "type": "string" },
"callbacks": { "type": "array", "items": { "type": "array", "items": { "type": "string" } } },
"lineLength": { "type": "integer", "description": "The maxumum line length" },
"generate": {
"type": "array",
"items": {
Expand Down
28 changes: 18 additions & 10 deletions jsonschema_gentypes/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def validate(

validator = Validator(schema)

def format_error(error: jsonschema.exceptions.ValidationError) -> str:
def format_error(error: jsonschema.exceptions.ValidationError) -> List[str]:
position = filename

if hasattr(error.instance, "lc"):
Expand All @@ -111,21 +111,29 @@ def format_error(error: jsonschema.exceptions.ValidationError) -> str:
parent = None
if hasattr(curent_data, "lc"):
parent = curent_data
for path in error.path:
for path in error.absolute_path:
curent_data = curent_data[path]
if hasattr(curent_data, "lc"):
parent = curent_data
if parent is not None:
position = f"{filename}:{parent.lc.line + 1}:{parent.lc.col + 1}" # type: ignore

return (
f" - {position} "
f'({".".join([str(i) for i in error.path] if error.path else "/")}): '
f"{error.message} (rule: "
f'{".".join([str(i) for i in error.schema_path] if error.schema_path else "/")})'
)

return sorted([format_error(e) for e in validator.iter_errors(data)]), data
if error.context:
results = []
for context in error.context:
results += format_error(context)
return results
else:
return [
f"-- {position} "
f'{".".join([str(i) for i in error.absolute_path] if error.absolute_path else "/")}: '
f"{error.message}"
]

results = []
for error in validator.iter_errors(data):
results += format_error(error)
return sorted(results), data


class ValidationError(Exception):
Expand Down
Loading

0 comments on commit e18a48d

Please sign in to comment.