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
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Xavier Thompson
cython
Commits
613da914
Commit
613da914
authored
Jul 19, 2019
by
gsamain
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Code snippet of nexedi's cypclass blog article
parent
4af9a38b
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
555 additions
and
0 deletions
+555
-0
nogil_test/snippets_article/arithmetic_operators.pyx
nogil_test/snippets_article/arithmetic_operators.pyx
+87
-0
nogil_test/snippets_article/cython_cppclass.pyx
nogil_test/snippets_article/cython_cppclass.pyx
+20
-0
nogil_test/snippets_article/cython_intro.pyx
nogil_test/snippets_article/cython_intro.pyx
+27
-0
nogil_test/snippets_article/del_statement.pyx
nogil_test/snippets_article/del_statement.pyx
+7
-0
nogil_test/snippets_article/nested_definition.pyx
nogil_test/snippets_article/nested_definition.pyx
+15
-0
nogil_test/snippets_article/new_statement.pyx
nogil_test/snippets_article/new_statement.pyx
+40
-0
nogil_test/snippets_article/null_assignment.pyx
nogil_test/snippets_article/null_assignment.pyx
+6
-0
nogil_test/snippets_article/okko_optional_args_wrapper.pyx
nogil_test/snippets_article/okko_optional_args_wrapper.pyx
+23
-0
nogil_test/snippets_article/self_syntax.pyx
nogil_test/snippets_article/self_syntax.pyx
+13
-0
nogil_test/snippets_article/test_class.pyx
nogil_test/snippets_article/test_class.pyx
+10
-0
nogil_test/snippets_article/tricky_multiple_inheritance.pyx
nogil_test/snippets_article/tricky_multiple_inheritance.pyx
+30
-0
nogil_test/snippets_article/tricky_new_init_composition.pyx
nogil_test/snippets_article/tricky_new_init_composition.pyx
+26
-0
nogil_test/snippets_article/typecast_operators.pyx
nogil_test/snippets_article/typecast_operators.pyx
+31
-0
nogil_test/snippets_article/wrapper_alloc.pyx
nogil_test/snippets_article/wrapper_alloc.pyx
+158
-0
nogil_test/snippets_article/wrapper_init.pyx
nogil_test/snippets_article/wrapper_init.pyx
+42
-0
nogil_test/snippets_article/wrapper_new.pyx
nogil_test/snippets_article/wrapper_new.pyx
+20
-0
No files found.
nogil_test/snippets_article/arithmetic_operators.pyx
0 → 100644
View file @
613da914
cdef
cypclass
CrazyOperations
:
int
a
double
b
CrazyOperations
__iadd__
(
self
,
CrazyOperations
other
):
self
.
a
+=
other
.
a
CrazyOperations
__add__
(
self
,
CrazyOperations
other
):
return
CrazyOperations
(
self
.
a
,
self
.
b
+
other
.
b
)
CrazyOperations
__isub__
(
self
,
CrazyOperations
other
):
self
.
b
-=
other
.
b
__init__
(
self
,
int
a
=
0
,
double
b
=
4.2
):
self
.
a
=
a
self
.
b
=
b
cdef
cypclass
SuperCrazyOperations
(
CrazyOperations
):
__init__
(
self
,
int
a
=
0
,
double
b
=
4.2
):
CrazyOperations
.
__init__
(
self
,
a
,
b
)
SuperCrazyOperations
__iadd__
(
self
,
CrazyOperations
other
):
CrazyOperations
.
__iadd__
(
self
,
other
)
SuperCrazyOperations
__iadd__
(
self
,
SuperCrazyOperations
other
):
# This is one (possibly weird) way to chain to base class' __iadd__, done here for the example
casted_self
=
<
CrazyOperations
>
self
# As this is casted, we will call base __iadd__ with the += below
casted_self
+=
<
CrazyOperations
>
other
self
.
b
=
0
# At the end, we added both a attributes, and set out b to 0.0
SuperCrazyOperations
__isub__
(
self
,
CrazyOperations
other
):
CrazyOperations
.
__isub__
(
self
,
other
)
SuperCrazyOperations
__isub__
(
self
,
SuperCrazyOperations
other
):
CrazyOperations
.
__isub__
(
self
,
other
)
self
.
a
=
4
def
test
():
o1
=
SuperCrazyOperations
(
1
,
2
)
o2
=
CrazyOperations
(
42
,
4.2
)
o3
=
SuperCrazyOperations
(
-
3
,
2.1
)
# This should print:
# 1 2.0
print
(
o1
.
a
,
o1
.
b
)
# This should print:
# 42 4.2
print
(
o2
.
a
,
o2
.
b
)
# This should print:
# -3 2.1
print
(
o3
.
a
,
o3
.
b
)
# We're calling __add__ so we just add the b attributes
tmp1
=
o1
+
o2
# This should print:
# 1 6.2
print
(
tmp1
.
a
,
tmp1
.
b
)
# CrazyOperations' __iadd__ is called: adding the a attributes
o2
+=
o1
# This should print:
# 43 4.2
print
(
o2
.
a
,
o2
.
b
)
# SuperCrazyOperations' __iadd__ is called: adding the a attributes AND setting own (o1) b to 0.0
o1
+=
o3
# This should print:
# -2 0
print
(
o1
.
a
,
o1
.
b
)
# We're calling CrazyOperations' __isub__, so we're substracting b attributes
o1
-=
o2
# This should print:
# -2 -4.2
print
(
o1
.
a
,
o1
.
b
)
# We're calling SuperCrazyOperations' __isub__, so we're substracting b attributes AND setting own (o1) a to 4
o1
-=
o3
# This should print:
# 4 -6.3
print
(
o1
.
a
,
o1
.
b
)
\ No newline at end of file
nogil_test/snippets_article/cython_cppclass.pyx
0 → 100644
View file @
613da914
cdef
extern
from
"SomeCppHeader.h"
nogil
:
cdef
cppclass
SomeDeclaredCppClass
nogil
:
int
getter
()
cdef
cppclass
SomeDefinedCppClass
nogil
:
int
value
int
getter
():
return
this
.
value
void
__init__
():
this
.
value
=
42
cdef
int
do_test
()
nogil
:
cdef
SomeDefinedCppClass
*
heap_allocated_cpp_object
=
new
SomeDefinedCppClass
()
val
=
heap_allocated_cpp_object
.
getter
()
del
heap_allocated_cpp_object
return
val
cpdef
void
test
():
print
(
do_test
())
nogil_test/snippets_article/cython_intro.pyx
0 → 100644
View file @
613da914
cdef
struct
SomeCStruct
:
# This type doesn't need the GIL
int
a
# This is a C int
double
b
# This is a C double
class
SomePythonClass
:
# This type needs the GIL
def
__init__
(
self
):
a
=
3
# This is a PyObject
cdef
class
SomeCythonClass
:
# This type needs the GIL
cdef
object
a
# This is a PyObject
cdef
int
b
# This is a C int
def
__init__
(
self
):
a
=
3
# This is a PyObject
def
test
():
# This function must hold the GIL to be able to instantiate the SomeCythonClass object
cdef
SomeCStruct
stack_allocated_struct
stack_allocated_struct
.
a
=
42
stack_allocated_struct
.
b
=
4.2
heap_allocated_cython
=
SomeCythonClass
()
heap_allocated_cython
.
b
=
stack_allocated_struct
.
a
# This is a C assignation (both are C int)
heap_allocated_cython
.
a
=
stack_allocated_struct
.
b
# The C double is coerced to a PyObject before assignation
print
(
heap_allocated_cython
.
a
,
heap_allocated_cython
.
b
)
nogil_test/snippets_article/del_statement.pyx
0 → 100644
View file @
613da914
cdef
cypclass
Test
:
int
a
cpdef
void
test
():
heap_allocated_test
=
Test
()
del
heap_allocated_test
nogil_test/snippets_article/nested_definition.pyx
0 → 100644
View file @
613da914
cdef
cypclass
A
:
int
a
cypclass
B
:
int
b
__init__
(
self
,
int
b
):
self
.
b
=
2
*
b
B
b
__init__
(
self
,
int
a
,
int
b
):
self
.
a
=
a
self
.
b
=
B
(
b
)
cpdef
void
test
():
a
=
A
(
2
,
2
)
b
=
A
.
B
(
3
)
print
(
a
.
a
,
a
.
b
.
b
,
b
.
b
)
nogil_test/snippets_article/new_statement.pyx
0 → 100644
View file @
613da914
cdef
cypclass
A
:
int
a
int
getter
(
self
):
return
self
.
a
*
6
int
__int__
(
self
):
return
self
.
getter
()
__init__
(
self
,
int
a
):
self
.
a
=
a
-
1
cdef
cypclass
MyClass
:
# Cypclass object are initialized to NULL by default.
# This is done in the C++ construction part.
A
cypobj
int
number
__init__
(
self
,
int
a
=
0
):
self
.
cypobj
=
A
(
a
)
self
.
number
=
a
__init__
(
self
,
A
obj
):
self
.
cypobj
=
obj
self
.
number
=
obj
.
a
+
1
int
get_A_value
(
self
):
if
self
.
cypobj
is
not
NULL
:
return
self
.
cypobj
.
getter
()
else
:
return
42
cdef
int
take_value
(
MyClass
o
)
nogil
:
value
=
o
.
get_A_value
()
return
value
def
print_values
():
# Not a cdef [...] nogil one because I'm tired of all the with gil to use Python's print
method1_specified
=
MyClass
(
2
)
method1_default
=
MyClass
()
method2
=
new
MyClass
()
print
(
take_value
(
method1_specified
),
take_value
(
method1_default
),
take_value
(
method2
))
nogil_test/snippets_article/null_assignment.pyx
0 → 100644
View file @
613da914
cdef
cypclass
Test
:
int
a
cpdef
void
test
():
heap_allocated_test
=
Test
()
heap_allocated_test
=
<
Test
>
NULL
nogil_test/snippets_article/okko_optional_args_wrapper.pyx
0 → 100644
View file @
613da914
cdef
cypclass
OK
:
"""
This is OK because the wrapper will pass a & b from __new__ to __init__ directly,
and will quite blindly transfer the information about c presence in __new__ to __init__
"""
__init__
(
self
,
int
a
,
double
b
=
4.2
,
int
c
=
42
):
pass
OK
__new__
(
alloc
,
int
a
,
double
b
,
int
c
=
0
):
return
alloc
()
cdef
cypclass
KO
:
"""
This will fail because the wrapper knows b (in __new__) is optional,
but doesn't know its default value, so it cannot pass it to __init__
"""
__init__
(
self
,
int
a
,
double
b
,
int
c
=
42
):
pass
KO
__new__
(
alloc
,
int
a
,
double
b
=
4.2
,
int
c
=
0
):
return
alloc
()
cdef
void
main
()
nogil
:
o1
=
OK
(
2
,
2.4
)
o2
=
KO
(
3
,
2.1
)
nogil_test/snippets_article/self_syntax.pyx
0 → 100644
View file @
613da914
cdef
cypclass
Base
:
int
getter
(
self
):
return
4
cdef
cypclass
Derived
(
Base
):
int
getter
(
self
):
return
2
def
print_42
():
o
=
Derived
()
print
(
str
(
Base
.
getter
(
o
))
+
str
(
o
.
getter
()))
return
nogil_test/snippets_article/test_class.pyx
0 → 100644
View file @
613da914
class
Class
:
def
__init__
(
self
):
self
.
a
=
3
cdef
test
(
int
a
=
0
):
return
a
def
main
():
cls
=
Class
()
s
=
test
()
nogil_test/snippets_article/tricky_multiple_inheritance.pyx
0 → 100644
View file @
613da914
cdef
cypclass
Base
:
int
base
cdef
cypclass
Derived1
(
Base
):
int
derived1
void
setBase
(
self
,
int
arg
):
self
.
base
=
arg
cdef
cypclass
Derived2
(
Base
):
int
derived2
void
setBase
(
self
,
int
arg
):
self
.
base
=
arg
cdef
cypclass
Diamond
(
Derived1
,
Derived2
):
int
diamond
cdef
void
printD1
(
Derived1
d1
)
with
gil
:
print
(
d1
.
derived1
,
d1
.
base
)
cdef
void
printD2
(
Derived2
d2
)
with
gil
:
print
(
d2
.
derived2
,
d2
.
base
)
cpdef
void
test
():
o
=
Diamond
()
o
.
derived1
=
42
o
.
derived2
=
4242
Derived1
.
setBase
(
o
,
4
)
Derived2
.
setBase
(
o
,
2
)
printD1
(
o
)
printD2
(
o
)
nogil_test/snippets_article/tricky_new_init_composition.pyx
0 → 100644
View file @
613da914
cdef
cypclass
Base
:
double
value
__init__
(
self
,
int
a
):
self
.
value
=
(
<
double
>
a
)
*
1.2
__init__
(
self
,
double
b
):
self
.
value
=
b
Base
__new__
(
alloc
,
int
a
):
obj
=
alloc
()
# After the return, the call Base.__init__(obj, a) will be issued
return
obj
cdef
cypclass
Derived
(
Base
):
Derived
__new__
(
alloc
,
double
b
):
obj
=
alloc
()
# After the return, the call Base.__init__(obj, b) will be issued
return
obj
cpdef
void
test
():
base
=
Base
(
5
)
derived
=
Derived
(
5
)
# Should print: 6 5
print
(
base
.
value
,
derived
.
value
)
nogil_test/snippets_article/typecast_operators.pyx
0 → 100644
View file @
613da914
cdef
cypclass
SomeClass
:
int
a
double
b
int
__int__
(
self
):
return
self
.
a
double
__double__
(
self
):
return
self
.
b
cdef
cypclass
SomeContainer
:
SomeClass
some_object
bint
__bool__
(
self
):
return
self
.
some_object
is
not
NULL
int
__int__
(
self
):
if
self
:
return
<
int
>
self
.
some_object
else
:
return
0
double
__double__
(
self
):
if
self
:
return
<
double
>
self
.
some_object
else
:
return
0.0
cpdef
void
test
():
contained
=
SomeClass
()
contained
.
a
=
42
contained
.
b
=
4.2
container
=
SomeContainer
()
container
.
some_object
=
contained
print
(
<
bint
>
container
,
<
int
>
container
,
<
double
>
container
)
nogil_test/snippets_article/wrapper_alloc.pyx
0 → 100644
View file @
613da914
cdef
cypclass
AheadOfTimeChunk
DEF
CHUNK_SIZE
=
2
cdef
AheadOfTimeChunk
chunk
[
CHUNK_SIZE
]
cdef
unsigned
int
chunk_started
=
0
# chunk_allocated[index] tells us the state of memory pointed by chunk[index]
cdef
int
chunk_allocated
[
CHUNK_SIZE
]
cdef
AheadOfTimeChunk
chunkclass_singleton
cdef
bint
chunkclass_singleton_allocated
=
False
cdef
cypclass
AheadOfTimeChunk
:
"""
A stupid chunk allocator:
Whenever the user requests a new object, fills each empty cell of
the chunk array with fresh memory. Give to the user the first
available chunk cell with allocated but unused memory.
This class is reaaally stupid because it still frees memory when
the user don't need the object anymore, so the __alloc__ will be filling
again and again the cells we just deallocated with fresh memory.
"""
int
cell_index
__dealloc__
(
self
):
global
chunkclass_singleton_allocated
global
chunkclass_singleton
global
chunk_allocated
idx
=
self
.
cell_index
if
idx
>=
0
:
with
gil
:
print
(
"Object destruction : mark cell"
,
idx
,
"as empty"
)
chunk_allocated
[
idx
]
=
-
1
else
:
with
gil
:
print
(
"Object destruction: out-of-chunk deallocation"
)
if
self
is
chunkclass_singleton
:
with
gil
:
print
(
" Object destruction : Mark singleton flag as unallocated"
)
chunkclass_singleton_allocated
=
False
chunkclass_singleton
=
<
AheadOfTimeChunk
>
NULL
AheadOfTimeChunk
__new__
(
alloc
,
bint
singleton
=
False
,
bint
must_fit_in_chunk
=
False
):
global
chunkclass_singleton_allocated
global
chunkclass_singleton
if
singleton
and
chunkclass_singleton_allocated
:
with
gil
:
print
(
"Object creation : Returning already allocated singleton"
)
obj
=
chunkclass_singleton
else
:
obj
=
alloc
()
if
obj
is
NULL
and
not
must_fit_in_chunk
:
with
gil
:
print
(
"Object creation : Allocating object outside of chunk"
)
obj
=
new
AheadOfTimeChunk
()
obj
.
cell_index
=
-
1
if
obj
is
not
NULL
and
singleton
:
with
gil
:
print
(
"Object creation : Singleton allocated"
)
chunkclass_singleton_allocated
=
True
chunkclass_singleton
=
obj
# We should decref here (assignment to chunkclass_singleton has incref'ed obj, we do not want that)
del
chunkclass_singleton
if
obj
is
NULL
:
# This is done to show something for the demo
with
gil
:
print
(
"Object creation : Obj is NULL !"
)
return
obj
AheadOfTimeChunk
__alloc__
():
"""
Convention for chunk_allocated is:
1 - Memory in use
0 - Memory allocated and available
-1 - No memory allocated
"""
global
chunk_started
global
chunk_allocated
global
chunk
if
not
chunk_started
:
for
i
in
range
(
CHUNK_SIZE
):
chunk_allocated
[
i
]
=
-
1
chunk_started
=
1
cdef
AheadOfTimeChunk
ptr
=
<
AheadOfTimeChunk
>
NULL
for
i
in
range
(
CHUNK_SIZE
):
if
chunk_allocated
[
i
]
==
-
1
:
with
gil
:
print
(
"cell"
,
i
,
"is empty: allocate it"
)
chunk
[
i
]
=
new
AheadOfTimeChunk
()
chunk
[
i
].
cell_index
=
i
chunk_allocated
[
i
]
=
0
if
ptr
is
NULL
and
chunk_allocated
[
i
]
==
0
:
with
gil
:
print
(
"cell"
,
i
,
"is allocated but unused: take it"
)
chunk_allocated
[
i
]
=
1
ptr
=
chunk
[
i
]
# We must decref ptr here, because it is the same reference
del
ptr
return
ptr
cpdef
void
testChunk
():
print
(
"=== Creating a basic object ==="
)
basic_obj
=
AheadOfTimeChunk
()
print
()
print
(
"=== Getting singleton (no previous references) ==="
)
singleton_obj1
=
AheadOfTimeChunk
(
True
)
print
()
print
(
"=== Getting singleton (one existing reference) ==="
)
singleton_obj2
=
AheadOfTimeChunk
(
True
)
if
singleton_obj1
is
singleton_obj2
and
singleton_obj1
is
chunkclass_singleton
:
print
(
" Both singleton pointers are correct"
)
print
()
print
(
"=== Deleting one singleton reference (one ref left) ==="
)
#del singleton_obj1
singleton_obj1
=
<
AheadOfTimeChunk
>
NULL
print
()
print
(
"=== Getting again singleton (one existing reference) ==="
)
singleton_obj3
=
AheadOfTimeChunk
(
True
)
if
singleton_obj3
is
singleton_obj2
:
print
(
" Freeing a singleton instance doesn't crash things"
)
print
()
print
(
"=== Allocating an object outside of chunk ==="
)
outside_allocation
=
AheadOfTimeChunk
()
print
()
print
(
"=== Make a fail allocation ==="
)
failed_outside_allocation
=
AheadOfTimeChunk
(
False
,
True
)
# Make some place in the chunk to see reuse
print
()
print
(
"=== Deleting one singleton reference (one ref left) ==="
)
#del singleton_obj3
singleton_obj3
=
<
AheadOfTimeChunk
>
NULL
print
()
print
(
"=== Deleting one singleton reference (zero refs left) ==="
)
#del singleton_obj2
singleton_obj2
=
<
AheadOfTimeChunk
>
NULL
print
()
print
(
"=== Allocating again a basic object (inside the chunk) ==="
)
inside_chunk
=
AheadOfTimeChunk
()
# We can afford a very specific test here
if
inside_chunk
.
cell_index
==
1
and
inside_chunk
is
chunk
[
1
]:
print
(
" Chunk cell reuse went fine"
)
print
()
print
(
"=== Allocating singleton outside of chunk (no previous references) ==="
)
outside_singleton
=
AheadOfTimeChunk
(
True
)
print
()
print
(
"Remaining objects at the end of this function are:"
)
print
(
"1 The first object we created (living at cell 0)"
)
print
(
"2 The first out-of-chunk allocated object"
)
print
(
"3 The last successfully non-singleton allocated object (living at cell 1)"
)
print
(
"4 One singleton instance (living outside of chunk)"
)
print
(
"The object destruction messages should reflect this order"
)
print
()
nogil_test/snippets_article/wrapper_init.pyx
0 → 100644
View file @
613da914
cdef
cypclass
Base
:
int
a
int
b
__init__
(
self
,
int
a
,
int
b
):
self
.
a
=
a
self
.
b
=
b
__init__
(
self
,
int
a
):
self
.
__init__
(
a
,
0
)
__init__
(
self
):
self
.
__init__
(
0
,
0
)
cdef
cypclass
Derived
(
Base
):
int
c
__init__
(
self
,
int
a
,
int
b
,
int
c
):
self
.
c
=
c
cdef
cypclass
AltDerived
(
Base
):
__init__
(
self
,
int
a
,
int
b
):
#self.a = a + b
self
.
b
=
b
-
a
cpdef
void
test
():
cdef
Base
base
=
Base
(
4
,
2
)
cdef
Derived
derived1
=
Derived
(
4
)
cdef
Derived
derived2
=
Derived
(
4
,
2
)
cdef
Derived
derived3
=
Derived
(
4
,
2
,
1
)
cdef
AltDerived
alt_derived
=
AltDerived
(
4
,
2
)
# Base should be correctly set
print
(
base
.
a
,
base
.
b
)
# derived1 has undefined c, so derived1.c will be garbage
print
(
derived1
.
a
,
derived1
.
b
,
derived1
.
c
)
# Same thing for derived2, but derived2.b is not zero
print
(
derived2
.
a
,
derived2
.
b
,
derived2
.
c
)
# derived3 has the opposite behaviour: c is set, a and b are garbage
print
(
derived3
.
a
,
derived3
.
b
,
derived3
.
c
)
# alt_derived won't output 4 2 but garbage and -2
print
(
alt_derived
.
a
,
alt_derived
.
b
)
nogil_test/snippets_article/wrapper_new.pyx
0 → 100644
View file @
613da914
cdef
cypclass
Singleton
cdef
int
allocated
=
0
cdef
Singleton
ptr
cdef
cypclass
Singleton
:
Singleton
__new__
(
alloc
):
global
allocated
global
ptr
if
not
allocated
:
ptr
=
alloc
()
allocated
=
1
return
ptr
cpdef
void
test
():
o1
=
Singleton
()
o2
=
Singleton
()
if
o1
is
o2
and
o1
is
ptr
:
print
(
"Everything is fine"
)
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