Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Jun 16, 2024

Conversation

kmichel-aiven
Copy link
Contributor

@kmichel-aiven kmichel-aiven commented Jun 15, 2024

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'

`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 this 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(0.1, 0.25)
2.7755575615628914e-17
>>> gcd(Fraction(str(0.1)), Fraction(str(0.25)))
Fraction(1, 20)
```
@shinnar shinnar merged commit 5912a05 into IBM:master Jun 16, 2024
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants