Skip to content

Commit f72c2c3

Browse files
committed
add ft support for func annotation read
1 parent e65a1eb commit f72c2c3

File tree

2 files changed

+43
-6
lines changed

2 files changed

+43
-6
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import concurrent.futures
2+
import unittest
3+
from threading import Thread, Barrier
4+
from unittest import TestCase
5+
6+
from test.support import threading_helper, Py_GIL_DISABLED
7+
8+
threading_helper.requires_working_threading(module=True)
9+
10+
11+
def get_func_annotate(f, b):
12+
b.wait()
13+
return f.__annotation__
14+
15+
16+
@unittest.skipUnless(Py_GIL_DISABLED, "Enable only in FT build")
17+
class TestFTFuncAnnotations(TestCase):
18+
def test_concurrent_read(self):
19+
def f(x: int) -> int:
20+
return x + 1
21+
22+
num_threads = 8
23+
b = Barrier(num_threads)
24+
threads = []
25+
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})

Objects/funcobject.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
/* Function object implementation */
33

44
#include "Python.h"
5-
#include "pycore_dict.h" // _Py_INCREF_DICT()
6-
#include "pycore_long.h" // _PyLong_GetOne()
7-
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
8-
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
9-
#include "pycore_pyerrors.h" // _PyErr_Occurred()
5+
#include "pycore_dict.h" // _Py_INCREF_DICT()
6+
#include "pycore_long.h" // _PyLong_GetOne()
7+
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
8+
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
9+
#include "pycore_pyerrors.h" // _PyErr_Occurred()
10+
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
1011

1112

1213
static const char *
@@ -863,13 +864,16 @@ static PyObject *
863864
func_get_annotations(PyObject *self, void *Py_UNUSED(ignored))
864865
{
865866
PyFunctionObject *op = _PyFunction_CAST(self);
867+
PyObject *d;
868+
Py_BEGIN_CRITICAL_SECTION(self);
866869
if (op->func_annotations == NULL &&
867870
(op->func_annotate == NULL || !PyCallable_Check(op->func_annotate))) {
868871
op->func_annotations = PyDict_New();
869872
if (op->func_annotations == NULL)
870873
return NULL;
871874
}
872-
PyObject *d = func_get_annotation_dict(op);
875+
d = func_get_annotation_dict(op);
876+
Py_END_CRITICAL_SECTION();
873877
return Py_XNewRef(d);
874878
}
875879

@@ -887,8 +891,10 @@ func_set_annotations(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
887891
"__annotations__ must be set to a dict object");
888892
return -1;
889893
}
894+
Py_BEGIN_CRITICAL_SECTION(self);
890895
Py_XSETREF(op->func_annotations, Py_XNewRef(value));
891896
Py_CLEAR(op->func_annotate);
897+
Py_END_CRITICAL_SECTION();
892898
return 0;
893899
}
894900

0 commit comments

Comments
 (0)