|
1 | 1 | """STRING-format edge cases for annotationlib (gh-157056).""" |
2 | 2 |
|
| 3 | +import inspect |
3 | 4 | import unittest |
| 5 | +from unittest.mock import patch |
4 | 6 |
|
5 | 7 | from annotationlib import Format, get_annotations, type_repr |
6 | 8 |
|
@@ -44,8 +46,54 @@ def mixed(a: int, b: {k: v for k, v in items}, c: lambda q: q): |
44 | 46 | }, |
45 | 47 | ) |
46 | 48 |
|
| 49 | + def test_quoted_annotation_not_requoted_on_unpack_fallback(self): |
| 50 | + def f(a: "int", b: {k: v for k, v in items}): |
| 51 | + pass |
| 52 | + |
| 53 | + self.assertEqual( |
| 54 | + get_annotations(f, format=Format.STRING), |
| 55 | + {"a": "int", "b": "{k: v for k, v in items}"}, |
| 56 | + ) |
| 57 | + |
| 58 | + def test_nested_lambda_uses_source_and_has_no_address(self): |
| 59 | + def f(x: [lambda q: q]): |
| 60 | + pass |
| 61 | + |
| 62 | + anno = get_annotations(f, format=Format.STRING) |
| 63 | + self.assertEqual(anno, {"x": "[lambda q: q]"}) |
| 64 | + self.assertNotIn("0x", anno["x"].lower()) |
| 65 | + |
| 66 | + def test_class_dictcomp_keeps_conditional_annotations(self): |
| 67 | + class C: |
| 68 | + a: int |
| 69 | + b: {k: v for k, v in items} |
| 70 | + if True: |
| 71 | + c: str |
| 72 | + |
| 73 | + self.assertEqual( |
| 74 | + get_annotations(C, format=Format.STRING), |
| 75 | + {"a": "int", "b": "{k: v for k, v in items}", "c": "str"}, |
| 76 | + ) |
| 77 | + |
| 78 | + def test_simple_string_format_does_not_read_source(self): |
| 79 | + def f(x: int) -> str: |
| 80 | + pass |
| 81 | + |
| 82 | + with patch.object(inspect, "getsource") as mocked: |
| 83 | + self.assertEqual( |
| 84 | + get_annotations(f, format=Format.STRING), |
| 85 | + {"x": "int", "return": "str"}, |
| 86 | + ) |
| 87 | + mocked.assert_not_called() |
| 88 | + |
| 89 | + def test_exec_without_source_still_raises_on_dictcomp(self): |
| 90 | + ns = {} |
| 91 | + exec("def f(x: {k: v for k, v in items}): pass", ns) |
| 92 | + with self.assertRaises(ValueError): |
| 93 | + get_annotations(ns["f"], format=Format.STRING) |
| 94 | + |
47 | 95 | def test_type_repr_lambda_and_genexpr_have_no_address(self): |
48 | | - lam = lambda q: q |
| 96 | + lam = (lambda q: q) |
49 | 97 | self.assertTrue(type_repr(lam).endswith("<lambda>")) |
50 | 98 | self.assertNotIn("0x", type_repr(lam).lower()) |
51 | 99 | gen = (w for w in ()) |
|
0 commit comments