|  | 
| 1 | 1 | import concurrent.futures | 
| 2 | 2 | import unittest | 
|  | 3 | +import inspect | 
| 3 | 4 | from threading import Thread, Barrier | 
| 4 | 5 | from unittest import TestCase | 
| 5 | 6 | 
 | 
|  | 
| 8 | 9 | threading_helper.requires_working_threading(module=True) | 
| 9 | 10 | 
 | 
| 10 | 11 | 
 | 
| 11 |  | -def get_func_annotate(f, b): | 
|  | 12 | +def get_func_annotation(f, b): | 
| 12 | 13 |     b.wait() | 
| 13 |  | -    return f.__annotation__ | 
|  | 14 | +    return inspect.get_annotations(f) | 
|  | 15 | + | 
|  | 16 | + | 
|  | 17 | +def get_func_annotation_dunder(f, b): | 
|  | 18 | +    b.wait() | 
|  | 19 | +    return f.__annotations__ | 
|  | 20 | + | 
|  | 21 | + | 
|  | 22 | +def set_func_annotation(f, b): | 
|  | 23 | +    b.wait() | 
|  | 24 | +    f.__annotations__ = {'x': int, 'y': int, 'return': int} | 
|  | 25 | +    return f.__annotations__ | 
| 14 | 26 | 
 | 
| 15 | 27 | 
 | 
| 16 | 28 | @unittest.skipUnless(Py_GIL_DISABLED, "Enable only in FT build") | 
| 17 | 29 | class TestFTFuncAnnotations(TestCase): | 
|  | 30 | +    NUM_THREADS = 8 | 
|  | 31 | + | 
| 18 | 32 |     def test_concurrent_read(self): | 
| 19 | 33 |         def f(x: int) -> int: | 
| 20 | 34 |             return x + 1 | 
| 21 | 35 | 
 | 
| 22 |  | -        num_threads = 8 | 
| 23 |  | -        b = Barrier(num_threads) | 
| 24 |  | -        threads = [] | 
|  | 36 | +        for _ in range(100): | 
|  | 37 | +            with concurrent.futures.ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: | 
|  | 38 | +                b = Barrier(self.NUM_THREADS) | 
|  | 39 | +                futures = {executor.submit(get_func_annotation, f, b): i for i in range(self.NUM_THREADS)} | 
|  | 40 | +                for fut in concurrent.futures.as_completed(futures): | 
|  | 41 | +                    annotate = fut.result() | 
|  | 42 | +                    self.assertIsNotNone(annotate) | 
|  | 43 | +                    self.assertEqual(annotate, {'x': int, 'return': int}) | 
|  | 44 | + | 
|  | 45 | +            with concurrent.futures.ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: | 
|  | 46 | +                b = Barrier(self.NUM_THREADS) | 
|  | 47 | +                futures = {executor.submit(get_func_annotation_dunder, f, b): i for i in range(self.NUM_THREADS)} | 
|  | 48 | +                for fut in concurrent.futures.as_completed(futures): | 
|  | 49 | +                    annotate = fut.result() | 
|  | 50 | +                    self.assertIsNotNone(annotate) | 
|  | 51 | +                    self.assertEqual(annotate, {'x': int, 'return': int}) | 
|  | 52 | + | 
|  | 53 | +    def test_concurrent_write(self): | 
|  | 54 | +        def bar(x: int, y: float) -> float: | 
|  | 55 | +            return y ** x | 
|  | 56 | + | 
|  | 57 | +        for _ in range(100): | 
|  | 58 | +            with concurrent.futures.ThreadPoolExecutor(max_workers=self.NUM_THREADS) as executor: | 
|  | 59 | +                b = Barrier(self.NUM_THREADS) | 
|  | 60 | +                futures = {executor.submit(set_func_annotation, bar, b): i for i in range(self.NUM_THREADS)} | 
|  | 61 | +                for fut in concurrent.futures.as_completed(futures): | 
|  | 62 | +                    annotate = fut.result() | 
|  | 63 | +                    self.assertIsNotNone(annotate) | 
|  | 64 | +                    self.assertEqual(annotate, {'x': int, 'y': int, 'return': int}) | 
| 25 | 65 | 
 | 
| 26 |  | -        with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: | 
| 27 |  | -            futures = {executor.submit(get_func_annotate, f, b): i for i in range(num_threads)} | 
| 28 |  | -            for fut in concurrent.futures.as_completed(futures): | 
| 29 |  | -                annotate = fut.result() | 
| 30 |  | -                self.assertIsNotNone(annotate) | 
| 31 |  | -                self.assertEqual(annotate, {'x': int, 'return': int}) | 
|  | 66 | +            # func_get_annotations returns in-place dict, so bar.__annotations__ should be modified as well | 
|  | 67 | +            self.assertEqual(bar.__annotations__, {'x': int, 'y': int, 'return': int}) | 
0 commit comments