From 46db62a373ccc3d75fefd36f97a5fa8fcafedac4 Mon Sep 17 00:00:00 2001 From: Zac-HD Date: Mon, 5 Oct 2020 10:35:10 +1100 Subject: [PATCH] Handle multipleOf overflow --- jsonschema/_validators.py | 14 ++++++- .../tests/test_jsonschema_test_suite.py | 39 ------------------- 2 files changed, 13 insertions(+), 40 deletions(-) diff --git a/jsonschema/_validators.py b/jsonschema/_validators.py index d7097ffbb..7e8c95a4e 100644 --- a/jsonschema/_validators.py +++ b/jsonschema/_validators.py @@ -1,3 +1,4 @@ +from fractions import Fraction import re from jsonschema._utils import ( @@ -166,7 +167,18 @@ def multipleOf(validator, dB, instance, schema): if isinstance(dB, float): quotient = instance / dB - failed = int(quotient) != quotient + try: + failed = int(quotient) != quotient + except OverflowError: + # When `instance` is large and `dB` is less than one, quotient can + # overflow to infinity; and then casting to int raises an error. + # + # In this case we fall back to Fraction logic, which is exact and + # cannot overflow. The performance is also acceptable: we try the + # fast all-float option first, and we know that fraction(dB) can have + # at most a few hundred digits in each part. The worst-case slowdown + # is therefore for already-slow enormous integers or Decimals. + failed = (Fraction(instance) / Fraction(dB)).denominator != 1 else: failed = instance % dB diff --git a/jsonschema/tests/test_jsonschema_test_suite.py b/jsonschema/tests/test_jsonschema_test_suite.py index d6721b597..0f9698a64 100644 --- a/jsonschema/tests/test_jsonschema_test_suite.py +++ b/jsonschema/tests/test_jsonschema_test_suite.py @@ -161,7 +161,6 @@ def missing_date_fromisoformat(test): DRAFT4.tests(), DRAFT4.format_tests(), DRAFT4.optional_tests_of(name="bignum"), - DRAFT4.optional_tests_of(name="float-overflow"), DRAFT4.optional_tests_of(name="non-bmp-regex"), DRAFT4.optional_tests_of(name="zeroTerminatedFloats"), Validator=Draft4Validator, @@ -235,18 +234,6 @@ def missing_date_fromisoformat(test): subject="uniqueItems", description='{"a": true} and {"a": 1} are unique', )(test) - or skip( - message=bug(743), - subject="float-overflow", - )(test) - or skip( - message=bug(743), - subject="multipleOf", - description=( - "always invalid, but naive implementations " - "may raise an overflow error" - ), - )(test) ), ) @@ -255,7 +242,6 @@ def missing_date_fromisoformat(test): DRAFT6.tests(), DRAFT6.format_tests(), DRAFT6.optional_tests_of(name="bignum"), - DRAFT6.optional_tests_of(name="float-overflow"), DRAFT6.optional_tests_of(name="non-bmp-regex"), Validator=Draft6Validator, format_checker=draft6_format_checker, @@ -348,18 +334,6 @@ def missing_date_fromisoformat(test): subject="const", case_description='const with {"a": true} does not match {"a": 1}', )(test) - or skip( - message=bug(743), - subject="float-overflow", - )(test) - or skip( - message=bug(743), - subject="multipleOf", - description=( - "always invalid, but naive implementations " - "may raise an overflow error" - ), - )(test) ), ) @@ -368,7 +342,6 @@ def missing_date_fromisoformat(test): DRAFT7.tests(), DRAFT7.format_tests(), DRAFT7.optional_tests_of(name="bignum"), - DRAFT7.optional_tests_of(name="float-overflow"), DRAFT7.optional_tests_of(name="content"), DRAFT7.optional_tests_of(name="non-bmp-regex"), Validator=Draft7Validator, @@ -484,18 +457,6 @@ def missing_date_fromisoformat(test): subject="const", case_description='const with {"a": true} does not match {"a": 1}', )(test) - or skip( - message=bug(743), - subject="float-overflow", - )(test) - or skip( - message=bug(743), - subject="multipleOf", - description=( - "always invalid, but naive implementations " - "may raise an overflow error" - ), - )(test) ), )