asynkit 0.16.2__tar.gz → 0.16.3__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.3}/PKG-INFO +1 -1
- {asynkit-0.16.2 → asynkit-0.16.3}/pyproject.toml +1 -1
- {asynkit-0.16.2 → asynkit-0.16.3}/setup.py +2 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/_cext/corostart.c +205 -110
- {asynkit-0.16.2 → asynkit-0.16.3/src/asynkit.egg-info}/PKG-INFO +1 -1
- {asynkit-0.16.2 → asynkit-0.16.3}/LICENSE +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/README.md +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/setup.cfg +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/__init__.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/compat.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/coroutine.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/experimental/__init__.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/experimental/anyio.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/experimental/interrupt.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/experimental/priority.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/loop/__init__.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/loop/default.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/loop/eventloop.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/loop/extensions.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/loop/schedulingloop.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/loop/types.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/monitor.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/py.typed +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/scheduling.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit/tools.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit.egg-info/SOURCES.txt +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit.egg-info/dependency_links.txt +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit.egg-info/not-zip-safe +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit.egg-info/requires.txt +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/src/asynkit.egg-info/top_level.txt +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_anyio.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_compat_eager.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_coro.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_df.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_eager_task_factory.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_eager_task_factory_context.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_implementation_params.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_loop.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_monitor.py +0 -0
- {asynkit-0.16.2 → asynkit-0.16.3}/tests/test_tools.py +0 -0
|
@@ -82,6 +82,7 @@ def get_compile_args():
|
|
|
82
82
|
"/Zi", # Debug symbols
|
|
83
83
|
"/Od", # No optimization
|
|
84
84
|
"/DDEBUG", # Debug macro
|
|
85
|
+
"/UNDEBUG", # Undefine NDEBUG to enable asserts
|
|
85
86
|
"/W3", # Warning level 3
|
|
86
87
|
]
|
|
87
88
|
)
|
|
@@ -91,6 +92,7 @@ def get_compile_args():
|
|
|
91
92
|
"-g", # Debug symbols
|
|
92
93
|
"-O0", # No optimization
|
|
93
94
|
"-DDEBUG", # Debug macro
|
|
95
|
+
"-UNDEBUG", # Undefine NDEBUG to enable asserts
|
|
94
96
|
"-Wall", # All warnings
|
|
95
97
|
"-Wextra", # Extra warnings
|
|
96
98
|
]
|
|
@@ -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,36 @@ 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
|
+
*/
|
|
106
|
+
|
|
107
|
+
#define _IS_DONE(self) ((self)->initial_result != PYGEN_NEXT)
|
|
108
|
+
#define _IS_CONTINUED(self) ((self)->s_value == NULL && (self)->s_exc_type == NULL)
|
|
109
|
+
#define _IS_PENDING(self) (!_IS_DONE(self) && !_IS_CONTINUED(self))
|
|
110
|
+
|
|
111
|
+
#define IS_DONE(self) \
|
|
112
|
+
(assert_corostart_invariant_impl((self), __LINE__), _IS_DONE(self))
|
|
113
|
+
#define IS_CONTINUED(self) \
|
|
114
|
+
(assert_corostart_invariant_impl((self), __LINE__), _IS_CONTINUED(self))
|
|
115
|
+
#define IS_PENDING(self) \
|
|
116
|
+
(assert_corostart_invariant_impl((self), __LINE__), _IS_PENDING(self))
|
|
117
|
+
|
|
118
|
+
/* Macro to call invariant checker with current line number */
|
|
119
|
+
#define ASSERT_COROSTART_INVARIANT(self) \
|
|
120
|
+
assert_corostart_invariant_impl((self), __LINE__)
|
|
98
121
|
|
|
99
122
|
/* State invariant checker for debugging and validation */
|
|
100
|
-
static inline void
|
|
123
|
+
static inline void assert_corostart_invariant_impl(CoroStartObject *self,
|
|
124
|
+
int line_number)
|
|
101
125
|
{
|
|
102
126
|
#ifdef NDEBUG
|
|
103
127
|
/* Skip checks in release builds */
|
|
104
128
|
(void) self;
|
|
129
|
+
(void) line_number;
|
|
105
130
|
#else
|
|
106
131
|
/* Validate state model invariants:
|
|
107
132
|
* 1. Exactly one of three states: done, pending, or continued
|
|
@@ -109,24 +134,56 @@ static inline void assert_corostart_invariant(CoroStartObject *self)
|
|
|
109
134
|
* 3. pending: s_value != NULL (and s_exc_type == NULL)
|
|
110
135
|
* 4. continued: all fields are NULL
|
|
111
136
|
*/
|
|
112
|
-
int is_done =
|
|
113
|
-
int is_pending =
|
|
114
|
-
int is_continued =
|
|
137
|
+
int is_done = _IS_DONE(self);
|
|
138
|
+
int is_pending = _IS_PENDING(self);
|
|
139
|
+
int is_continued = _IS_CONTINUED(self);
|
|
115
140
|
|
|
116
141
|
/* Exactly one state should be true */
|
|
117
142
|
int state_count = is_done + is_pending + is_continued;
|
|
143
|
+
if(state_count != 1) {
|
|
144
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
145
|
+
}
|
|
118
146
|
assert(state_count == 1 && "CoroStart must be in exactly one state");
|
|
119
147
|
|
|
120
148
|
/* Additional consistency checks */
|
|
121
149
|
if(is_done) {
|
|
122
|
-
/* done() state
|
|
123
|
-
|
|
150
|
+
/* done() state can have either s_value (PYGEN_RETURN) or exception
|
|
151
|
+
* (PYGEN_ERROR) */
|
|
152
|
+
if(self->initial_result == PYGEN_RETURN) {
|
|
153
|
+
if(!(self->s_value != NULL)) {
|
|
154
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
155
|
+
}
|
|
156
|
+
assert(self->s_value != NULL && "PYGEN_RETURN should have s_value");
|
|
157
|
+
if(!(self->s_exc_type == NULL)) {
|
|
158
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
159
|
+
}
|
|
160
|
+
assert(self->s_exc_type == NULL &&
|
|
161
|
+
"PYGEN_RETURN should not have exception");
|
|
162
|
+
} else if(self->initial_result == PYGEN_ERROR) {
|
|
163
|
+
if(!(self->s_value == NULL)) {
|
|
164
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
165
|
+
}
|
|
166
|
+
assert(self->s_value == NULL && "PYGEN_ERROR should not have s_value");
|
|
167
|
+
if(!(self->s_exc_type != NULL)) {
|
|
168
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
169
|
+
}
|
|
170
|
+
assert(self->s_exc_type != NULL && "PYGEN_ERROR should have exception");
|
|
171
|
+
}
|
|
124
172
|
}
|
|
125
173
|
if(is_pending) {
|
|
126
174
|
/* pending() state - should not have exception fields */
|
|
175
|
+
if(!(self->s_exc_type == NULL)) {
|
|
176
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
177
|
+
}
|
|
127
178
|
assert(self->s_exc_type == NULL && "pending() state should not have exception");
|
|
179
|
+
if(!(self->s_exc_value == NULL)) {
|
|
180
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
181
|
+
}
|
|
128
182
|
assert(self->s_exc_value == NULL &&
|
|
129
183
|
"pending() state should not have exception value");
|
|
184
|
+
if(!(self->s_exc_traceback == NULL)) {
|
|
185
|
+
fprintf(stderr, __FILE__ ":%d: ", line_number);
|
|
186
|
+
}
|
|
130
187
|
assert(self->s_exc_traceback == NULL &&
|
|
131
188
|
"pending() state should not have exception traceback");
|
|
132
189
|
}
|
|
@@ -149,6 +206,8 @@ static int corostart_start(CoroStartObject *self)
|
|
|
149
206
|
}
|
|
150
207
|
|
|
151
208
|
/* Call coro.send(None) using PyIter_Send API */
|
|
209
|
+
/* STATE MODIFICATION: Sets initial_result and may set s_value or exception fields
|
|
210
|
+
*/
|
|
152
211
|
self->initial_result = PyIter_Send(self->wrapped_coro, Py_None, &self->s_value);
|
|
153
212
|
|
|
154
213
|
TRACE_LOG("PyIter_Send returned: %d (NEXT=1, RETURN=0, ERROR=-1)",
|
|
@@ -157,16 +216,22 @@ static int corostart_start(CoroStartObject *self)
|
|
|
157
216
|
switch(self->initial_result) {
|
|
158
217
|
case PYGEN_NEXT:
|
|
159
218
|
/* Coroutine yielded a value */
|
|
219
|
+
/* STATE: PENDING (s_value set by PyIter_Send) */
|
|
160
220
|
TRACE_LOG("PYGEN_NEXT: coroutine yielded a value");
|
|
221
|
+
assert(IS_PENDING(self));
|
|
161
222
|
break;
|
|
162
223
|
case PYGEN_RETURN:
|
|
163
224
|
/* Coroutine completed normally - create StopIteration */
|
|
225
|
+
/* STATE: DONE (s_value set by PyIter_Send) */
|
|
164
226
|
TRACE_LOG("PYGEN_RETURN: coroutine completed normally");
|
|
227
|
+
assert(IS_DONE(self));
|
|
165
228
|
break;
|
|
166
229
|
case PYGEN_ERROR:
|
|
167
230
|
/* Exception occurred - PyIter_Send already set the exception */
|
|
231
|
+
/* STATE MODIFICATION: Transition to DONE with exception */
|
|
168
232
|
TRACE_LOG("PYGEN_ERROR: exception occurred");
|
|
169
233
|
PyErr_Fetch(&self->s_exc_type, &self->s_exc_value, &self->s_exc_traceback);
|
|
234
|
+
assert(IS_DONE(self));
|
|
170
235
|
break;
|
|
171
236
|
}
|
|
172
237
|
|
|
@@ -184,7 +249,7 @@ static int corostart_start(CoroStartObject *self)
|
|
|
184
249
|
TRACE_LOG("Coroutine completed or raised exception");
|
|
185
250
|
}
|
|
186
251
|
|
|
187
|
-
|
|
252
|
+
ASSERT_COROSTART_INVARIANT(self);
|
|
188
253
|
return 0; /* Success (we handled the exception) */
|
|
189
254
|
}
|
|
190
255
|
|
|
@@ -343,31 +408,37 @@ static PySendResult corostart_wrapper_am_send_slot(PyObject *_self,
|
|
|
343
408
|
if(corostart->s_value != NULL) {
|
|
344
409
|
// We have a result
|
|
345
410
|
TRACE_LOG("Coroutine completed with result");
|
|
411
|
+
/* STATE MODIFICATION: Transfer ownership, transition to CONTINUED */
|
|
346
412
|
*result = corostart->s_value;
|
|
347
|
-
corostart->s_value =
|
|
348
|
-
|
|
413
|
+
corostart->s_value = NULL;
|
|
414
|
+
corostart->initial_result = PYGEN_NEXT; // Mark as continued
|
|
415
|
+
assert(IS_CONTINUED(corostart));
|
|
349
416
|
return PYGEN_RETURN;
|
|
350
417
|
}
|
|
351
418
|
|
|
352
419
|
// we have an exception, restore it and return error
|
|
420
|
+
/* STATE MODIFICATION: Transfer exception, transition to CONTINUED */
|
|
353
421
|
PyErr_Restore(corostart->s_exc_type,
|
|
354
422
|
corostart->s_exc_value,
|
|
355
423
|
corostart->s_exc_traceback);
|
|
356
|
-
/* Clear
|
|
357
|
-
*/
|
|
424
|
+
/* Clear (PyErr_Restore steals references) - marks as continued */
|
|
358
425
|
corostart->s_exc_type = NULL;
|
|
359
426
|
corostart->s_exc_value = NULL;
|
|
360
427
|
corostart->s_exc_traceback = NULL;
|
|
428
|
+
corostart->initial_result = PYGEN_NEXT; // Mark as continued
|
|
361
429
|
*result = NULL;
|
|
430
|
+
assert(IS_CONTINUED(corostart));
|
|
362
431
|
return PYGEN_ERROR;
|
|
363
432
|
}
|
|
364
433
|
|
|
365
434
|
// we are pending
|
|
366
435
|
TRACE_LOG("Coroutine yielded during start - returning yielded value");
|
|
367
436
|
/* Coroutine yielded a value - return it and clear state to mark as continued */
|
|
437
|
+
/* STATE MODIFICATION: Transfer value, transition to CONTINUED */
|
|
368
438
|
*result = corostart->s_value;
|
|
369
|
-
corostart->s_value =
|
|
370
|
-
|
|
439
|
+
corostart->s_value = NULL;
|
|
440
|
+
corostart->initial_result = PYGEN_NEXT; // Mark as continued
|
|
441
|
+
assert(IS_CONTINUED(corostart));
|
|
371
442
|
return PYGEN_NEXT;
|
|
372
443
|
}
|
|
373
444
|
|
|
@@ -393,6 +464,10 @@ static PySendResult corostart_wrapper_am_send_slot(PyObject *_self,
|
|
|
393
464
|
if(corostart->context != NULL) {
|
|
394
465
|
if(PyContext_Exit(corostart->context) < 0) {
|
|
395
466
|
if(send_result != PYGEN_ERROR) {
|
|
467
|
+
// Api dictates we must clear result if we return PYGEN_ERROR
|
|
468
|
+
// this error takes precedence over any send result we might
|
|
469
|
+
// have gotten. If there was an error from send, it will
|
|
470
|
+
// be chained automatically by Python.
|
|
396
471
|
Py_CLEAR(*result);
|
|
397
472
|
}
|
|
398
473
|
return PYGEN_ERROR;
|
|
@@ -548,7 +623,6 @@ static PyType_Spec corostart_spec = {
|
|
|
548
623
|
static PyObject *corostart_done(PyObject *_self)
|
|
549
624
|
{
|
|
550
625
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
551
|
-
assert_corostart_invariant(self);
|
|
552
626
|
// Return True if we have completed (indicated by having an exception)
|
|
553
627
|
// Either StopIteration (normal completion) or any other exception (error)
|
|
554
628
|
if(IS_DONE(self)) {
|
|
@@ -562,7 +636,6 @@ static PyObject *corostart_done(PyObject *_self)
|
|
|
562
636
|
static PyObject *corostart_continued(PyObject *_self, PyObject *Py_UNUSED(args))
|
|
563
637
|
{
|
|
564
638
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
565
|
-
assert_corostart_invariant(self);
|
|
566
639
|
// Return True if the coroutine has been continued (awaited) after initial start
|
|
567
640
|
// In C implementation, continued means all start result fields are NULL
|
|
568
641
|
if(IS_CONTINUED(self)) {
|
|
@@ -574,7 +647,6 @@ static PyObject *corostart_continued(PyObject *_self, PyObject *Py_UNUSED(args))
|
|
|
574
647
|
static PyObject *corostart_pending(PyObject *_self, PyObject *Py_UNUSED(args))
|
|
575
648
|
{
|
|
576
649
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
577
|
-
assert_corostart_invariant(self);
|
|
578
650
|
// Return True if the coroutine is pending, waiting for async operation
|
|
579
651
|
// This means we have a yielded value (s_value != NULL)
|
|
580
652
|
if(IS_PENDING(self)) {
|
|
@@ -639,34 +711,32 @@ static PyObject *corostart_exception(PyObject *_self)
|
|
|
639
711
|
Py_RETURN_NONE;
|
|
640
712
|
}
|
|
641
713
|
|
|
642
|
-
|
|
643
714
|
// Return the exception (not re-raise it like result() does)
|
|
644
715
|
if(self->s_exc_value && PyObject_IsInstance(self->s_exc_value, self->s_exc_type)) {
|
|
645
716
|
// s_exc_value is an actual exception instance
|
|
646
|
-
|
|
647
|
-
return self->s_exc_value;
|
|
717
|
+
return Py_NewRef(self->s_exc_value);
|
|
648
718
|
} else {
|
|
649
719
|
// s_exc_value is the raw value, need to construct exception
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
? self->s_exc_value
|
|
654
|
-
: Py_None);
|
|
655
|
-
return exc_instance;
|
|
720
|
+
// if it is null, it is omitted by the following call
|
|
721
|
+
// any failure is propagated to the caller.
|
|
722
|
+
return PyObject_CallFunctionObjArgs(self->s_exc_type, self->s_exc_value, NULL);
|
|
656
723
|
}
|
|
657
724
|
}
|
|
658
725
|
|
|
726
|
+
|
|
727
|
+
// Throw an exception into the coroutine
|
|
728
|
+
// set the state accordingly. any error except validation will set the state to done
|
|
729
|
+
// with the exception.
|
|
659
730
|
static PyObject *corostart__throw(PyObject *_self, PyObject *exc)
|
|
660
731
|
{
|
|
661
732
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
662
|
-
|
|
733
|
+
ASSERT_COROSTART_INVARIANT(self);
|
|
663
734
|
|
|
664
735
|
// Convert exception type to instance if needed
|
|
665
736
|
PyObject *value;
|
|
666
737
|
if(PyExceptionInstance_Check(exc)) {
|
|
667
738
|
// exc is already an exception instance
|
|
668
|
-
value = exc;
|
|
669
|
-
Py_INCREF(value);
|
|
739
|
+
value = Py_NewRef(exc);
|
|
670
740
|
} else if(PyExceptionClass_Check(exc)) {
|
|
671
741
|
// exc is an exception type, instantiate it
|
|
672
742
|
value = PyObject_CallFunction(exc, NULL);
|
|
@@ -678,9 +748,8 @@ static PyObject *corostart__throw(PyObject *_self, PyObject *exc)
|
|
|
678
748
|
return NULL;
|
|
679
749
|
}
|
|
680
750
|
|
|
681
|
-
PyObject *result;
|
|
682
|
-
|
|
683
751
|
// Call throw() with context support
|
|
752
|
+
PyObject *result;
|
|
684
753
|
if(self->context != NULL) {
|
|
685
754
|
/* Enter context */
|
|
686
755
|
if(PyContext_Enter(self->context) < 0) {
|
|
@@ -699,86 +768,89 @@ static PyObject *corostart__throw(PyObject *_self, PyObject *exc)
|
|
|
699
768
|
} else {
|
|
700
769
|
result = PyObject_CallMethod(self->wrapped_coro, "throw", "O", value);
|
|
701
770
|
}
|
|
771
|
+
Py_DECREF(value); // done with this
|
|
772
|
+
|
|
773
|
+
/* STATE MODIFICATION: Clear all state fields before setting new state */
|
|
774
|
+
Py_CLEAR(self->s_value);
|
|
775
|
+
Py_CLEAR(self->s_exc_type);
|
|
776
|
+
Py_CLEAR(self->s_exc_value);
|
|
777
|
+
Py_CLEAR(self->s_exc_traceback);
|
|
702
778
|
|
|
703
779
|
if(result != NULL) {
|
|
780
|
+
/* STATE MODIFICATION: Transition to PENDING with new yielded value */
|
|
704
781
|
// 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
782
|
self->s_value = result; // Store new yielded value (becomes pending state)
|
|
710
|
-
self->initial_result =
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
Py_DECREF(value);
|
|
783
|
+
self->initial_result = PYGEN_NEXT;
|
|
784
|
+
assert(IS_PENDING(self));
|
|
714
785
|
Py_RETURN_NONE;
|
|
715
786
|
}
|
|
716
787
|
|
|
717
788
|
// Check if we got StopIteration (normal completion)
|
|
718
789
|
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
|
|
790
|
+
/* STATE MODIFICATION: Transition to DONE with return value */
|
|
791
|
+
self->s_value = extract_stopiteration_value();
|
|
792
|
+
if(self->s_value == NULL) {
|
|
793
|
+
/* STATE MODIFICATION: extract failed, transition to DONE with exception */
|
|
794
|
+
// something wrong with extract_stopiteration_value, store it as regular
|
|
795
|
+
// exception
|
|
796
|
+
PyErr_Fetch(&self->s_exc_type, &self->s_exc_value, &self->s_exc_traceback);
|
|
797
|
+
self->initial_result = PYGEN_ERROR;
|
|
740
798
|
} 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
|
-
}
|
|
799
|
+
self->initial_result = PYGEN_RETURN;
|
|
747
800
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
//
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
Py_DECREF(value);
|
|
755
|
-
Py_RETURN_NONE;
|
|
801
|
+
} else {
|
|
802
|
+
/* STATE MODIFICATION: Transition to DONE with exception */
|
|
803
|
+
// any other exception
|
|
804
|
+
PyErr_Fetch(&self->s_exc_type, &self->s_exc_value, &self->s_exc_traceback);
|
|
805
|
+
self->initial_result = PYGEN_ERROR;
|
|
756
806
|
}
|
|
757
807
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
808
|
+
assert(IS_DONE(self));
|
|
809
|
+
Py_RETURN_NONE;
|
|
810
|
+
}
|
|
761
811
|
|
|
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
812
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
813
|
+
/* extract the value of a stopiteration. Assumes that we have
|
|
814
|
+
* a StopIteration error state
|
|
815
|
+
*/
|
|
816
|
+
static PyObject *extract_stopiteration_value(void)
|
|
817
|
+
{
|
|
818
|
+
PyObject *exc_type, *exc_value, *exc_traceback;
|
|
772
819
|
|
|
773
|
-
|
|
774
|
-
|
|
820
|
+
PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
|
|
821
|
+
assert(exc_type != NULL);
|
|
822
|
+
assert(PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration));
|
|
823
|
+
|
|
824
|
+
if(exc_value != NULL && PyObject_IsInstance(exc_value, exc_type)) {
|
|
825
|
+
// exc_value is an actual StopIteration instance - extract .value
|
|
826
|
+
PyObject *return_value = PyObject_GetAttrString(exc_value, "value");
|
|
827
|
+
if(return_value == NULL) {
|
|
828
|
+
// handle the error case. re-set the original error
|
|
829
|
+
PyErr_Restore(exc_type, exc_value, exc_traceback);
|
|
830
|
+
} else {
|
|
831
|
+
// clear the original error
|
|
832
|
+
Py_DECREF(exc_type);
|
|
833
|
+
Py_DECREF(exc_value);
|
|
834
|
+
Py_XDECREF(exc_traceback);
|
|
835
|
+
}
|
|
836
|
+
return return_value;
|
|
837
|
+
} else {
|
|
838
|
+
// exc_value is the raw value (internal C python optimization)
|
|
839
|
+
// or NULL if StopIteration was raised with no value
|
|
840
|
+
Py_DECREF(exc_type);
|
|
841
|
+
Py_XDECREF(exc_traceback);
|
|
842
|
+
if(exc_value == NULL) {
|
|
843
|
+
// No return value - return None
|
|
844
|
+
Py_RETURN_NONE;
|
|
845
|
+
}
|
|
846
|
+
return exc_value; // Transfer ownership from PyErr_Fetch
|
|
847
|
+
}
|
|
775
848
|
}
|
|
776
849
|
|
|
777
|
-
|
|
778
850
|
static PyObject *corostart_close(PyObject *_self)
|
|
779
851
|
{
|
|
780
852
|
CoroStartObject *self = (CoroStartObject *) _self;
|
|
781
|
-
|
|
853
|
+
ASSERT_COROSTART_INVARIANT(self);
|
|
782
854
|
|
|
783
855
|
PyObject *result;
|
|
784
856
|
|
|
@@ -802,14 +874,16 @@ static PyObject *corostart_close(PyObject *_self)
|
|
|
802
874
|
result = PyObject_CallMethod(self->wrapped_coro, "close", NULL);
|
|
803
875
|
}
|
|
804
876
|
|
|
877
|
+
/* STATE MODIFICATION: Transition to CONTINUED state */
|
|
805
878
|
// Transition to continued() state so that subsequent await attempts
|
|
806
879
|
// trigger the "cannot reuse already awaited coroutine" error in __await__
|
|
807
880
|
Py_CLEAR(self->s_value);
|
|
808
881
|
Py_CLEAR(self->s_exc_type);
|
|
809
882
|
Py_CLEAR(self->s_exc_value);
|
|
810
883
|
Py_CLEAR(self->s_exc_traceback);
|
|
884
|
+
self->initial_result = PYGEN_NEXT; // Mark as not-done to enter CONTINUED state
|
|
811
885
|
|
|
812
|
-
|
|
886
|
+
assert(IS_CONTINUED(self));
|
|
813
887
|
return result;
|
|
814
888
|
}
|
|
815
889
|
|
|
@@ -847,7 +921,7 @@ static PyObject *corostart_new(PyTypeObject *type, PyObject *args, PyObject *kwa
|
|
|
847
921
|
Py_XINCREF(cs->context);
|
|
848
922
|
}
|
|
849
923
|
|
|
850
|
-
/*
|
|
924
|
+
/* STATE INITIALIZATION: All state fields start as NULL */
|
|
851
925
|
cs->s_value = NULL;
|
|
852
926
|
cs->s_exc_type = NULL;
|
|
853
927
|
cs->s_exc_value = NULL;
|
|
@@ -860,7 +934,7 @@ static PyObject *corostart_new(PyTypeObject *type, PyObject *args, PyObject *kwa
|
|
|
860
934
|
return NULL;
|
|
861
935
|
}
|
|
862
936
|
|
|
863
|
-
|
|
937
|
+
ASSERT_COROSTART_INVARIANT(cs);
|
|
864
938
|
return (PyObject *) cs;
|
|
865
939
|
}
|
|
866
940
|
|
|
@@ -874,20 +948,41 @@ static PyObject *cext_get_build_info(PyObject *_self)
|
|
|
874
948
|
return NULL;
|
|
875
949
|
}
|
|
876
950
|
|
|
951
|
+
PyObject *temp;
|
|
952
|
+
int res;
|
|
953
|
+
|
|
877
954
|
#ifdef DEBUG
|
|
878
|
-
|
|
879
|
-
|
|
955
|
+
const char *build_type = "debug";
|
|
956
|
+
PyObject *debug_enabled = Py_True;
|
|
880
957
|
#else
|
|
881
|
-
|
|
882
|
-
|
|
958
|
+
const char *build_type = "optimized";
|
|
959
|
+
PyObject *debug_enabled = Py_False;
|
|
883
960
|
#endif
|
|
884
|
-
|
|
885
961
|
#ifdef NDEBUG
|
|
886
|
-
|
|
962
|
+
PyObject *ndebug_enabled = Py_True;
|
|
887
963
|
#else
|
|
888
|
-
|
|
964
|
+
PyObject *ndebug_enabled = Py_False;
|
|
889
965
|
#endif
|
|
890
966
|
|
|
967
|
+
temp = PyUnicode_FromString(build_type);
|
|
968
|
+
if(temp == NULL) {
|
|
969
|
+
Py_DECREF(info);
|
|
970
|
+
return NULL;
|
|
971
|
+
}
|
|
972
|
+
res = PyDict_SetItemString(info, "build_type", temp);
|
|
973
|
+
Py_DECREF(temp);
|
|
974
|
+
if(res < 0) {
|
|
975
|
+
Py_DECREF(info);
|
|
976
|
+
return NULL;
|
|
977
|
+
}
|
|
978
|
+
if(PyDict_SetItemString(info, "debug_enabled", debug_enabled) < 0) {
|
|
979
|
+
Py_DECREF(info);
|
|
980
|
+
return NULL;
|
|
981
|
+
}
|
|
982
|
+
if(PyDict_SetItemString(info, "ndebug_enabled", ndebug_enabled) < 0) {
|
|
983
|
+
Py_DECREF(info);
|
|
984
|
+
return NULL;
|
|
985
|
+
}
|
|
891
986
|
return info;
|
|
892
987
|
}
|
|
893
988
|
|
|
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
|
|
File without changes
|