|
104 | 104 | 'temp_umask', "reap_children", |
105 | 105 | # logging |
106 | 106 | "TestHandler", |
107 | | - # threads |
108 | | - "threading_setup", "threading_cleanup", "reap_threads", "start_threads", |
109 | 107 | # miscellaneous |
110 | 108 | "check_warnings", "check_no_resource_warning", "check_no_warnings", |
111 | 109 | "EnvironmentVarGuard", |
@@ -1541,104 +1539,6 @@ def print_warning(msg): |
1541 | 1539 | # to cleanup threads. |
1542 | 1540 | environment_altered = False |
1543 | 1541 |
|
1544 | | -# NOTE: we use thread._count() rather than threading.enumerate() (or the |
1545 | | -# moral equivalent thereof) because a threading.Thread object is still alive |
1546 | | -# until its __bootstrap() method has returned, even after it has been |
1547 | | -# unregistered from the threading module. |
1548 | | -# thread._count(), on the other hand, only gets decremented *after* the |
1549 | | -# __bootstrap() method has returned, which gives us reliable reference counts |
1550 | | -# at the end of a test run. |
1551 | | - |
1552 | | -def threading_setup(): |
1553 | | - return _thread._count(), threading._dangling.copy() |
1554 | | - |
1555 | | -def threading_cleanup(*original_values): |
1556 | | - global environment_altered |
1557 | | - |
1558 | | - _MAX_COUNT = 100 |
1559 | | - |
1560 | | - for count in range(_MAX_COUNT): |
1561 | | - values = _thread._count(), threading._dangling |
1562 | | - if values == original_values: |
1563 | | - break |
1564 | | - |
1565 | | - if not count: # Display a warning at the first iteration |
1566 | | - environment_altered = True |
1567 | | - dangling_threads = values[1] |
1568 | | - print_warning(f"threading_cleanup() failed to cleanup " |
1569 | | - f"{values[0] - original_values[0]} threads " |
1570 | | - f"(count: {values[0]}, " |
1571 | | - f"dangling: {len(dangling_threads)})") |
1572 | | - for thread in dangling_threads: |
1573 | | - print_warning(f"Dangling thread: {thread!r}") |
1574 | | - |
1575 | | - # Don't hold references to threads |
1576 | | - dangling_threads = None |
1577 | | - values = None |
1578 | | - |
1579 | | - time.sleep(0.01) |
1580 | | - gc_collect() |
1581 | | - |
1582 | | - |
1583 | | -def reap_threads(func): |
1584 | | - """Use this function when threads are being used. This will |
1585 | | - ensure that the threads are cleaned up even when the test fails. |
1586 | | - """ |
1587 | | - @functools.wraps(func) |
1588 | | - def decorator(*args): |
1589 | | - key = threading_setup() |
1590 | | - try: |
1591 | | - return func(*args) |
1592 | | - finally: |
1593 | | - threading_cleanup(*key) |
1594 | | - return decorator |
1595 | | - |
1596 | | - |
1597 | | -@contextlib.contextmanager |
1598 | | -def wait_threads_exit(timeout=60.0): |
1599 | | - """ |
1600 | | - bpo-31234: Context manager to wait until all threads created in the with |
1601 | | - statement exit. |
1602 | | -
|
1603 | | - Use _thread.count() to check if threads exited. Indirectly, wait until |
1604 | | - threads exit the internal t_bootstrap() C function of the _thread module. |
1605 | | -
|
1606 | | - threading_setup() and threading_cleanup() are designed to emit a warning |
1607 | | - if a test leaves running threads in the background. This context manager |
1608 | | - is designed to cleanup threads started by the _thread.start_new_thread() |
1609 | | - which doesn't allow to wait for thread exit, whereas thread.Thread has a |
1610 | | - join() method. |
1611 | | - """ |
1612 | | - old_count = _thread._count() |
1613 | | - try: |
1614 | | - yield |
1615 | | - finally: |
1616 | | - start_time = time.monotonic() |
1617 | | - deadline = start_time + timeout |
1618 | | - while True: |
1619 | | - count = _thread._count() |
1620 | | - if count <= old_count: |
1621 | | - break |
1622 | | - if time.monotonic() > deadline: |
1623 | | - dt = time.monotonic() - start_time |
1624 | | - msg = (f"wait_threads() failed to cleanup {count - old_count} " |
1625 | | - f"threads after {dt:.1f} seconds " |
1626 | | - f"(count: {count}, old count: {old_count})") |
1627 | | - raise AssertionError(msg) |
1628 | | - time.sleep(0.010) |
1629 | | - gc_collect() |
1630 | | - |
1631 | | - |
1632 | | -def join_thread(thread, timeout=30.0): |
1633 | | - """Join a thread. Raise an AssertionError if the thread is still alive |
1634 | | - after timeout seconds. |
1635 | | - """ |
1636 | | - thread.join(timeout) |
1637 | | - if thread.is_alive(): |
1638 | | - msg = f"failed to join the thread in {timeout:.1f} seconds" |
1639 | | - raise AssertionError(msg) |
1640 | | - |
1641 | | - |
1642 | 1542 | def reap_children(): |
1643 | 1543 | """Use this function at the end of test_main() whenever sub-processes |
1644 | 1544 | are started. This will help ensure that no extra children (zombies) |
@@ -1667,42 +1567,6 @@ def reap_children(): |
1667 | 1567 | environment_altered = True |
1668 | 1568 |
|
1669 | 1569 |
|
1670 | | -@contextlib.contextmanager |
1671 | | -def start_threads(threads, unlock=None): |
1672 | | - threads = list(threads) |
1673 | | - started = [] |
1674 | | - try: |
1675 | | - try: |
1676 | | - for t in threads: |
1677 | | - t.start() |
1678 | | - started.append(t) |
1679 | | - except: |
1680 | | - if verbose: |
1681 | | - print("Can't start %d threads, only %d threads started" % |
1682 | | - (len(threads), len(started))) |
1683 | | - raise |
1684 | | - yield |
1685 | | - finally: |
1686 | | - try: |
1687 | | - if unlock: |
1688 | | - unlock() |
1689 | | - endtime = starttime = time.monotonic() |
1690 | | - for timeout in range(1, 16): |
1691 | | - endtime += 60 |
1692 | | - for t in started: |
1693 | | - t.join(max(endtime - time.monotonic(), 0.01)) |
1694 | | - started = [t for t in started if t.is_alive()] |
1695 | | - if not started: |
1696 | | - break |
1697 | | - if verbose: |
1698 | | - print('Unable to join %d threads during a period of ' |
1699 | | - '%d minutes' % (len(started), timeout)) |
1700 | | - finally: |
1701 | | - started = [t for t in started if t.is_alive()] |
1702 | | - if started: |
1703 | | - faulthandler.dump_traceback(sys.stdout) |
1704 | | - raise AssertionError('Unable to join %d threads' % len(started)) |
1705 | | - |
1706 | 1570 | @contextlib.contextmanager |
1707 | 1571 | def swap_attr(obj, attr, new_val): |
1708 | 1572 | """Temporary swap out an attribute with a new object. |
@@ -2507,63 +2371,6 @@ def __exit__(self, *exc_info): |
2507 | 2371 | del self.unraisable |
2508 | 2372 |
|
2509 | 2373 |
|
2510 | | -class catch_threading_exception: |
2511 | | - """ |
2512 | | - Context manager catching threading.Thread exception using |
2513 | | - threading.excepthook. |
2514 | | -
|
2515 | | - Attributes set when an exception is catched: |
2516 | | -
|
2517 | | - * exc_type |
2518 | | - * exc_value |
2519 | | - * exc_traceback |
2520 | | - * thread |
2521 | | -
|
2522 | | - See threading.excepthook() documentation for these attributes. |
2523 | | -
|
2524 | | - These attributes are deleted at the context manager exit. |
2525 | | -
|
2526 | | - Usage: |
2527 | | -
|
2528 | | - with support.catch_threading_exception() as cm: |
2529 | | - # code spawning a thread which raises an exception |
2530 | | - ... |
2531 | | -
|
2532 | | - # check the thread exception, use cm attributes: |
2533 | | - # exc_type, exc_value, exc_traceback, thread |
2534 | | - ... |
2535 | | -
|
2536 | | - # exc_type, exc_value, exc_traceback, thread attributes of cm no longer |
2537 | | - # exists at this point |
2538 | | - # (to avoid reference cycles) |
2539 | | - """ |
2540 | | - |
2541 | | - def __init__(self): |
2542 | | - self.exc_type = None |
2543 | | - self.exc_value = None |
2544 | | - self.exc_traceback = None |
2545 | | - self.thread = None |
2546 | | - self._old_hook = None |
2547 | | - |
2548 | | - def _hook(self, args): |
2549 | | - self.exc_type = args.exc_type |
2550 | | - self.exc_value = args.exc_value |
2551 | | - self.exc_traceback = args.exc_traceback |
2552 | | - self.thread = args.thread |
2553 | | - |
2554 | | - def __enter__(self): |
2555 | | - self._old_hook = threading.excepthook |
2556 | | - threading.excepthook = self._hook |
2557 | | - return self |
2558 | | - |
2559 | | - def __exit__(self, *exc_info): |
2560 | | - threading.excepthook = self._old_hook |
2561 | | - del self.exc_type |
2562 | | - del self.exc_value |
2563 | | - del self.exc_traceback |
2564 | | - del self.thread |
2565 | | - |
2566 | | - |
2567 | 2374 | def wait_process(pid, *, exitcode, timeout=None): |
2568 | 2375 | """ |
2569 | 2376 | Wait until process pid completes and check that the process exit code is |
|
0 commit comments