Repository: sympy/sympy

Issue:
Sympy numbers don't adhere to Python's numbers ABC
I'd expect this to be `True`:

```
>>> import sympy
>>> x = sympy.numbers.Float(5)
>>> import numbers
>>> isinstance(x, numbers.Number)
False
```

https://docs.python.org/3/library/numbers.html

(Sorry if this has been considered before--a quick search didn't reveal anything.)

Discussion:

Patch:
diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py
index aa717b97ef..916388735a 100644
--- a/sympy/core/numbers.py
+++ b/sympy/core/numbers.py
@@ -1,5 +1,6 @@
-from __future__ import print_function, division
+from __future__ import absolute_import, print_function, division
 
+import numbers
 import decimal
 import fractions
 import math
@@ -594,6 +595,12 @@ def ceiling(self):
         raise NotImplementedError('%s needs .ceiling() method' %
             (self.__class__.__name__))
 
+    def __floor__(self):
+        return self.floor()
+
+    def __ceil__(self):
+        return self.ceiling()
+
     def _eval_conjugate(self):
         return self
 
@@ -1102,6 +1109,13 @@ def ceiling(self):
         return Integer(int(mlib.to_int(
             mlib.mpf_ceil(self._mpf_, self._prec))))
 
+    def __floor__(self):
+        return self.floor()
+
+    def __ceil__(self):
+        return self.ceiling()
+
+
     @property
     def num(self):
         return mpmath.mpf(self._mpf_)
@@ -1727,6 +1741,12 @@ def floor(self):
     def ceiling(self):
         return -Integer(-self.p // self.q)
 
+    def __floor__(self):
+        return self.floor()
+
+    def __ceil__(self):
+        return self.ceiling()
+
     def __eq__(self, other):
         try:
             other = _sympify(other)
@@ -1835,6 +1855,12 @@ def factors(self, limit=None, use_trial=True, use_rho=False,
                       use_rho=use_rho, use_pm1=use_pm1,
                       verbose=verbose).copy()
 
+    def numerator(self):
+        return self.p
+
+    def denominator(self):
+        return self.q
+
     @_sympifyit('other', NotImplemented)
     def gcd(self, other):
         if isinstance(other, Rational):
@@ -1972,6 +1998,12 @@ def floor(self):
     def ceiling(self):
         return Integer(self.p)
 
+    def __floor__(self):
+        return self.floor()
+
+    def __ceil__(self):
+        return self.ceiling()
+
     def __neg__(self):
         return Integer(-self.p)
 
@@ -3861,3 +3893,11 @@ def sympify_complex(a):
 Mul.identity = One()
 from .add import Add
 Add.identity = Zero()
+
+def _register_classes():
+    numbers.Number.register(Number)
+    numbers.Real.register(Float)
+    numbers.Rational.register(Rational)
+    numbers.Rational.register(Integer)
+
+_register_classes()


Test patch:
diff --git a/sympy/core/tests/test_numbers.py b/sympy/core/tests/test_numbers.py
index 890af31db5..a88226f07b 100644
--- a/sympy/core/tests/test_numbers.py
+++ b/sympy/core/tests/test_numbers.py
@@ -1,3 +1,5 @@
+from __future__ import absolute_import
+import numbers as nums
 import decimal
 from sympy import (Rational, Symbol, Float, I, sqrt, cbrt, oo, nan, pi, E,
                    Integer, S, factorial, Catalan, EulerGamma, GoldenRatio,
@@ -19,9 +21,7 @@
 from mpmath import mpf
 from mpmath.rational import mpq
 import mpmath
-
-
-
+from sympy import numbers
 t = Symbol('t', real=False)
 
 def same_and_same_prec(a, b):
@@ -1912,3 +1912,16 @@ def test_issue_6133():
     raises(TypeError, lambda: (oo < None))
     raises(TypeError, lambda: (oo > None))
     raises(TypeError, lambda: (S(2) < None))
+
+def test_abc():
+    x = numbers.Float(5)
+    assert(isinstance(x, nums.Number))
+    assert(isinstance(x, numbers.Number))
+    assert(isinstance(x, nums.Real))
+    y = numbers.Rational(1, 3)
+    assert(isinstance(y, nums.Number))
+    assert(y.numerator() == 1)
+    assert(y.denominator() == 3)
+    assert(isinstance(y, nums.Rational))
+    z = numbers.Integer(3)
+    assert(isinstance(z, nums.Number))