@@ -1194,6 +1194,25 @@ class RaisesAttributeError:
11941194 },
11951195 )
11961196
1197+ def test_raises_error_from_value (self ):
1198+ # test that if VALUE is the only supported format, but raises an error
1199+ # that error is propagated from get_annotations
1200+ class DemoException (Exception ): ...
1201+
1202+ def annotate (format , / ):
1203+ if format == Format .VALUE :
1204+ raise DemoException ()
1205+ else :
1206+ raise NotImplementedError (format )
1207+
1208+ def f (): ...
1209+
1210+ f .__annotate__ = annotate
1211+
1212+ for fmt in [Format .VALUE , Format .FORWARDREF , Format .STRING ]:
1213+ with self .assertRaises (DemoException ):
1214+ get_annotations (f , format = fmt )
1215+
11971216
11981217class TestCallEvaluateFunction (unittest .TestCase ):
11991218 def test_evaluation (self ):
@@ -1214,6 +1233,163 @@ def evaluate(format, exc=NotImplementedError):
12141233 )
12151234
12161235
1236+ class TestCallAnnotateFunction (unittest .TestCase ):
1237+ # Tests for user defined annotate functions.
1238+
1239+ # Format and NotImplementedError are provided as arguments so they exist in
1240+ # the fake globals namespace.
1241+ # This avoids non-matching conditions passing by being converted to stringifiers.
1242+ # See: https://github.com/python/cpython/issues/138764
1243+
1244+ def test_user_annotate_value (self ):
1245+ def annotate (format , / ):
1246+ if format == Format .VALUE :
1247+ return {"x" : str }
1248+ else :
1249+ raise NotImplementedError (format )
1250+
1251+ annotations = annotationlib .call_annotate_function (
1252+ annotate ,
1253+ Format .VALUE ,
1254+ )
1255+
1256+ self .assertEqual (annotations , {"x" : str })
1257+
1258+ def test_user_annotate_forwardref_supported (self ):
1259+ # If Format.FORWARDREF is supported prefer it over Format.VALUE
1260+ def annotate (format , / , __Format = Format , __NotImplementedError = NotImplementedError ):
1261+ if format == __Format .VALUE :
1262+ return {'x' : str }
1263+ elif format == __Format .VALUE_WITH_FAKE_GLOBALS :
1264+ return {'x' : int }
1265+ elif format == __Format .FORWARDREF :
1266+ return {'x' : float }
1267+ else :
1268+ raise __NotImplementedError (format )
1269+
1270+ annotations = annotationlib .call_annotate_function (
1271+ annotate ,
1272+ Format .FORWARDREF
1273+ )
1274+
1275+ self .assertEqual (annotations , {"x" : float })
1276+
1277+ def test_user_annotate_forwardref_fakeglobals (self ):
1278+ # If Format.FORWARDREF is not supported, use Format.VALUE_WITH_FAKE_GLOBALS
1279+ # before falling back to Format.VALUE
1280+ def annotate (format , / , __Format = Format , __NotImplementedError = NotImplementedError ):
1281+ if format == __Format .VALUE :
1282+ return {'x' : str }
1283+ elif format == __Format .VALUE_WITH_FAKE_GLOBALS :
1284+ return {'x' : int }
1285+ else :
1286+ raise __NotImplementedError (format )
1287+
1288+ annotations = annotationlib .call_annotate_function (
1289+ annotate ,
1290+ Format .FORWARDREF
1291+ )
1292+
1293+ self .assertEqual (annotations , {"x" : int })
1294+
1295+ def test_user_annotate_forwardref_value_fallback (self ):
1296+ # If Format.FORWARDREF and Format.VALUE_WITH_FAKE_GLOBALS are not supported
1297+ # use Format.VALUE
1298+ def annotate (format , / , __Format = Format , __NotImplementedError = NotImplementedError ):
1299+ if format == __Format .VALUE :
1300+ return {"x" : str }
1301+ else :
1302+ raise __NotImplementedError (format )
1303+
1304+ annotations = annotationlib .call_annotate_function (
1305+ annotate ,
1306+ Format .FORWARDREF ,
1307+ )
1308+
1309+ self .assertEqual (annotations , {"x" : str })
1310+
1311+ def test_user_annotate_string_supported (self ):
1312+ # If Format.STRING is supported prefer it over Format.VALUE
1313+ def annotate (format , / , __Format = Format , __NotImplementedError = NotImplementedError ):
1314+ if format == __Format .VALUE :
1315+ return {'x' : str }
1316+ elif format == __Format .VALUE_WITH_FAKE_GLOBALS :
1317+ return {'x' : int }
1318+ elif format == __Format .STRING :
1319+ return {'x' : "float" }
1320+ else :
1321+ raise __NotImplementedError (format )
1322+
1323+ annotations = annotationlib .call_annotate_function (
1324+ annotate ,
1325+ Format .STRING ,
1326+ )
1327+
1328+ self .assertEqual (annotations , {"x" : "float" })
1329+
1330+ def test_user_annotate_string_fakeglobals (self ):
1331+ # If Format.STRING is not supported but Format.VALUE_WITH_FAKE_GLOBALS is
1332+ # prefer that over Format.VALUE
1333+ def annotate (format , / , __Format = Format , __NotImplementedError = NotImplementedError ):
1334+ if format == __Format .VALUE :
1335+ return {'x' : str }
1336+ elif format == __Format .VALUE_WITH_FAKE_GLOBALS :
1337+ return {'x' : int }
1338+ else :
1339+ raise __NotImplementedError (format )
1340+
1341+ annotations = annotationlib .call_annotate_function (
1342+ annotate ,
1343+ Format .STRING ,
1344+ )
1345+
1346+ self .assertEqual (annotations , {"x" : "int" })
1347+
1348+ def test_user_annotate_string_value_fallback (self ):
1349+ # If Format.STRING and Format.VALUE_WITH_FAKE_GLOBALS are not
1350+ # supported fall back to Format.VALUE and convert to strings
1351+ def annotate (format , / , __Format = Format , __NotImplementedError = NotImplementedError ):
1352+ if format == __Format .VALUE :
1353+ return {"x" : str }
1354+ else :
1355+ raise __NotImplementedError (format )
1356+
1357+ annotations = annotationlib .call_annotate_function (
1358+ annotate ,
1359+ Format .STRING ,
1360+ )
1361+
1362+ self .assertEqual (annotations , {"x" : "str" })
1363+
1364+ def test_condition_not_stringified (self ):
1365+ # Make sure the first condition isn't evaluated as True by being converted
1366+ # to a _Stringifier
1367+ def annotate (format , / ):
1368+ if format == Format .FORWARDREF :
1369+ return {"x" : str }
1370+ else :
1371+ raise NotImplementedError (format )
1372+
1373+ with self .assertRaises (NotImplementedError ):
1374+ annotationlib .call_annotate_function (annotate , Format .STRING )
1375+
1376+ def test_error_from_value_raised (self ):
1377+ # Test that the error from format.VALUE is raised
1378+ # if all formats fail
1379+
1380+ class DemoException (Exception ): ...
1381+
1382+ def annotate (format , / ):
1383+ if format == Format .VALUE :
1384+ raise DemoException ()
1385+ else :
1386+ raise NotImplementedError (format )
1387+
1388+ for fmt in [Format .VALUE , Format .FORWARDREF , Format .STRING ]:
1389+ with self .assertRaises (DemoException ):
1390+ annotationlib .call_annotate_function (annotate , format = fmt )
1391+
1392+
12171393class MetaclassTests (unittest .TestCase ):
12181394 def test_annotated_meta (self ):
12191395 class Meta (type ):
0 commit comments