IBM / jsonsubschema

Tool for checking whether a JSON schema is a subschema of another JSON schema.
Apache License 2.0
82 stars 17 forks source link

Fix removed fractions.gcd in Python >= 3.9 #30

Closed kmichel-aiven closed 2 weeks ago

kmichel-aiven commented 2 weeks ago

fractions.gcd was removed. The recommended alternative is math.gcd but it does not work with integers (and some of the test cases use floats).

Re-implement gcd by using Python 3.8 implementation, but add a step to turn floats into fractions.

This isn't pretty but gives less surprising results for the kind of numbers that are expected to appear in JSON schemas :

>>> from fractions import Fraction, gcd
>>> gcd(0.1, 0.01)
1.734723475976807e-18
>>> gcd(Fraction(str(0.1)), Fraction(str(0.01)))
Fraction(1, 100)
>>> gcd(0.6, 0.4)
1.1102230246251565e-16
>>> gcd(Fraction(str(0.6)), Fraction(str(0.4)))
Fraction(1, 5)
>>> from fractions import Fraction, gcd
>>> gcd(0.1, 0.01)
1.734723475976807e-18
>>> gcd(Fraction(str(0.1)), Fraction(str(0.01)))
Fraction(1, 100)
>>> gcd(0.6, 0.4)
1.1102230246251565e-16
>>> gcd(Fraction(str(0.6)), Fraction(str(0.4)))
Fraction(1, 5)
>>> gcd(0.1, 0.25)
2.7755575615628914e-17
>>> gcd(Fraction(str(0.1)), Fraction(str(0.25)))
Fraction(1, 20)

This fixes:

Traceback (most recent call last):
  File "/home/runner/work/jsonsubschema/jsonsubschema/test/test_numeric.py", line 647, in test_all_all_3
    self.assertFalse(isSubschema(s1, s2))
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/api.py", line 56, in isSubschema
    s1, s2 = prepare_operands(s1, s2)
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/api.py", line 39, in prepare_operands
    canonicalize_schema(s1))
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_canonicalization.py", line 35, in canonicalize_schema
    canonical_schema = canonicalize_dict(obj)
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_canonicalization.py", line 84, in canonicalize_dict
    return canonicalize_connectors(d)
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_canonicalization.py", line 218, in canonicalize_connectors
    allofs.append(canonicalize_dict({c: d[c]}))
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_canonicalization.py", line 84, in canonicalize_dict
    return canonicalize_connectors(d)
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_canonicalization.py", line 211, in canonicalize_connectors
    simplified = simplify_schema_and_embed_checkers(d)
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_canonicalization.py", line 369, in simplify_schema_and_embed_checkers
    allofs = [simplify_schema_and_embed_checkers(i) for i in s["allOf"]]
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_canonicalization.py", line 369, in <listcomp>
    allofs = [simplify_schema_and_embed_checkers(i) for i in s["allOf"]]
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_canonicalization.py", line 366, in simplify_schema_and_embed_checkers
    return boolToConstructor.get("anyOf")({"anyOf": anyofs})
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_checkers.py", line 1481, in JSONanyOfFactory
    ret = ret.join(i)
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_checkers.py", line 133, in join
    ret = self._join(s)
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_checkers.py", line 686, in _join
    return _joinNumber(self, s)
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_checkers.py", line 672, in _joinNumber
    gcd = utils.gcd(s1.multipleOf, s2.multipleOf)
  File "/home/runner/work/jsonsubschema/jsonsubschema/jsonsubschema/_utils.py", line 269, in gcd
    return fractions.gcd(x, y)
AttributeError: module 'fractions' has no attribute 'gcd'