Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
cython
Commits
e6067f09
Commit
e6067f09
authored
Mar 23, 2020
by
Stefan Behnel
Committed by
GitHub
Mar 23, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2864 from cython/gh2564_enable_binding: Enable "binding" directive by default
parents
4dd7c18d
f055a66c
Changes
17
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
427 additions
and
67 deletions
+427
-67
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+4
-1
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+3
-3
Cython/Compiler/Options.py
Cython/Compiler/Options.py
+1
-2
docs/src/userguide/source_files_and_compilation.rst
docs/src/userguide/source_files_and_compilation.rst
+5
-1
tests/buffers/bufaccess.pyx
tests/buffers/bufaccess.pyx
+13
-12
tests/memoryview/memoryview.pyx
tests/memoryview/memoryview.pyx
+1
-0
tests/memoryview/memslice.pyx
tests/memoryview/memslice.pyx
+6
-3
tests/run/cdef_multiple_inheritance.pyx
tests/run/cdef_multiple_inheritance.pyx
+29
-2
tests/run/cpdef_nogil.pyx
tests/run/cpdef_nogil.pyx
+19
-0
tests/run/extstarargs.pyx
tests/run/extstarargs.pyx
+21
-1
tests/run/function_as_method_T494.pyx
tests/run/function_as_method_T494.pyx
+36
-0
tests/run/function_as_method_py_T494.py
tests/run/function_as_method_py_T494.py
+32
-0
tests/run/isnot.pyx
tests/run/isnot.pyx
+8
-3
tests/run/knuth_man_or_boy_test.pyx
tests/run/knuth_man_or_boy_test.pyx
+5
-1
tests/run/pstats_profile_test.pyx
tests/run/pstats_profile_test.pyx
+4
-8
tests/run/pstats_profile_test_pycfunc.pyx
tests/run/pstats_profile_test_pycfunc.pyx
+228
-0
tests/run/staticmethod.pyx
tests/run/staticmethod.pyx
+12
-30
No files found.
Cython/Compiler/ExprNodes.py
View file @
e6067f09
...
...
@@ -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
))
...
...
Cython/Compiler/Nodes.py
View file @
e6067f09
...
...
@@ -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
...
...
Cython/Compiler/Options.py
View file @
e6067f09
...
...
@@ -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
,
}
...
...
docs/src/userguide/source_files_and_compilation.rst
View file @
e6067f09
...
...
@@ -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
...
...
tests/buffers/bufaccess.pyx
View file @
e6067f09
...
...
@@ -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
...
...
tests/memoryview/memoryview.pyx
View file @
e6067f09
...
...
@@ -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
...
...
tests/memoryview/memslice.pyx
View file @
e6067f09
...
...
@@ -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
))
...
...
tests/run/cdef_multiple_inheritance.pyx
View file @
e6067f09
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
(
Both
Bound
):
"""
>>> 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
()
tests/run/cpdef_nogil.pyx
0 → 100644
View file @
e6067f09
# 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
()
tests/run/extstarargs.pyx
View file @
e6067f09
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()
...
...
tests/run/function_as_method_T494.pyx
View file @
e6067f09
...
...
@@ -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
tests/run/function_as_method_py_T494.py
View file @
e6067f09
...
...
@@ -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
tests/run/isnot.pyx
View file @
e6067f09
...
...
@@ -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
"""
...
...
tests/run/knuth_man_or_boy_test.pyx
View file @
e6067f09
...
...
@@ -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
():
...
...
tests/run/pstats_profile_test.pyx
View file @
e6067f09
...
...
@@ -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)
...
...
tests/run/pstats_profile_test_pycfunc.pyx
0 → 100644
View file @
e6067f09
# 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
)
tests/run/staticmethod.pyx
View file @
e6067f09
__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
class
5
(
object
):
class
class
2
(
object
):
def
__new__
(
cls
):
# implicit staticmethod
return
object
.
__new__
(
cls
)
@
staticmethod
def
plus1
(
a
):
return
a
+
1
return
class
5
return
class
2
cdef
class
BaseClass
(
object
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment