Commit e6067f09 authored by Stefan Behnel's avatar Stefan Behnel Committed by GitHub

Merge pull request #2864 from cython/gh2564_enable_binding: Enable "binding" directive by default

parents 4dd7c18d f055a66c
......@@ -9235,7 +9235,10 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
scope = Symtab.StructOrUnionScope(cname)
self.defaults = []
for arg in nonliteral_objects:
entry = scope.declare_var(arg.name, arg.type, None,
type_ = arg.type
if type_.is_buffer:
type_ = type_.base
entry = scope.declare_var(arg.name, type_, None,
Naming.arg_prefix + arg.name,
allow_pyobject=True)
self.defaults.append((arg, entry))
......
......@@ -2300,7 +2300,7 @@ class CFuncDefNode(FuncDefNode):
# is_c_class_method whether this is a cclass method
child_attrs = ["base_type", "declarator", "body", "py_func_stat", "decorators"]
outer_attrs = ["decorators"]
outer_attrs = ["decorators", "py_func_stat"]
inline_in_pxd = False
decorators = None
......@@ -5041,8 +5041,6 @@ class CClassDefNode(ClassDefNode):
# This is needed to generate evaluation code for
# default values of method arguments.
code.mark_pos(self.pos)
if self.body:
self.body.generate_execution_code(code)
if not self.entry.type.early_init:
if self.type_init_args:
self.type_init_args.generate_evaluation_code(code)
......@@ -5070,6 +5068,8 @@ class CClassDefNode(ClassDefNode):
self.type_init_args.free_temps(code)
self.generate_type_ready_code(self.entry, code, True)
if self.body:
self.body.generate_execution_code(code)
# Also called from ModuleNode for early init types.
@staticmethod
......
......@@ -169,6 +169,7 @@ def get_directive_defaults():
# Declare compiler directives
_directive_defaults = {
'binding': True,
'boundscheck' : True,
'nonecheck' : False,
'initializedcheck' : True,
......@@ -237,8 +238,6 @@ _directive_defaults = {
'test_fail_if_path_exists' : [],
# experimental, subject to change
'binding': None,
'formal_grammar': False,
}
......
......@@ -713,7 +713,11 @@ Cython code. Here is the list of currently supported directives:
class attribute (hence the name) and will emulate the attributes
of Python functions, including introspections like argument names and
annotations.
Default is False.
Default is True.
.. versionchanged:: 3.0.0
Default changed from False to True
``boundscheck`` (True / False)
If set to False, Cython is free to assume that indexing operations
......
......@@ -959,6 +959,7 @@ def addref(*args):
def decref(*args):
for item in args: Py_DECREF(item)
@cython.binding(False)
def get_refcount(x):
return (<PyObject*>x).ob_refcnt
......@@ -991,15 +992,16 @@ def assign_to_object(object[object] buf, int idx, obj):
See comments on printbuf_object above.
>>> a, b = [1, 2, 3], [4, 5, 6]
>>> get_refcount(a), get_refcount(b)
(2, 2)
>>> rca1, rcb1 = get_refcount(a), get_refcount(b)
>>> rca1 == rcb1
True
>>> addref(a)
>>> A = ObjectMockBuffer(None, [1, a]) # 1, ...,otherwise it thinks nested lists...
>>> get_refcount(a), get_refcount(b)
(3, 2)
>>> get_refcount(a) == rca1+1, get_refcount(b) == rcb1
(True, True)
>>> assign_to_object(A, 1, b)
>>> get_refcount(a), get_refcount(b)
(2, 3)
>>> get_refcount(a) == rca1, get_refcount(b) == rcb1+1
(True, True)
>>> decref(b)
"""
buf[idx] = obj
......@@ -1010,15 +1012,14 @@ def assign_temporary_to_object(object[object] buf):
See comments on printbuf_object above.
>>> a, b = [1, 2, 3], {4:23}
>>> get_refcount(a)
2
>>> rc1 = get_refcount(a)
>>> addref(a)
>>> A = ObjectMockBuffer(None, [b, a])
>>> get_refcount(a)
3
>>> get_refcount(a) == rc1+1
True
>>> assign_temporary_to_object(A)
>>> get_refcount(a)
2
>>> get_refcount(a) == rc1
True
>>> printbuf_object(A, (2,))
{4: 23} 2
......
......@@ -626,6 +626,7 @@ def addref(*args):
def decref(*args):
for item in args: Py_DECREF(item)
@cython.binding(False)
def get_refcount(x):
return (<PyObject*>x).ob_refcnt
......
......@@ -1058,6 +1058,7 @@ def addref(*args):
def decref(*args):
for item in args: Py_DECREF(item)
@cython.binding(False)
def get_refcount(x):
return (<PyObject*>x).ob_refcnt
......@@ -2141,7 +2142,7 @@ def test_object_dtype_copying():
7
8
9
2 5
5
1 5
"""
cdef int i
......@@ -2162,10 +2163,12 @@ def test_object_dtype_copying():
print m2[i]
obj = m2[5]
print get_refcount(obj), obj
refcount1 = get_refcount(obj)
print obj
del m2
print get_refcount(obj), obj
refcount2 = get_refcount(obj)
print refcount1 - refcount2, obj
assert unique_refcount == get_refcount(unique), (unique_refcount, get_refcount(unique))
......
cimport cython
cdef class CBase(object):
cdef int a
cdef c_method(self):
......@@ -9,7 +11,8 @@ class PyBase(object):
def py_method(self):
return "PyBase"
cdef class Both(CBase, PyBase):
@cython.binding(True)
cdef class BothBound(CBase, PyBase):
cdef dict __dict__
"""
>>> b = Both()
......@@ -32,7 +35,7 @@ cdef class Both(CBase, PyBase):
def call_c_method(self):
return self.c_method()
cdef class BothSub(Both):
cdef class BothSub(BothBound):
"""
>>> b = BothSub()
>>> b.py_method()
......@@ -43,3 +46,27 @@ cdef class BothSub(Both):
'Both'
"""
pass
@cython.binding(False)
cdef class BothUnbound(CBase, PyBase):
cdef dict __dict__
"""
>>> b = Both()
>>> b.py_method()
'PyBase'
>>> b.cp_method()
'Both'
>>> b.call_c_method()
'Both'
>>> isinstance(b, CBase)
True
>>> isinstance(b, PyBase)
True
"""
cdef c_method(self):
return "Both"
cpdef cp_method(self):
return "Both"
def call_c_method(self):
return self.c_method()
# cython: binding=True
# mode: run
# tag: cyfunction
cpdef int simple() nogil:
"""
>>> simple()
1
"""
return 1
cpdef int call_nogil():
"""
>>> call_nogil()
1
"""
with nogil:
return simple()
cimport cython
cdef sorteditems(d):
return tuple(sorted(d.items()))
......@@ -90,7 +92,24 @@ cdef class Silly:
>>> s.onlyt(1, a=2)
Traceback (most recent call last):
TypeError: onlyt() got an unexpected keyword argument 'a'
>>> test_no_copy_args(s.onlyt)
"""
return a
@cython.binding(False) # passthrough of exact same tuple can't work with binding
def onlyt_nobinding(self, *a):
"""
>>> s = Silly()
>>> s.onlyt_nobinding(1)
(1,)
>>> s.onlyt_nobinding(1,2)
(1, 2)
>>> s.onlyt_nobinding(a=1)
Traceback (most recent call last):
TypeError: onlyt_nobinding() got an unexpected keyword argument 'a'
>>> s.onlyt_nobinding(1, a=2)
Traceback (most recent call last):
TypeError: onlyt_nobinding() got an unexpected keyword argument 'a'
>>> test_no_copy_args(s.onlyt_nobinding)
True
"""
return a
......@@ -130,6 +149,7 @@ cdef class Silly:
"""
return a + sorteditems(k)
@cython.binding(False) # passthrough of exact same tuple can't work with binding
def t_kwonly(self, *a, k):
"""
>>> s = Silly()
......
......@@ -12,3 +12,39 @@ class A:
def foo(self):
return self is not None
# assignment of functions used in a "static method" type way behaves differently
# in Python2 and 3
import sys
if sys.version_info[0] == 2:
__doc__ = """>>> B.plus1(1) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: unbound
"""
else:
__doc__ = """>>> B.plus1(1)
2
"""
# with binding==False assignment of functions always worked - doesn't match Python
# behaviour but ensures Cython behaviour stays consistent
__doc__ += """
>>> B.plus1_nobind(1)
2
"""
cimport cython
def f_plus(a):
return a + 1
@cython.binding(False)
def f_plus_nobind(a):
return a+1
cdef class B:
plus1 = f_plus
plus1_nobind = f_plus_nobind
......@@ -11,3 +11,35 @@ class A:
def foo(self):
return self is not None
# assignment of functions used in a "static method" type way behaves differently
# in Python2 and 3
import sys
if sys.version_info[0] == 2:
__doc__ = u"""
>>> B.plus1(1) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: unbound
>>> C.plus1(1) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: unbound
"""
else:
__doc__ = u"""
>>> B.plus1(1)
2
>>> C.plus1(1)
2
"""
def f_plus(a):
return a + 1
class B:
plus1 = f_plus
class C(object):
plus1 = f_plus
......@@ -3,12 +3,17 @@
cimport cython
# Use a single global object for identity checks.
# PyPy can optimise away integer objects, for example, and may fail the 'is' test.
obj = object()
@cython.test_fail_if_path_exists('//NotNode')
def is_not(a, b):
"""
>>> is_not(1, 2)
True
>>> x = 1
>>> x = obj
>>> is_not(x, x)
False
"""
......@@ -20,7 +25,7 @@ def not_is_not(a, b):
"""
>>> not_is_not(1, 2)
False
>>> x = 1
>>> x = obj
>>> not_is_not(x, x)
True
"""
......@@ -32,7 +37,7 @@ def not_is(a, b):
"""
>>> not_is(1, 2)
True
>>> x = 1
>>> x = obj
>>> not_is(x, x)
False
"""
......
......@@ -46,9 +46,13 @@ def compute(val):
def a(in_k, x1, x2, x3, x4, x5):
"""
>>> import sys
>>> sys.setrecursionlimit(1350)
>>> old_limit = sys.getrecursionlimit()
>>> sys.setrecursionlimit(1350 if not getattr(sys, 'pypy_version_info', None) else 2700)
>>> a(10, 1, -1, -1, 1, 0)
-67
>>> sys.setrecursionlimit(old_limit)
"""
k = [in_k]
def b():
......
......@@ -12,9 +12,7 @@ __doc__ = u"""
>>> short_stats['f_cdef']
100
>>> short_stats['f_cpdef']
200
>>> short_stats['f_cpdef (wrapper)']
100
300
>>> short_stats['f_inline']
100
>>> short_stats['f_inline_prof']
......@@ -50,9 +48,7 @@ __doc__ = u"""
>>> short_stats['m_cdef']
100
>>> short_stats['m_cpdef']
200
>>> short_stats['m_cpdef (wrapper)']
100
300
>>> try:
... os.unlink(statsfile)
......@@ -60,10 +56,10 @@ __doc__ = u"""
... pass
>>> sorted(callees(s, 'test_profile')) #doctest: +NORMALIZE_WHITESPACE
['f_cdef', 'f_cpdef', 'f_cpdef (wrapper)', 'f_def',
['f_cdef', 'f_cpdef', 'f_def',
'f_inline', 'f_inline_prof',
'f_raise',
'm_cdef', 'm_cpdef', 'm_cpdef (wrapper)', 'm_def',
'm_cdef', 'm_cpdef', 'm_def',
'withgil_prof']
>>> profile.runctx("test_generators()", locals(), globals(), statsfile)
......
# tag: pstats
# cython: profile = True
# cython: binding = False
__doc__ = u"""
>>> import os, tempfile, cProfile as profile, pstats
>>> statsfile = tempfile.mkstemp()[1]
>>> profile.runctx("test_profile(100)", locals(), globals(), statsfile)
>>> s = pstats.Stats(statsfile)
>>> short_stats = dict([(k[2], v[1]) for k,v in s.stats.items()])
>>> short_stats['f_def']
100
>>> short_stats['f_cdef']
100
>>> short_stats['f_cpdef']
200
>>> short_stats['f_cpdef (wrapper)']
100
>>> short_stats['f_inline']
100
>>> short_stats['f_inline_prof']
100
>>> short_stats['f_noprof']
Traceback (most recent call last):
...
KeyError: 'f_noprof'
>>> short_stats['f_raise']
100
>>> short_stats['withgil_prof']
100
>>> short_stats['withgil_noprof']
Traceback (most recent call last):
...
KeyError: 'withgil_noprof'
>>> short_stats['nogil_prof']
Traceback (most recent call last):
...
KeyError: 'nogil_prof'
>>> short_stats['nogil_noprof']
Traceback (most recent call last):
...
KeyError: 'nogil_noprof'
>>> short_stats['f_raise']
100
>>> short_stats['m_def']
200
>>> short_stats['m_cdef']
100
>>> short_stats['m_cpdef']
200
>>> short_stats['m_cpdef (wrapper)']
100
>>> try:
... os.unlink(statsfile)
... except:
... pass
>>> sorted(callees(s, 'test_profile')) #doctest: +NORMALIZE_WHITESPACE
['f_cdef', 'f_cpdef', 'f_cpdef (wrapper)', 'f_def',
'f_inline', 'f_inline_prof',
'f_raise',
'm_cdef', 'm_cpdef', 'm_cpdef (wrapper)', 'm_def',
'withgil_prof']
>>> profile.runctx("test_generators()", locals(), globals(), statsfile)
>>> s = pstats.Stats(statsfile)
>>> short_stats = dict([(k[2], v[1]) for k,v in s.stats.items()])
>>> short_stats['generator']
3
>>> short_stats['generator_exception']
2
>>> short_stats['genexpr']
11
>>> sorted(callees(s, 'test_generators'))
['call_generator', 'call_generator_exception', 'generator_expr']
>>> list(callees(s, 'call_generator'))
['generator']
>>> list(callees(s, 'generator'))
[]
>>> list(callees(s, 'generator_exception'))
[]
>>> list(callees(s, 'generator_expr'))
['genexpr']
>>> list(callees(s, 'genexpr'))
[]
>>> def python_generator():
... yield 1
... yield 2
>>> def call_python_generator():
... list(python_generator())
>>> profile.runctx("call_python_generator()", locals(), globals(), statsfile)
>>> python_stats = pstats.Stats(statsfile)
>>> python_stats_dict = dict([(k[2], v[1]) for k,v in python_stats.stats.items()])
>>> profile.runctx("call_generator()", locals(), globals(), statsfile)
>>> cython_stats = pstats.Stats(statsfile)
>>> cython_stats_dict = dict([(k[2], v[1]) for k,v in cython_stats.stats.items()])
>>> python_stats_dict['python_generator'] == cython_stats_dict['generator']
True
>>> try:
... os.unlink(statsfile)
... except:
... pass
"""
cimport cython
def callees(pstats, target_caller):
pstats.calc_callees()
for (_, _, caller), callees in pstats.all_callees.items():
if caller == target_caller:
for (file, line, callee) in callees.keys():
if 'pyx' in file:
yield callee
def test_profile(long N):
cdef long i, n = 0
cdef A a = A()
for i from 0 <= i < N:
n += f_def(i)
n += f_cdef(i)
n += f_cpdef(i)
n += (<object>f_cpdef)(i)
n += f_inline(i)
n += f_inline_prof(i)
n += f_noprof(i)
n += nogil_noprof(i)
n += nogil_prof(i)
n += withgil_noprof(i)
n += withgil_prof(i)
n += a.m_def(i)
n += (<object>a).m_def(i)
n += a.m_cpdef(i)
n += (<object>a).m_cpdef(i)
n += a.m_cdef(i)
try:
n += f_raise(i+2)
except RuntimeError:
pass
return n
def f_def(long a):
return a
cdef long f_cdef(long a):
return a
cpdef long f_cpdef(long a):
return a
cdef inline long f_inline(long a):
return a
@cython.profile(True)
cdef inline long f_inline_prof(long a):
return a
@cython.profile(False)
cdef int f_noprof(long a):
return a
cdef long f_raise(long) except -2:
raise RuntimeError
@cython.profile(False)
cdef int withgil_noprof(long a) with gil:
return (a)
@cython.profile(True)
cdef int withgil_prof(long a) with gil:
return (a)
@cython.profile(False)
cdef int nogil_noprof(long a) nogil:
return a
@cython.profile(True)
cdef int nogil_prof(long a) nogil:
return a
cdef class A(object):
def m_def(self, long a):
return a
cpdef m_cpdef(self, long a):
return a
cdef m_cdef(self, long a):
return a
def test_generators():
call_generator()
call_generator_exception()
generator_expr()
def call_generator():
list(generator())
def generator():
yield 1
yield 2
def call_generator_exception():
try:
list(generator_exception())
except ValueError:
pass
def generator_exception():
yield 1
raise ValueError(2)
def generator_expr():
e = (x for x in range(10))
return sum(e)
__doc__ = u"""
>>> class1.plus1(1)
2
>>> class2.plus1(1)
2
>>> class3.plus1(1)
2
>>> class4.plus1(1)
2
>>> class4().plus1(1)
2
>>> class4.bplus1(1)
2
>>> class4().bplus1(1)
2
"""
cimport cython
def f_plus(a):
return a + 1
class class1:
plus1 = f_plus
class class2(object):
plus1 = f_plus
cdef class class3:
plus1 = f_plus
class class4:
u"""
>>> class1.plus1(1)
2
>>> class1().plus1(1)
2
>>> class1.bplus1(1)
2
>>> class1().bplus1(1)
2
"""
@staticmethod
def plus1(a):
return a + 1
......@@ -49,14 +31,14 @@ def nested_class():
>>> obj.plus1(1)
2
"""
class class5(object):
class class2(object):
def __new__(cls): # implicit staticmethod
return object.__new__(cls)
@staticmethod
def plus1(a):
return a + 1
return class5
return class2
cdef class BaseClass(object):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment