Repository: cython/cython

Issue:
[BUG] int annotation now sets the type to exact Python int
**Describe the bug**

Since 31d40c8c62acef9509675155fe5b5bb8e48dba5a an annotation of `int` now corresponds to an exact Python `int`. This matters on Python 2 because it rejects `long`. Was this intended?

**To Reproduce**
Code to reproduce the behaviour:
```cython
def f(x: int):
  return x+1
```

then from python 2
```python
import testmod
testmod.f(1)  # works
testmod.f(long(1))  # fails
```

**Environment (please complete the following information):**
 - Python version: 2.7
 - Cython version: current master branch

**Additional context**
Obviously this is the long-term plan, but I'm not sure we meant to do it now?

Also, whether or not it's supported we probably need some test coverage


Discussion:

Resolving patch:
diff --git a/Cython/Compiler/Builtin.py b/Cython/Compiler/Builtin.py
--- a/Cython/Compiler/Builtin.py
+++ b/Cython/Compiler/Builtin.py
@@ -427,6 +427,7 @@ def init_builtins():
     global list_type, tuple_type, dict_type, set_type, frozenset_type
     global bytes_type, str_type, unicode_type, basestring_type, slice_type
     global float_type, long_type, bool_type, type_type, complex_type, bytearray_type
+    global int_type
     type_type  = builtin_scope.lookup('type').type
     list_type  = builtin_scope.lookup('list').type
     tuple_type = builtin_scope.lookup('tuple').type
@@ -443,6 +444,8 @@ def init_builtins():
     long_type = builtin_scope.lookup('long').type
     bool_type  = builtin_scope.lookup('bool').type
     complex_type  = builtin_scope.lookup('complex').type
+    # Be careful with int type while Py2 is still supported
+    int_type = builtin_scope.lookup('int').type
 
     # Set up type inference links between equivalent Python/C types
     bool_type.equivalent_type = PyrexTypes.c_bint_type
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -1538,6 +1538,11 @@ def _analyse_name_as_type(name, pos, env):
     global_entry = env.global_scope().lookup(name)
     if global_entry and global_entry.is_type:
         type = global_entry.type
+        if (not env.in_c_type_context and
+                name == 'int' and type is Builtin.int_type):
+            # While we still support Python2 this needs to be downgraded
+            # to a generic Python object to include both int and long
+            type = py_object_type
         if type and (type.is_pyobject or env.in_c_type_context):
             return type
         ctype = ctype or type
@@ -2119,6 +2124,10 @@ def analyse_as_type(self, env):
                 type = py_object_type
             elif type.is_pyobject and type.equivalent_type:
                 type = type.equivalent_type
+            elif type is Builtin.int_type:
+                # while we still support Python 2 this must be an object
+                # so that it can be either int or long
+                type = py_object_type
             return type
         if self.name == 'object':
             # This is normally parsed as "simple C type", but not if we don't parse C types.


Test patch:
diff --git a/tests/run/annotation_typing.pyx b/tests/run/annotation_typing.pyx
--- a/tests/run/annotation_typing.pyx
+++ b/tests/run/annotation_typing.pyx
@@ -14,10 +14,10 @@ except ImportError:
 def old_dict_syntax(a: list, b: "int" = 2, c: {'ctype': 'long int'} = 3, d: {'type': 'long int'} = 4) -> list:
     """
     >>> old_dict_syntax([1])
-    ('list object', 'int object', 'long', 'long')
+    ('list object', 'Python object', 'long', 'long')
     [1, 2, 3, 4]
     >>> old_dict_syntax([1], 3)
-    ('list object', 'int object', 'long', 'long')
+    ('list object', 'Python object', 'long', 'long')
     [1, 3, 3, 4]
     >>> old_dict_syntax(123)
     Traceback (most recent call last):
@@ -36,13 +36,13 @@ def old_dict_syntax(a: list, b: "int" = 2, c: {'ctype': 'long int'} = 3, d: {'ty
 def pytypes_def(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list = None, o: Optional[tuple] = ()) -> list:
     """
     >>> pytypes_def([1])
-    ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object')
+    ('list object', 'Python object', 'Python object', 'double', 'list object', 'tuple object')
     [1, 2, 3, 4.0, None, ()]
     >>> pytypes_def([1], 3)
-    ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object')
+    ('list object', 'Python object', 'Python object', 'double', 'list object', 'tuple object')
     [1, 3, 3, 4.0, None, ()]
     >>> pytypes_def([1], 3, 2, 1, [], None)
-    ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object')
+    ('list object', 'Python object', 'Python object', 'double', 'list object', 'tuple object')
     [1, 3, 2, 1.0, [], None]
     >>> pytypes_def(123)
     Traceback (most recent call last):
@@ -63,13 +63,13 @@ def pytypes_def(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list = None
 cpdef pytypes_cpdef(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list = None, o: Optional[tuple] = ()):
     """
     >>> pytypes_cpdef([1])
-    ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object')
+    ('list object', 'Python object', 'Python object', 'double', 'list object', 'tuple object')
     [1, 2, 3, 4.0, None, ()]
     >>> pytypes_cpdef([1], 3)
-    ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object')
+    ('list object', 'Python object', 'Python object', 'double', 'list object', 'tuple object')
     [1, 3, 3, 4.0, None, ()]
     >>> pytypes_cpdef([1], 3, 2, 1, [], None)
-    ('list object', 'int object', 'Python object', 'double', 'list object', 'tuple object')
+    ('list object', 'Python object', 'Python object', 'double', 'list object', 'tuple object')
     [1, 3, 2, 1.0, [], None]
     >>> pytypes_cpdef(123)
     Traceback (most recent call last):
@@ -99,10 +99,10 @@ cdef c_pytypes_cdef(a: list, b: int = 2, c: long = 3, d: float = 4.0, n: list =
 def pytypes_cdef(a, b=2, c=3, d=4):
     """
     >>> pytypes_cdef([1])
-    ('list object', 'int object', 'Python object', 'double', 'list object')
+    ('list object', 'Python object', 'Python object', 'double', 'list object')
     [1, 2, 3, 4.0, None]
     >>> pytypes_cdef([1], 3)
-    ('list object', 'int object', 'Python object', 'double', 'list object')
+    ('list object', 'Python object', 'Python object', 'double', 'list object')
     [1, 3, 3, 4.0, None]
     >>> pytypes_cdef(123)   # doctest: +ELLIPSIS
     Traceback (most recent call last):
@@ -111,6 +111,15 @@ def pytypes_cdef(a, b=2, c=3, d=4):
     return c_pytypes_cdef(a, b, c, d)
 
 
+def pyint(a: int):
+    """
+    >>> large_int = eval('0x'+'F'*64)  # definitely bigger than C int64
+    >>> pyint(large_int) == large_int
+    True
+    """
+    return a
+
+
 def ctypes_def(a: list, b: cython.int = 2, c: cython.long = 3, d: cython.float = 4) -> list:
     """
     >>> ctypes_def([1])
@@ -372,14 +381,14 @@ _WARNINGS = """
 63:70: PEP-484 recommends 'typing.Optional[...]' for arguments that can be None.
 90:44: Found Python 2.x type 'long' in a Python annotation. Did you mean to use 'cython.long'?
 90:70: PEP-484 recommends 'typing.Optional[...]' for arguments that can be None.
-274:44: Unknown type declaration in annotation, ignoring
-302:15: Annotation ignored since class-level attributes must be Python objects. Were you trying to set up an instance attribute?
+283:44: Unknown type declaration in annotation, ignoring
+311:15: Annotation ignored since class-level attributes must be Python objects. Were you trying to set up an instance attribute?
 # DUPLICATE:
 63:44: Found Python 2.x type 'long' in a Python annotation. Did you mean to use 'cython.long'?
 # BUG:
 63:6: 'pytypes_cpdef' redeclared
-146:0: 'struct_io' redeclared
-181:0: 'struct_convert' redeclared
-200:0: 'exception_default' redeclared
-231:0: 'exception_default_uint' redeclared
+155:0: 'struct_io' redeclared
+190:0: 'struct_convert' redeclared
+209:0: 'exception_default' redeclared
+240:0: 'exception_default_uint' redeclared
 """