Skip to content
Open
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
3 changes: 2 additions & 1 deletion jsonschema/_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from contextlib import suppress
from datetime import date, datetime
from uuid import UUID
import decimal
import ipaddress
import re
import string
Expand Down Expand Up @@ -518,7 +519,7 @@ def is_uri_template(instance: object) -> bool:
@_checks_drafts(
draft201909="duration",
draft202012="duration",
raises=isoduration.DurationParsingException,
raises=(isoduration.DurationParsingException, decimal.Overflow),
)
def is_duration(instance: object) -> bool:
if not isinstance(instance, str):
Expand Down
25 changes: 24 additions & 1 deletion jsonschema/tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@
Tests for the parts of jsonschema related to the :kw:`format` keyword.
"""

from unittest import TestCase
from unittest import TestCase, skipUnless

from jsonschema import FormatChecker, ValidationError
from jsonschema.exceptions import FormatError
from jsonschema.validators import Draft4Validator

try:
import isoduration
except ImportError:
isoduration = None

BOOM = ValueError("Boom!")
BANG = ZeroDivisionError("Bang!")

Expand Down Expand Up @@ -80,6 +85,24 @@ def test_format_checkers_come_with_defaults(self):
with self.assertRaises(FormatError):
checker.check(instance="not-an-ipv4", format="ipv4")

@skipUnless(isoduration is not None, "isoduration not installed")
def test_duration_with_a_huge_exponent_is_invalid_not_an_error(self):
# A duration amount whose magnitude exceeds the decimal context's
# ``Emax`` makes isoduration's ``Decimal(...)`` raise
# ``decimal.Overflow`` rather than ``DurationParsingException``. Such
# a string is not a valid RFC 3339 Appendix A duration and should be
# reported as invalid, not propagate an uncaught exception.
checker = FormatChecker()
with self.assertRaises(FormatError):
checker.check(instance="P1E1000000D", format="duration")

@skipUnless(isoduration is not None, "isoduration not installed")
def test_duration_with_a_huge_digit_run_is_invalid_not_an_error(self):
checker = FormatChecker()
instance = "P" + "1" * 1000001 + "D"
with self.assertRaises(FormatError):
checker.check(instance=instance, format="duration")

def test_repr(self):
checker = FormatChecker(formats=())
checker.checks("foo")(lambda thing: True) # pragma: no cover
Expand Down
Loading