Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions gedcom7/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,31 @@

from importlib.metadata import PackageNotFoundError, version

from .exceptions import GedcomError, GedcomParseError, GedcomSerializeError
from .format import format_value, set_value
from .exceptions import (
GedcomError,
GedcomParseError,
GedcomSerializeError,
GedcomValidationError,
)
from .formatter import format_value, set_value
from .parser import load, loads
from .serializer import dump, dumps, generate_schema
from .validator import Error, validate

__all__ = [
"GedcomError",
"GedcomParseError",
"GedcomSerializeError",
"GedcomValidationError",
"Error",
"dump",
"dumps",
"format_value",
"generate_schema",
"load",
"loads",
"set_value",
"validate",
]

try:
Expand Down
16 changes: 16 additions & 0 deletions gedcom7/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from .validator import Error


class GedcomError(Exception):
"""Base class for all errors raised by this package."""
Expand All @@ -11,6 +16,17 @@ class GedcomSerializeError(GedcomError, ValueError):
"""Raised when structures cannot be encoded as a conforming data stream."""


class GedcomValidationError(GedcomError, ValueError):
"""Raised when a dataset fails validation, carrying every problem found."""

def __init__(self, errors: list[Error]) -> None:
"""Record the errors and summarize them in the message."""
self.errors = errors
first = "; ".join(f"{e.path}: {e.message}" for e in errors[:3])
more = f", and {len(errors) - 3} more" if len(errors) > 3 else ""
super().__init__(f"{len(errors)} validation errors: {first}{more}")


class GedcomParseError(GedcomError, ValueError):
"""Raised when a data stream does not conform to the GEDCOM 7 grammar.

Expand Down
File renamed without changes.
14 changes: 12 additions & 2 deletions gedcom7/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
from typing import TYPE_CHECKING

from . import const, grammar
from .exceptions import GedcomSerializeError
from .exceptions import GedcomSerializeError, GedcomValidationError
from .types import GedcomStructure
from .validator import validate as _validate

if TYPE_CHECKING:
from collections.abc import Iterable
Expand Down Expand Up @@ -203,6 +204,7 @@ def dumps(
*,
line_terminator: str = "\n",
byte_order_mark: bool = True,
validate: bool = False,
) -> str:
"""Serialize structures to a GEDCOM 7 data stream.

Expand All @@ -217,7 +219,11 @@ def dumps(
terminators are not altered on the way out.

Raises :class:`~gedcom7.exceptions.GedcomSerializeError` if the structures
cannot be encoded as conforming lines.
cannot be encoded as conforming lines. Pass ``validate=True`` to check the
dataset first and raise
:class:`~gedcom7.exceptions.GedcomValidationError`, whose ``errors`` holds
every problem :func:`~gedcom7.validator.validate` found, rather than only the
first one that stops a line being written.
"""
if line_terminator not in ("\n", "\r\n", "\r"):
raise GedcomSerializeError(
Expand All @@ -226,6 +232,10 @@ def dumps(
)

records = list(records)
if validate:
errors = _validate(records)
if errors:
raise GedcomValidationError(errors)
uris = _schema(records)
lines = [line for record in records for line in _lines(record, 0, uris)]

Expand Down
Loading