| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent cc1bdf9 commit 6e3809c
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -645,6 +645,10 @@ loops that truncate the stream. | |||
| 645 | 645 | used anywhere else; otherwise, the *iterable* could get advanced without | |
| 646 | 646 | the tee objects being informed. | |
| 647 | 647 | ||
| 648 | + ``tee`` iterators are not threadsafe. A :exc:`RuntimeError` may be | ||
| 649 | + raised when using simultaneously iterators returned by the same :func:`tee` | ||
| 650 | + call, even if the original *iterable* is threadsafe. | ||
| 651 | + | ||
| 648 | 652 | This itertool may require significant auxiliary storage (depending on how | |
| 649 | 653 | much temporary data needs to be stored). In general, if one iterator uses | |
| 650 | 654 | most or all of the data before another iterator starts, it is faster to use | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -11,6 +11,7 @@ | |||
| 11 | 11 | from functools import reduce | |
| 12 | 12 | import sys | |
| 13 | 13 | import struct | |
| 14 | + import threading | ||
| 14 | 15 | maxsize = support.MAX_Py_ssize_t | |
| 15 | 16 | minsize = -maxsize-1 | |
| 16 | 17 | ||
@@ -1494,6 +1495,42 @@ def test_tee_del_backward(self): | |||
| 1494 | 1495 | del forward, backward | |
| 1495 | 1496 | raise | |
| 1496 | 1497 | ||
| 1498 | + def test_tee_reenter(self): | ||
| 1499 | + class I: | ||
| 1500 | + first = True | ||
| 1501 | + def __iter__(self): | ||
| 1502 | + return self | ||
| 1503 | + def __next__(self): | ||
| 1504 | + first = self.first | ||
| 1505 | + self.first = False | ||
| 1506 | + if first: | ||
| 1507 | + return next(b) | ||
| 1508 | + | ||
| 1509 | + a, b = tee(I()) | ||
| 1510 | + with self.assertRaisesRegex(RuntimeError, "tee"): | ||
| 1511 | + next(a) | ||
| 1512 | + | ||
| 1513 | + def test_tee_concurrent(self): | ||
| 1514 | + start = threading.Event() | ||
| 1515 | + finish = threading.Event() | ||
| 1516 | + class I: | ||
| 1517 | + def __iter__(self): | ||
| 1518 | + return self | ||
| 1519 | + def __next__(self): | ||
| 1520 | + start.set() | ||
| 1521 | + finish.wait() | ||
| 1522 | + | ||
| 1523 | + a, b = tee(I()) | ||
| 1524 | + thread = threading.Thread(target=next, args=[a]) | ||
| 1525 | + thread.start() | ||
| 1526 | + try: | ||
| 1527 | + start.wait() | ||
| 1528 | + with self.assertRaisesRegex(RuntimeError, "tee"): | ||
| 1529 | + next(b) | ||
| 1530 | + finally: | ||
| 1531 | + finish.set() | ||
| 1532 | + thread.join() | ||
| 1533 | + | ||
| 1497 | 1534 | def test_StopIteration(self): | |
| 1498 | 1535 | self.assertRaises(StopIteration, next, zip()) | |
| 1499 | 1536 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Fixed a crash in the :func:`tee` iterator when re-enter it. RuntimeError is | ||
| 2 | + now raised in this case. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -443,6 +443,7 @@ typedef struct { | |||
| 443 | 443 | PyObject_HEAD | |
| 444 | 444 | PyObject *it; | |
| 445 | 445 | int numread; /* 0 <= numread <= LINKCELLS */ | |
| 446 | + int running; | ||
| 446 | 447 | PyObject *nextlink; | |
| 447 | 448 | PyObject *(values[LINKCELLS]); | |
| 448 | 449 | } teedataobject; | |
@@ -465,6 +466,7 @@ teedataobject_newinternal(PyObject *it) | |||
| 465 | 466 | if (tdo == NULL) | |
| 466 | 467 | return NULL; | |
| 467 | 468 | ||
| 469 | + tdo->running = 0; | ||
| 468 | 470 | tdo->numread = 0; | |
| 469 | 471 | tdo->nextlink = NULL; | |
| 470 | 472 | Py_INCREF(it); | |
@@ -493,7 +495,14 @@ teedataobject_getitem(teedataobject *tdo, int i) | |||
| 493 | 495 | else { | |
| 494 | 496 | /* this is the lead iterator, so fetch more data */ | |
| 495 | 497 | assert(i == tdo->numread); | |
| 498 | + if (tdo->running) { | ||
| 499 | + PyErr_SetString(PyExc_RuntimeError, | ||
| 500 | + "cannot re-enter the tee iterator"); | ||
| 501 | + return NULL; | ||
| 502 | + } | ||
| 503 | + tdo->running = 1; | ||
| 496 | 504 | value = PyIter_Next(tdo->it); | |
| 505 | + tdo->running = 0; | ||
| 497 | 506 | if (value == NULL) | |
| 498 | 507 | return NULL; | |
| 499 | 508 | tdo->numread++; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments