asynkit 0.16.2__tar.gz → 0.16.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {asynkit-0.16.2/src/asynkit.egg-info → asynkit-0.16.4}/PKG-INFO +1 -1
- {asynkit-0.16.2 → asynkit-0.16.4}/pyproject.toml +1 -1
- {asynkit-0.16.2 → asynkit-0.16.4}/setup.py +2 -2
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/_cext/corostart.c +206 -125
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/coroutine.py +1 -0
- {asynkit-0.16.2 → asynkit-0.16.4/src/asynkit.egg-info}/PKG-INFO +1 -1
- {asynkit-0.16.2 → asynkit-0.16.4}/LICENSE +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/README.md +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/setup.cfg +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/__init__.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/compat.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/experimental/__init__.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/experimental/anyio.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/experimental/interrupt.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/experimental/priority.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/loop/__init__.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/loop/default.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/loop/eventloop.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/loop/extensions.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/loop/schedulingloop.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/loop/types.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/monitor.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/py.typed +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/scheduling.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit/tools.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit.egg-info/SOURCES.txt +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit.egg-info/dependency_links.txt +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit.egg-info/not-zip-safe +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit.egg-info/requires.txt +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/src/asynkit.egg-info/top_level.txt +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_anyio.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_compat_eager.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_coro.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_df.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_eager_task_factory.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_eager_task_factory_context.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_implementation_params.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_loop.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_monitor.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.4}/tests/test_tools.py +0 -0
|
@@ -81,7 +81,7 @@ def get_compile_args():
|
|
|
81
81
|
[
|
|
82
82
|
"/Zi", # Debug symbols
|
|
83
83
|
"/Od", # No optimization
|
|
84
|
-
"/
|
|
84
|
+
"/UNDEBUG", # Undefine NDEBUG to enable asserts
|
|
85
85
|
"/W3", # Warning level 3
|
|
86
86
|
]
|
|
87
87
|
)
|
|
@@ -90,7 +90,7 @@ def get_compile_args():
|
|
|
90
90
|
[
|
|
91
91
|
"-g", # Debug symbols
|
|
92
92
|
"-O0", # No optimization
|
|
93
|
-
"-
|
|
93
|
+
"-UNDEBUG", # Undefine NDEBUG to enable asserts
|
|
94
94
|
"-Wall", # All warnings
|
|
95
95
|
"-Wextra", # Extra warnings
|
|
96
96
|
]
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* asynkit C Extension -
|
|
2
|
+
* asynkit C Extension - CoroStartBase with CoroStartWrapper
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* This module implements a C version of CoroStartBase from asynkit.
|
|
5
|
+
* The purpose of this is to optimize the execution of the coroutine protocol.
|
|
6
|
+
* In the Python code, this is done with a complicated generator function which
|
|
7
|
+
* implements the protocol in python to act as an intermediate between the event
|
|
8
|
+
* loop and the user coroutine. This function is necessarily entered twice for
|
|
9
|
+
* each yield point of the coroutine.
|
|
10
|
+
* In C it is possible to do this much more efficiently by directly implementing
|
|
11
|
+
* the PyIter_Send() protocol.
|
|
12
|
+
* This virtually eliminates the runtime overhead of driving a coroutine through
|
|
13
|
+
* the CoroStart mechanism.
|
|
9
14
|
*/
|
|
10
15
|
|
|
11
16
|
#define PY_SSIZE_T_CLEAN
|
|
@@ -48,6 +53,7 @@ static PyObject *corostart_close(PyObject *_self);
|
|
|
48
53
|
|
|
49
54
|
/* Helper function forward declarations */
|
|
50
55
|
static PyObject *invalid_state_error(void);
|
|
56
|
+
static PyObject *extract_stopiteration_value(void);
|
|
51
57
|
|
|
52
58
|
/* Module initialization function */
|
|
53
59
|
PyMODINIT_FUNC PyInit__cext(void);
|
|
@@ -78,7 +84,7 @@ typedef struct CoroStartObject {
|
|
|
78
84
|
/* Type-specific fields go here */
|
|
79
85
|
PyObject *wrapped_coro;
|
|
80
86
|
PyObject *context;
|
|
81
|
-
PySendResult initial_result; // Result from initial PyIter_Send call
|
|
87
|
+
PySendResult initial_result; // Result from initial PyIter_Send call
|
|
82
88
|
PyObject *s_value; // completed value (if not exception)
|
|
83
89
|
PyObject *s_exc_type; // exception type (if completed with exception)
|
|
84
90
|
PyObject *s_exc_value; // exception value
|
|
@@ -91,17 +97,38 @@ static int corostart_traverse(CoroStartObject *self, visitproc visit, void *arg)
|
|
|
91
97
|
static int corostart_clear(CoroStartObject *self);
|
|
92
98
|
static PyObject *corostart_await(CoroStartObject *self);
|
|
93
99
|
|
|
94
|
-
/* State checking macros for CoroStart objects
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
/* State checking macros for CoroStart objects
|
|
101
|
+
* DONE guarantees that s_value or s_exc_type is set
|
|
102
|
+
* CONTINUED means all fields are NULL (exception or value has
|
|
103
|
+
* been consumed by the PyIter_Send)
|
|
104
|
+
* PENDING is derived. it is not Done, and not yet consumed.
|
|
105
|
+
* these are the three valid states, further validation is done
|
|
106
|
+
* in the invariant checker.
|
|
107
|
+
*/
|
|
108
|
+
|
|
109
|
+
#define _IS_DONE(self) ((self)->initial_result != PYGEN_NEXT)
|
|
110
|
+
#define _IS_CONTINUED(self) ((self)->s_value == NULL && (self)->s_exc_type == NULL)
|
|
111
|
+
#define _IS_PENDING(self) (!_IS_DONE(self) && !_IS_CONTINUED(self))
|
|
112
|
+
|
|
113
|
+
#define IS_DONE(self) \
|
|
114
|
+
(assert_corostart_invariant_impl((self), __LINE__), _IS_DONE(self))
|
|
115
|
+
#define IS_CONTINUED(self) \
|
|
116
|
+
(assert_corostart_invariant_impl((self), __LINE__), _IS_CONTINUED(self))
|
|
117
|
+
#define IS_PENDING(self) \
|
|
118
|
+
(assert_corostart_invariant_impl((self), __LINE__), _IS_PENDING(self))
|
|
119
|
+
|
|
120
|
+
/* Macro to call invariant checker with current line number */
|
|
121
|
+
#define ASSERT_COROSTART_INVARIANT(self) \
|
|
122
|
+
assert_corostart_invariant_impl((self), __LINE__)
|
|
98
123
|
|
|
99
124
|
/* State invariant checker for debugging and validation */
|
|
100
|
-
static inline void
|
|
125
|
+
static inline void assert_corostart_invariant_impl(CoroStartObject *self,
|
|
126
|
+
int line_number)
|
|
101
127
|
{
|
|
102
128
|
#ifdef NDEBUG
|
|
103
129
|
/* Skip checks in release builds */
|
|
104
130
|
(void) self;
|
|
131
|
+
(void) line_number;
|
|
105
132
|
#else
|
|
106
133
|
/* Validate state model invariants:
|
|
107
134
|
* 1. Exactly one of three states: done, pending, or continued
|
|
@@ -109,26 +136,52 @@ static inline void assert_corostart_invariant(CoroStartObject *self)
|
|
|
109
136
|
* 3. pending: s_value != NULL (and s_exc_type == NULL)
|
|
110
137
|
* 4. continued: all fields are NULL
|
|
111
138
|
*/
|
|
112
|
-
int is_done =
|
|
113
|
-
int is_pending =
|
|
114
|
-
int is_continued =
|
|
139
|
+
int is_done = _IS_DONE(self);
|
|
140
|
+
int is_pending = _IS_PENDING(self);
|
|
141
|
+
int is_continued = _IS_CONTINUED(self);
|
|
115
142
|
|
|
116
143
|
/* Exactly one state should be true */
|
|
117
144
|
int state_count = is_done + is_pending + is_continued;
|
|
145
|
+
if(state_count != 1) {
|
|
146
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
147
|
+
}
|
|
118
148
|
assert(state_count == 1 && "CoroStart must be in exactly one state");
|
|
119
149
|
|
|
120
150
|
/* Additional consistency checks */
|
|
121
151
|
if(is_done) {
|
|
122
|
-
/* done() state
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
assert(self->
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
152
|
+
/* done() state can have either s_value (PYGEN_RETURN) or exception
|
|
153
|
+
* (PYGEN_ERROR) */
|
|
154
|
+
if(!(self->initial_result == PYGEN_RETURN ||
|
|
155
|
+
self->initial_result == PYGEN_ERROR)) {
|
|
156
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
157
|
+
}
|
|
158
|
+
assert(self->initial_result == PYGEN_RETURN ||
|
|
159
|
+
self->initial_result == PYGEN_ERROR);
|
|
160
|
+
// either s_value is non-NULL or s_exc_type is non-NULL (exclusive or)
|
|
161
|
+
if(!((self->s_value != NULL) ^ (self->s_exc_type != NULL))) {
|
|
162
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
163
|
+
}
|
|
164
|
+
assert((self->s_value != NULL) ^ (self->s_exc_type != NULL));
|
|
165
|
+
} else if(is_pending) {
|
|
166
|
+
/* pending() state - should only have s_value set */
|
|
167
|
+
if(!(self->initial_result == PYGEN_NEXT)) {
|
|
168
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
169
|
+
}
|
|
170
|
+
assert(self->initial_result == PYGEN_NEXT);
|
|
171
|
+
if(!(self->s_value != NULL && self->s_exc_type == NULL)) {
|
|
172
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
173
|
+
}
|
|
174
|
+
assert(self->s_value != NULL && self->s_exc_type == NULL);
|
|
175
|
+
} else {
|
|
176
|
+
/* continued() state - all fields should be NULL */
|
|
177
|
+
if(!(self->initial_result == PYGEN_NEXT)) {
|
|
178
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
179
|
+
}
|
|
180
|
+
assert(self->initial_result == PYGEN_NEXT);
|
|
181
|
+
if(!(self->s_value == NULL && self->s_exc_type == NULL)) {
|
|
182
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
183
|
+
}
|
|
184
|
+
assert(self->s_value == NULL && self->s_exc_type == NULL);
|
|
132
185
|
}
|
|
133
186
|
#endif
|
|
134
187
|
}
|
|
@@ -149,6 +202,8 @@ static int corostart_start(CoroStartObject *self)
|
|
|
149
202
|
}
|
|
150
203
|
|
|
151
204
|
/* Call coro.send(None) using PyIter_Send API */
|
|
205
|
+
/* STATE MODIFICATION: Sets initial_result and may set s_value or exception fields
|
|
206
|
+
*/
|
|
152
207
|
self->initial_result = PyIter_Send(self->wrapped_coro, Py_None, &self->s_value);
|
|
153
208
|
|
|
154
209
|
TRACE_LOG("PyIter_Send returned: %d (NEXT=1, RETURN=0, ERROR=-1)",
|
|
@@ -157,16 +212,22 @@ static int corostart_start(CoroStartObject *self)
|
|
|
157
212
|
switch(self->initial_result) {
|
|
158
213
|
case PYGEN_NEXT:
|
|
159
214
|
/* Coroutine yielded a value */
|
|
215
|
+
/* STATE: PENDING (s_value set by PyIter_Send) */
|
|
160
216
|
TRACE_LOG("PYGEN_NEXT: coroutine yielded a value");
|
|
217
|
+
assert(IS_PENDING(self));
|
|
161
218
|
break;
|
|
162
219
|
case PYGEN_RETURN:
|
|
163
220
|
/* Coroutine completed normally - create StopIteration */
|
|
221
|
+
/* STATE: DONE (s_value set by PyIter_Send) */
|
|
164
222
|
TRACE_LOG("PYGEN_RETURN: coroutine completed normally");
|
|
223
|
+
assert(IS_DONE(self));
|
|
165
224
|
break;
|
|
166
225
|
case PYGEN_ERROR:
|
|
167
226
|
/* Exception occurred - PyIter_Send already set the exception */
|
|
227
|
+
/* STATE MODIFICATION: Transition to DONE with exception */
|
|
168
228
|
TRACE_LOG("PYGEN_ERROR: exception occurred");
|
|
169
229
|
PyErr_Fetch(&self->s_exc_type, &self->s_exc_value, &self->s_exc_traceback);
|
|
230
|
+
assert(IS_DONE(self));
|
|
170
231
|
break;
|
|
171
232
|
}
|
|
172
233
|
|
|
@@ -184,7 +245,7 @@ static int corostart_start(CoroStartObject *self)
|
|
|
184
245
|
TRACE_LOG("Coroutine completed or raised exception");
|
|
185
246
|
}
|
|
186
247
|
|
|
187
|
-
|
|
248
|
+
ASSERT_COROSTART_INVARIANT(self);
|
|
188
249
|
return 0; /* Success (we handled the exception) */
|
|
189
250
|
}
|
|
190
251
|
|
|
@@ -343,31 +404,37 @@ static PySendResult corostart_wrapper_am_send_slot(PyObject *_self,
|
|
|
343
404
|
if(corostart->s_value != NULL) {
|
|
344
405
|
// We have a result
|
|
345
406
|
TRACE_LOG("Coroutine completed with result");
|
|
407
|
+
/* STATE MODIFICATION: Transfer ownership, transition to CONTINUED */
|
|
346
408
|
*result = corostart->s_value;
|
|
347
|
-
corostart->s_value =
|
|
348
|
-
|
|
409
|
+
corostart->s_value = NULL;
|
|
410
|
+
corostart->initial_result = PYGEN_NEXT; // Mark as continued
|
|
411
|
+
assert(IS_CONTINUED(corostart));
|
|
349
412
|
return PYGEN_RETURN;
|
|
350
413
|
}
|
|
351
414
|
|
|
352
415
|
// we have an exception, restore it and return error
|
|
416
|
+
/* STATE MODIFICATION: Transfer exception, transition to CONTINUED */
|
|
353
417
|
PyErr_Restore(corostart->s_exc_type,
|
|
354
418
|
corostart->s_exc_value,
|
|
355
419
|
corostart->s_exc_traceback);
|
|
356
|
-
/* Clear
|
|
357
|
-
*/
|
|
420
|
+
/* Clear (PyErr_Restore steals references) - marks as continued */
|
|
358
421
|
corostart->s_exc_type = NULL;
|
|
359
422
|
corostart->s_exc_value = NULL;
|
|
360
423
|
corostart->s_exc_traceback = NULL;
|
|
424
|
+
corostart->initial_result = PYGEN_NEXT; // Mark as continued
|
|
361
425
|
*result = NULL;
|
|
426
|
+
assert(IS_CONTINUED(corostart));
|
|
362
427
|
return PYGEN_ERROR;
|
|
363
428
|
}
|
|
364
429
|
|
|
365
430
|
// we are pending
|
|
366
431
|
TRACE_LOG("Coroutine yielded during start - returning yielded value");
|
|
367
432
|
/* Coroutine yielded a value - return it and clear state to mark as continued */
|
|
433
|
+
/* STATE MODIFICATION: Transfer value, transition to CONTINUED */
|
|
368
434
|
*result = corostart->s_value;
|
|
369
|
-
corostart->s_value =
|
|
370
|
-
|
|
435
|
+
corostart->s_value = NULL;
|
|
436
|
+
corostart->initial_result = PYGEN_NEXT; // Mark as continued
|
|
437
|
+
assert(IS_CONTINUED(corostart));
|
|
371
438
|
return PYGEN_NEXT;
|
|
372
439
|
}
|
|
373
440
|
|
|
@@ -393,6 +460,10 @@ static PySendResult corostart_wrapper_am_send_slot(PyObject *_self,
|
|
|
393
460
|
if(corostart->context != NULL) {
|
|
394
461
|
if(PyContext_Exit(corostart->context) < 0) {
|
|
395
462
|
if(send_result != PYGEN_ERROR) {
|
|
463
|
+
// Api dictates we must clear result if we return PYGEN_ERROR
|
|
464
|
+
// this error takes precedence over any send result we might
|
|
465
|
+
// have gotten. If there was an error from send, it will
|
|
466
|
+
// be chained automatically by Python.
|
|
396
467
|
Py_CLEAR(*result);
|
|
397
468
|
}
|
|
398
469
|
return PYGEN_ERROR;
|
|
@@ -548,7 +619,6 @@ static PyType_Spec corostart_spec = {
|
|
|
548
619
|
static PyObject *corostart_done(PyObject *_self)
|
|
549
620
|
{
|
|
550
621
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
551
|
-
assert_corostart_invariant(self);
|
|
552
622
|
// Return True if we have completed (indicated by having an exception)
|
|
553
623
|
// Either StopIteration (normal completion) or any other exception (error)
|
|
554
624
|
if(IS_DONE(self)) {
|
|
@@ -562,7 +632,6 @@ static PyObject *corostart_done(PyObject *_self)
|
|
|
562
632
|
static PyObject *corostart_continued(PyObject *_self, PyObject *Py_UNUSED(args))
|
|
563
633
|
{
|
|
564
634
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
565
|
-
assert_corostart_invariant(self);
|
|
566
635
|
// Return True if the coroutine has been continued (awaited) after initial start
|
|
567
636
|
// In C implementation, continued means all start result fields are NULL
|
|
568
637
|
if(IS_CONTINUED(self)) {
|
|
@@ -574,7 +643,6 @@ static PyObject *corostart_continued(PyObject *_self, PyObject *Py_UNUSED(args))
|
|
|
574
643
|
static PyObject *corostart_pending(PyObject *_self, PyObject *Py_UNUSED(args))
|
|
575
644
|
{
|
|
576
645
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
577
|
-
assert_corostart_invariant(self);
|
|
578
646
|
// Return True if the coroutine is pending, waiting for async operation
|
|
579
647
|
// This means we have a yielded value (s_value != NULL)
|
|
580
648
|
if(IS_PENDING(self)) {
|
|
@@ -639,34 +707,37 @@ static PyObject *corostart_exception(PyObject *_self)
|
|
|
639
707
|
Py_RETURN_NONE;
|
|
640
708
|
}
|
|
641
709
|
|
|
642
|
-
|
|
643
710
|
// Return the exception (not re-raise it like result() does)
|
|
644
|
-
if(self->s_exc_value
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
? self->s_exc_value
|
|
654
|
-
: Py_None);
|
|
655
|
-
return exc_instance;
|
|
711
|
+
if(self->s_exc_value) {
|
|
712
|
+
int res = PyObject_IsInstance(self->s_exc_value, self->s_exc_type);
|
|
713
|
+
if(res < 0) {
|
|
714
|
+
return NULL; // error during IsInstance check
|
|
715
|
+
}
|
|
716
|
+
if(res == 1) {
|
|
717
|
+
// s_exc_value is an actual exception instance
|
|
718
|
+
return Py_NewRef(self->s_exc_value);
|
|
719
|
+
}
|
|
656
720
|
}
|
|
721
|
+
// s_exc_value is the raw value, need to construct exception
|
|
722
|
+
// if it is null, it is omitted by the following call
|
|
723
|
+
// any failure is propagated to the caller.
|
|
724
|
+
return PyObject_CallFunctionObjArgs(self->s_exc_type, self->s_exc_value, NULL);
|
|
657
725
|
}
|
|
658
726
|
|
|
727
|
+
|
|
728
|
+
// Throw an exception into the coroutine
|
|
729
|
+
// set the state accordingly. any error except validation will set the state to done
|
|
730
|
+
// with the exception.
|
|
659
731
|
static PyObject *corostart__throw(PyObject *_self, PyObject *exc)
|
|
660
732
|
{
|
|
661
733
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
662
|
-
|
|
734
|
+
ASSERT_COROSTART_INVARIANT(self);
|
|
663
735
|
|
|
664
736
|
// Convert exception type to instance if needed
|
|
665
737
|
PyObject *value;
|
|
666
738
|
if(PyExceptionInstance_Check(exc)) {
|
|
667
739
|
// exc is already an exception instance
|
|
668
|
-
value = exc;
|
|
669
|
-
Py_INCREF(value);
|
|
740
|
+
value = Py_NewRef(exc);
|
|
670
741
|
} else if(PyExceptionClass_Check(exc)) {
|
|
671
742
|
// exc is an exception type, instantiate it
|
|
672
743
|
value = PyObject_CallFunction(exc, NULL);
|
|
@@ -678,9 +749,8 @@ static PyObject *corostart__throw(PyObject *_self, PyObject *exc)
|
|
|
678
749
|
return NULL;
|
|
679
750
|
}
|
|
680
751
|
|
|
681
|
-
PyObject *result;
|
|
682
|
-
|
|
683
752
|
// Call throw() with context support
|
|
753
|
+
PyObject *result;
|
|
684
754
|
if(self->context != NULL) {
|
|
685
755
|
/* Enter context */
|
|
686
756
|
if(PyContext_Enter(self->context) < 0) {
|
|
@@ -699,86 +769,89 @@ static PyObject *corostart__throw(PyObject *_self, PyObject *exc)
|
|
|
699
769
|
} else {
|
|
700
770
|
result = PyObject_CallMethod(self->wrapped_coro, "throw", "O", value);
|
|
701
771
|
}
|
|
772
|
+
Py_DECREF(value); // done with this
|
|
773
|
+
|
|
774
|
+
/* STATE MODIFICATION: Clear all state fields before setting new state */
|
|
775
|
+
Py_CLEAR(self->s_value);
|
|
776
|
+
Py_CLEAR(self->s_exc_type);
|
|
777
|
+
Py_CLEAR(self->s_exc_value);
|
|
778
|
+
Py_CLEAR(self->s_exc_traceback);
|
|
702
779
|
|
|
703
780
|
if(result != NULL) {
|
|
781
|
+
/* STATE MODIFICATION: Transition to PENDING with new yielded value */
|
|
704
782
|
// Coroutine yielded a value - update state to pending with the new value
|
|
705
|
-
Py_CLEAR(self->s_value);
|
|
706
|
-
Py_CLEAR(self->s_exc_type);
|
|
707
|
-
Py_CLEAR(self->s_exc_value);
|
|
708
|
-
Py_CLEAR(self->s_exc_traceback);
|
|
709
783
|
self->s_value = result; // Store new yielded value (becomes pending state)
|
|
710
|
-
self->initial_result =
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
Py_DECREF(value);
|
|
784
|
+
self->initial_result = PYGEN_NEXT;
|
|
785
|
+
assert(IS_PENDING(self));
|
|
714
786
|
Py_RETURN_NONE;
|
|
715
787
|
}
|
|
716
788
|
|
|
717
789
|
// Check if we got StopIteration (normal completion)
|
|
718
790
|
if(PyErr_ExceptionMatches(PyExc_StopIteration)) {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
// For PYGEN_RETURN, store the return value in s_value (like corostart_start
|
|
729
|
-
// does) Extract the value from StopIteration - it might be an instance or raw
|
|
730
|
-
// value
|
|
731
|
-
if(exc_value && PyObject_IsInstance(exc_value, exc_type)) {
|
|
732
|
-
// exc_value is an actual StopIteration instance - extract .value
|
|
733
|
-
PyObject *return_value = PyObject_GetAttrString(exc_value, "value");
|
|
734
|
-
if(return_value == NULL) {
|
|
735
|
-
PyErr_Clear();
|
|
736
|
-
return_value = Py_NewRef(Py_None);
|
|
737
|
-
}
|
|
738
|
-
self->s_value = return_value;
|
|
739
|
-
Py_DECREF(exc_value); // Clean up the StopIteration instance
|
|
791
|
+
/* STATE MODIFICATION: Transition to DONE with return value */
|
|
792
|
+
self->s_value = extract_stopiteration_value();
|
|
793
|
+
if(self->s_value == NULL) {
|
|
794
|
+
/* STATE MODIFICATION: extract failed, transition to DONE with exception */
|
|
795
|
+
// something wrong with extract_stopiteration_value, store it as regular
|
|
796
|
+
// exception
|
|
797
|
+
PyErr_Fetch(&self->s_exc_type, &self->s_exc_value, &self->s_exc_traceback);
|
|
798
|
+
self->initial_result = PYGEN_ERROR;
|
|
740
799
|
} else {
|
|
741
|
-
|
|
742
|
-
if(exc_value) {
|
|
743
|
-
self->s_value = exc_value; // Transfer ownership from PyErr_Fetch
|
|
744
|
-
} else {
|
|
745
|
-
self->s_value = Py_NewRef(Py_None);
|
|
746
|
-
}
|
|
800
|
+
self->initial_result = PYGEN_RETURN;
|
|
747
801
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
//
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
Py_DECREF(value);
|
|
755
|
-
Py_RETURN_NONE;
|
|
802
|
+
} else {
|
|
803
|
+
/* STATE MODIFICATION: Transition to DONE with exception */
|
|
804
|
+
// any other exception
|
|
805
|
+
PyErr_Fetch(&self->s_exc_type, &self->s_exc_value, &self->s_exc_traceback);
|
|
806
|
+
self->initial_result = PYGEN_ERROR;
|
|
756
807
|
}
|
|
757
808
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
809
|
+
assert(IS_DONE(self));
|
|
810
|
+
Py_RETURN_NONE;
|
|
811
|
+
}
|
|
761
812
|
|
|
762
|
-
Py_CLEAR(self->s_value);
|
|
763
|
-
Py_CLEAR(self->s_exc_type);
|
|
764
|
-
Py_CLEAR(self->s_exc_value);
|
|
765
|
-
Py_CLEAR(self->s_exc_traceback);
|
|
766
813
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
814
|
+
/* extract the value of a stopiteration. Assumes that we have
|
|
815
|
+
* a StopIteration error state
|
|
816
|
+
*/
|
|
817
|
+
static PyObject *extract_stopiteration_value(void)
|
|
818
|
+
{
|
|
819
|
+
PyObject *exc_type, *exc_value, *exc_traceback;
|
|
772
820
|
|
|
773
|
-
|
|
774
|
-
|
|
821
|
+
PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
|
|
822
|
+
assert(exc_type != NULL);
|
|
823
|
+
assert(PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration));
|
|
824
|
+
|
|
825
|
+
if(exc_value != NULL && PyObject_IsInstance(exc_value, exc_type)) {
|
|
826
|
+
// exc_value is an actual StopIteration instance - extract .value
|
|
827
|
+
PyObject *return_value = PyObject_GetAttrString(exc_value, "value");
|
|
828
|
+
if(return_value == NULL) {
|
|
829
|
+
// handle the error case. re-set the original error
|
|
830
|
+
PyErr_Restore(exc_type, exc_value, exc_traceback);
|
|
831
|
+
} else {
|
|
832
|
+
// clear the original error
|
|
833
|
+
Py_DECREF(exc_type);
|
|
834
|
+
Py_DECREF(exc_value);
|
|
835
|
+
Py_XDECREF(exc_traceback);
|
|
836
|
+
}
|
|
837
|
+
return return_value;
|
|
838
|
+
} else {
|
|
839
|
+
// exc_value is the raw value (internal C python optimization)
|
|
840
|
+
// or NULL if StopIteration was raised with no value
|
|
841
|
+
Py_DECREF(exc_type);
|
|
842
|
+
Py_XDECREF(exc_traceback);
|
|
843
|
+
if(exc_value == NULL) {
|
|
844
|
+
// No return value - return None
|
|
845
|
+
Py_RETURN_NONE;
|
|
846
|
+
}
|
|
847
|
+
return exc_value; // Transfer ownership from PyErr_Fetch
|
|
848
|
+
}
|
|
775
849
|
}
|
|
776
850
|
|
|
777
|
-
|
|
778
851
|
static PyObject *corostart_close(PyObject *_self)
|
|
779
852
|
{
|
|
780
853
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
781
|
-
|
|
854
|
+
ASSERT_COROSTART_INVARIANT(self);
|
|
782
855
|
|
|
783
856
|
PyObject *result;
|
|
784
857
|
|
|
@@ -802,14 +875,16 @@ static PyObject *corostart_close(PyObject *_self)
|
|
|
802
875
|
result = PyObject_CallMethod(self->wrapped_coro, "close", NULL);
|
|
803
876
|
}
|
|
804
877
|
|
|
878
|
+
/* STATE MODIFICATION: Transition to CONTINUED state */
|
|
805
879
|
// Transition to continued() state so that subsequent await attempts
|
|
806
880
|
// trigger the "cannot reuse already awaited coroutine" error in __await__
|
|
807
881
|
Py_CLEAR(self->s_value);
|
|
808
882
|
Py_CLEAR(self->s_exc_type);
|
|
809
883
|
Py_CLEAR(self->s_exc_value);
|
|
810
884
|
Py_CLEAR(self->s_exc_traceback);
|
|
885
|
+
self->initial_result = PYGEN_NEXT; // Mark as not-done to enter CONTINUED state
|
|
811
886
|
|
|
812
|
-
|
|
887
|
+
assert(IS_CONTINUED(self));
|
|
813
888
|
return result;
|
|
814
889
|
}
|
|
815
890
|
|
|
@@ -847,7 +922,7 @@ static PyObject *corostart_new(PyTypeObject *type, PyObject *args, PyObject *kwa
|
|
|
847
922
|
Py_XINCREF(cs->context);
|
|
848
923
|
}
|
|
849
924
|
|
|
850
|
-
/*
|
|
925
|
+
/* STATE INITIALIZATION: All state fields start as NULL */
|
|
851
926
|
cs->s_value = NULL;
|
|
852
927
|
cs->s_exc_type = NULL;
|
|
853
928
|
cs->s_exc_value = NULL;
|
|
@@ -860,7 +935,7 @@ static PyObject *corostart_new(PyTypeObject *type, PyObject *args, PyObject *kwa
|
|
|
860
935
|
return NULL;
|
|
861
936
|
}
|
|
862
937
|
|
|
863
|
-
|
|
938
|
+
ASSERT_COROSTART_INVARIANT(cs);
|
|
864
939
|
return (PyObject *) cs;
|
|
865
940
|
}
|
|
866
941
|
|
|
@@ -874,20 +949,26 @@ static PyObject *cext_get_build_info(PyObject *_self)
|
|
|
874
949
|
return NULL;
|
|
875
950
|
}
|
|
876
951
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
PyDict_SetItemString(info, "debug_enabled", Py_True);
|
|
880
|
-
#else
|
|
881
|
-
PyDict_SetItemString(info, "build_type", PyUnicode_FromString("optimized"));
|
|
882
|
-
PyDict_SetItemString(info, "debug_enabled", Py_False);
|
|
883
|
-
#endif
|
|
952
|
+
PyObject *temp;
|
|
953
|
+
int res;
|
|
884
954
|
|
|
885
|
-
#
|
|
886
|
-
|
|
955
|
+
#ifndef NDEBUG
|
|
956
|
+
const char *build_type = "debug";
|
|
887
957
|
#else
|
|
888
|
-
|
|
958
|
+
const char *build_type = "optimized";
|
|
889
959
|
#endif
|
|
890
960
|
|
|
961
|
+
temp = PyUnicode_FromString(build_type);
|
|
962
|
+
if(temp == NULL) {
|
|
963
|
+
Py_DECREF(info);
|
|
964
|
+
return NULL;
|
|
965
|
+
}
|
|
966
|
+
res = PyDict_SetItemString(info, "build_type", temp);
|
|
967
|
+
Py_DECREF(temp);
|
|
968
|
+
if(res < 0) {
|
|
969
|
+
Py_DECREF(info);
|
|
970
|
+
return NULL;
|
|
971
|
+
}
|
|
891
972
|
return info;
|
|
892
973
|
}
|
|
893
974
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|