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
Kirill Smelkov
cython
Commits
aeeef39d
Commit
aeeef39d
authored
7 years ago
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement line tracing for generators and coroutines.
Closes #1949.
parent
9c1ef852
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
75 additions
and
13 deletions
+75
-13
CHANGES.rst
CHANGES.rst
+3
-0
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+9
-2
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/ParseTreeTransforms.py
+2
-0
Cython/Utility/AsyncGen.c
Cython/Utility/AsyncGen.c
+2
-2
Cython/Utility/Coroutine.c
Cython/Utility/Coroutine.c
+15
-9
tests/run/line_profile_test.srctree
tests/run/line_profile_test.srctree
+44
-0
No files found.
CHANGES.rst
View file @
aeeef39d
...
...
@@ -34,6 +34,9 @@ Features added
Bugs fixed
----------
* Line tracing did not include generators and coroutines.
(Github issue #1949)
* C++ declarations for ``unordered_map`` were corrected.
Patch by Michael Schatzow. (Github issue #1484)
...
...
This diff is collapsed.
Click to expand it.
Cython/Compiler/Nodes.py
View file @
aeeef39d
...
...
@@ -4032,9 +4032,10 @@ class GeneratorDefNode(DefNode):
code
.
putln
(
'{'
)
code
.
putln
(
'__pyx_CoroutineObject *gen = __Pyx_%s_New('
'(__pyx_coroutine_body_t) %s, (PyObject *) %s, %s, %s, %s); %s'
%
(
'(__pyx_coroutine_body_t) %s,
%s,
(PyObject *) %s, %s, %s, %s); %s'
%
(
self
.
gen_type_name
,
body_cname
,
Naming
.
cur_scope_cname
,
name
,
qualname
,
module_name
,
body_cname
,
self
.
code_object
.
calculate_result_code
(
code
)
if
self
.
code_object
else
'NULL'
,
Naming
.
cur_scope_cname
,
name
,
qualname
,
module_name
,
code
.
error_goto_if_null
(
'gen'
,
self
.
pos
)))
code
.
put_decref
(
Naming
.
cur_scope_cname
,
py_object_type
)
if
self
.
requires_classobj
:
...
...
@@ -4129,6 +4130,9 @@ class GeneratorBodyDefNode(DefNode):
linetrace
=
code
.
globalstate
.
directives
[
'linetrace'
]
if
profile
or
linetrace
:
tempvardecl_code
.
put_trace_declarations
()
code
.
funcstate
.
can_trace
=
True
code_object
=
self
.
code_object
.
calculate_result_code
(
code
)
if
self
.
code_object
else
None
code
.
put_trace_frame_init
(
code_object
)
# ----- Resume switch point.
code
.
funcstate
.
init_closure_temps
(
lenv
.
scope_class
.
type
.
scope
)
...
...
@@ -4167,6 +4171,9 @@ class GeneratorBodyDefNode(DefNode):
# FIXME: this silences a potential "unused" warning => try to avoid unused closures in more cases
code
.
putln
(
"CYTHON_MAYBE_UNUSED_VAR(%s);"
%
Naming
.
cur_scope_cname
)
if
profile
or
linetrace
:
code
.
funcstate
.
can_trace
=
False
code
.
mark_pos
(
self
.
pos
)
code
.
putln
(
""
)
code
.
putln
(
"/* function exit code */"
)
...
...
This diff is collapsed.
Click to expand it.
Cython/Compiler/ParseTreeTransforms.py
View file @
aeeef39d
...
...
@@ -1940,6 +1940,8 @@ if VALUE is not None:
binding
=
self
.
current_directives
.
get
(
'binding'
)
rhs
=
ExprNodes
.
PyCFunctionNode
.
from_defnode
(
node
,
binding
)
node
.
code_object
=
rhs
.
code_object
if
node
.
is_generator
:
node
.
gbody
.
code_object
=
node
.
code_object
if
env
.
is_py_class_scope
:
rhs
.
binding
=
True
...
...
This diff is collapsed.
Click to expand it.
Cython/Utility/AsyncGen.c
View file @
aeeef39d
...
...
@@ -34,7 +34,7 @@ static PyObject *__Pyx__PyAsyncGenValueWrapperNew(PyObject *val);
static
__pyx_CoroutineObject
*
__Pyx_AsyncGen_New
(
__pyx_coroutine_body_t
body
,
PyObject
*
closure
,
__pyx_coroutine_body_t
body
,
PyObject
*
c
ode
,
PyObject
*
c
losure
,
PyObject
*
name
,
PyObject
*
qualname
,
PyObject
*
module_name
)
{
__pyx_PyAsyncGenObject
*
gen
=
PyObject_GC_New
(
__pyx_PyAsyncGenObject
,
__pyx_AsyncGenType
);
if
(
unlikely
(
!
gen
))
...
...
@@ -42,7 +42,7 @@ static __pyx_CoroutineObject *__Pyx_AsyncGen_New(
gen
->
ag_finalizer
=
NULL
;
gen
->
ag_closed
=
0
;
gen
->
ag_hooks_inited
=
0
;
return
__Pyx__Coroutine_NewInit
((
__pyx_CoroutineObject
*
)
gen
,
body
,
closure
,
name
,
qualname
,
module_name
);
return
__Pyx__Coroutine_NewInit
((
__pyx_CoroutineObject
*
)
gen
,
body
,
c
ode
,
c
losure
,
name
,
qualname
,
module_name
);
}
static
int
__pyx_AsyncGen_init
(
void
);
...
...
This diff is collapsed.
Click to expand it.
Cython/Utility/Coroutine.c
View file @
aeeef39d
...
...
@@ -382,17 +382,18 @@ typedef struct {
PyObject
*
gi_name
;
PyObject
*
gi_qualname
;
PyObject
*
gi_modulename
;
PyObject
*
gi_code
;
int
resume_label
;
// using T_BOOL for property below requires char value
char
is_running
;
}
__pyx_CoroutineObject
;
static
__pyx_CoroutineObject
*
__Pyx__Coroutine_New
(
PyTypeObject
*
type
,
__pyx_coroutine_body_t
body
,
PyObject
*
closure
,
PyTypeObject
*
type
,
__pyx_coroutine_body_t
body
,
PyObject
*
c
ode
,
PyObject
*
c
losure
,
PyObject
*
name
,
PyObject
*
qualname
,
PyObject
*
module_name
);
/*proto*/
static
__pyx_CoroutineObject
*
__Pyx__Coroutine_NewInit
(
__pyx_CoroutineObject
*
gen
,
__pyx_coroutine_body_t
body
,
PyObject
*
closure
,
__pyx_CoroutineObject
*
gen
,
__pyx_coroutine_body_t
body
,
PyObject
*
c
ode
,
PyObject
*
c
losure
,
PyObject
*
name
,
PyObject
*
qualname
,
PyObject
*
module_name
);
/*proto*/
static
int
__Pyx_Coroutine_clear
(
PyObject
*
self
);
/*proto*/
...
...
@@ -429,8 +430,8 @@ static PyTypeObject *__pyx_CoroutineAwaitType = 0;
#define __Pyx_Coroutine_CheckExact(obj) (Py_TYPE(obj) == __pyx_CoroutineType)
#define __Pyx_CoroutineAwait_CheckExact(obj) (Py_TYPE(obj) == __pyx_CoroutineAwaitType)
#define __Pyx_Coroutine_New(body, closure, name, qualname, module_name) \
__Pyx__Coroutine_New(__pyx_CoroutineType, body, closure, name, qualname, module_name)
#define __Pyx_Coroutine_New(body, c
ode, c
losure, name, qualname, module_name) \
__Pyx__Coroutine_New(__pyx_CoroutineType, body, c
ode, c
losure, name, qualname, module_name)
static
int
__pyx_Coroutine_init
(
void
);
/*proto*/
static
PyObject
*
__Pyx__Coroutine_await
(
PyObject
*
coroutine
);
/*proto*/
...
...
@@ -450,8 +451,8 @@ static PyObject *__Pyx_CoroutineAwait_Throw(__pyx_CoroutineAwaitObject *self, Py
static
PyTypeObject
*
__pyx_GeneratorType
=
0
;
#define __Pyx_Generator_CheckExact(obj) (Py_TYPE(obj) == __pyx_GeneratorType)
#define __Pyx_Generator_New(body, closure, name, qualname, module_name) \
__Pyx__Coroutine_New(__pyx_GeneratorType, body, closure, name, qualname, module_name)
#define __Pyx_Generator_New(body, c
ode, c
losure, name, qualname, module_name) \
__Pyx__Coroutine_New(__pyx_GeneratorType, body, c
ode, c
losure, name, qualname, module_name)
static
PyObject
*
__Pyx_Generator_Next
(
PyObject
*
self
);
static
int
__pyx_Generator_init
(
void
);
/*proto*/
...
...
@@ -1056,6 +1057,7 @@ static int __Pyx_Coroutine_clear(PyObject *self) {
Py_CLEAR
(((
__pyx_PyAsyncGenObject
*
)
gen
)
->
ag_finalizer
);
}
#endif
Py_CLEAR
(
gen
->
gi_code
);
Py_CLEAR
(
gen
->
gi_name
);
Py_CLEAR
(
gen
->
gi_qualname
);
Py_CLEAR
(
gen
->
gi_modulename
);
...
...
@@ -1287,16 +1289,16 @@ __Pyx_Coroutine_set_qualname(__pyx_CoroutineObject *self, PyObject *value)
}
static
__pyx_CoroutineObject
*
__Pyx__Coroutine_New
(
PyTypeObject
*
type
,
__pyx_coroutine_body_t
body
,
PyObject
*
closure
,
PyTypeObject
*
type
,
__pyx_coroutine_body_t
body
,
PyObject
*
c
ode
,
PyObject
*
c
losure
,
PyObject
*
name
,
PyObject
*
qualname
,
PyObject
*
module_name
)
{
__pyx_CoroutineObject
*
gen
=
PyObject_GC_New
(
__pyx_CoroutineObject
,
type
);
if
(
unlikely
(
!
gen
))
return
NULL
;
return
__Pyx__Coroutine_NewInit
(
gen
,
body
,
closure
,
name
,
qualname
,
module_name
);
return
__Pyx__Coroutine_NewInit
(
gen
,
body
,
c
ode
,
c
losure
,
name
,
qualname
,
module_name
);
}
static
__pyx_CoroutineObject
*
__Pyx__Coroutine_NewInit
(
__pyx_CoroutineObject
*
gen
,
__pyx_coroutine_body_t
body
,
PyObject
*
closure
,
__pyx_CoroutineObject
*
gen
,
__pyx_coroutine_body_t
body
,
PyObject
*
c
ode
,
PyObject
*
c
losure
,
PyObject
*
name
,
PyObject
*
qualname
,
PyObject
*
module_name
)
{
gen
->
body
=
body
;
gen
->
closure
=
closure
;
...
...
@@ -1315,6 +1317,8 @@ static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit(
gen
->
gi_name
=
name
;
Py_XINCREF
(
module_name
);
gen
->
gi_modulename
=
module_name
;
Py_XINCREF
(
code
);
gen
->
gi_code
=
code
;
PyObject_GC_Track
(
gen
);
return
gen
;
...
...
@@ -1485,6 +1489,7 @@ static PyMemberDef __pyx_Coroutine_memberlist[] = {
{(
char
*
)
"cr_running"
,
T_BOOL
,
offsetof
(
__pyx_CoroutineObject
,
is_running
),
READONLY
,
NULL
},
{(
char
*
)
"cr_await"
,
T_OBJECT
,
offsetof
(
__pyx_CoroutineObject
,
yieldfrom
),
READONLY
,
(
char
*
)
PyDoc_STR
(
"object being awaited, or None"
)},
{(
char
*
)
"cr_code"
,
T_OBJECT
,
offsetof
(
__pyx_CoroutineObject
,
gi_code
),
READONLY
,
NULL
},
{(
char
*
)
"__module__"
,
T_OBJECT
,
offsetof
(
__pyx_CoroutineObject
,
gi_modulename
),
PY_WRITE_RESTRICTED
,
0
},
{
0
,
0
,
0
,
0
,
0
}
};
...
...
@@ -1606,6 +1611,7 @@ static PyMemberDef __pyx_Generator_memberlist[] = {
{(
char
*
)
"gi_running"
,
T_BOOL
,
offsetof
(
__pyx_CoroutineObject
,
is_running
),
READONLY
,
NULL
},
{(
char
*
)
"gi_yieldfrom"
,
T_OBJECT
,
offsetof
(
__pyx_CoroutineObject
,
yieldfrom
),
READONLY
,
(
char
*
)
PyDoc_STR
(
"object being iterated by 'yield from', or None"
)},
{(
char
*
)
"gi_code"
,
T_OBJECT
,
offsetof
(
__pyx_CoroutineObject
,
gi_code
),
READONLY
,
NULL
},
{
0
,
0
,
0
,
0
,
0
}
};
...
...
This diff is collapsed.
Click to expand it.
tests/run/line_profile_test.srctree
View file @
aeeef39d
...
...
@@ -49,6 +49,18 @@ profile = line_profiler.LineProfiler(func)
profile.runcall(func, 19)
assert_stats(profile, func.__name__)
from collatz import run_generator, cy_generator
func = cy_generator
profile = line_profiler.LineProfiler(func)
profile.runcall(run_generator, 19)
assert_stats(profile, func.__name__)
from collatz import run_coro, cy_coro
func = cy_coro
profile = line_profiler.LineProfiler(func)
profile.runcall(run_coro, 19)
assert_stats(profile, func.__name__)
from collatz import PyClass
obj = PyClass()
func = obj.py_pymethod
...
...
@@ -92,6 +104,38 @@ cpdef cp_collatz(n):
n = 3*n+1
@cython.binding(True)
def cy_generator(int n):
x = 1
for i in range(n):
yield x + 2
@cython.binding(True)
def run_generator(n):
assert len(list(cy_generator(n))) == n
@cython.binding(True)
async def cy_coro(int n):
while n > 1:
if n % 2 == 0:
n //= 2
else:
n = 3*n+1
@cython.binding(True)
def run_coro(n):
coro = cy_coro(n)
try:
coro.send(None)
except StopIteration:
assert True
else:
assert False, "Coroutine did not raise"
@cython.binding(True)
class PyClass(object):
def py_pymethod(self):
...
...
This diff is collapsed.
Click to expand it.
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