|
1 | 1 | """ Tests for the internal type cache in CPython. """ |
2 | 2 | importunittest |
| 3 | +importdis |
3 | 4 | fromtestimportsupport |
4 | 5 | fromtest.supportimportimport_helper |
5 | 6 | try: |
|
8 | 9 | _clear_type_cache=None |
9 | 10 |
|
10 | 11 | # Skip this test if the _testcapi module isn't available. |
11 | | -type_get_version=import_helper.import_module('_testcapi').type_get_version |
12 | | -type_assign_version=import_helper.import_module('_testcapi').type_assign_version |
| 12 | +_testcapi=import_helper.import_module("_testcapi") |
| 13 | +type_get_version=_testcapi.type_get_version |
| 14 | +type_assign_specific_version_unsafe=_testcapi.type_assign_specific_version_unsafe |
| 15 | +type_assign_version=_testcapi.type_assign_version |
| 16 | +type_modified=_testcapi.type_modified |
13 | 17 |
|
14 | 18 |
|
15 | 19 | @support.cpython_only |
@@ -56,6 +60,144 @@ class C: |
56 | 60 | self.assertNotEqual(type_get_version(C),0) |
57 | 61 | self.assertNotEqual(type_get_version(C),c_ver) |
58 | 62 |
|
| 63 | +deftest_type_assign_specific_version(self): |
| 64 | +"""meta-test for type_assign_specific_version_unsafe""" |
| 65 | +classC: |
| 66 | +pass |
| 67 | + |
| 68 | +type_assign_version(C) |
| 69 | +orig_version=type_get_version(C) |
| 70 | +iforig_version==0: |
| 71 | +self.skipTest("Could not assign a valid type version") |
| 72 | + |
| 73 | +type_modified(C) |
| 74 | +type_assign_specific_version_unsafe(C,orig_version+5) |
| 75 | +type_assign_version(C)# this should do nothing |
| 76 | + |
| 77 | +new_version=type_get_version(C) |
| 78 | +self.assertEqual(new_version,orig_version+5) |
| 79 | + |
| 80 | +_clear_type_cache() |
| 81 | + |
| 82 | + |
| 83 | +@support.cpython_only |
| 84 | +classTypeCacheWithSpecializationTests(unittest.TestCase): |
| 85 | +deftearDown(self): |
| 86 | +_clear_type_cache() |
| 87 | + |
| 88 | +def_assign_valid_version_or_skip(self,type_): |
| 89 | +type_modified(type_) |
| 90 | +type_assign_version(type_) |
| 91 | +iftype_get_version(type_)==0: |
| 92 | +self.skipTest("Could not assign valid type version") |
| 93 | + |
| 94 | +def_assign_and_check_version_0(self,user_type): |
| 95 | +type_modified(user_type) |
| 96 | +type_assign_specific_version_unsafe(user_type,0) |
| 97 | +self.assertEqual(type_get_version(user_type),0) |
| 98 | + |
| 99 | +def_all_opnames(self,func): |
| 100 | +returnset(instr.opnameforinstrindis.Bytecode(func,adaptive=True)) |
| 101 | + |
| 102 | +def_check_specialization(self,func,arg,opname,*,should_specialize): |
| 103 | +for_inrange(100): |
| 104 | +func(arg) |
| 105 | + |
| 106 | +ifshould_specialize: |
| 107 | +self.assertNotIn(opname,self._all_opnames(func)) |
| 108 | +else: |
| 109 | +self.assertIn(opname,self._all_opnames(func)) |
| 110 | + |
| 111 | +deftest_class_load_attr_specialization_user_type(self): |
| 112 | +classA: |
| 113 | +deffoo(self): |
| 114 | +pass |
| 115 | + |
| 116 | +self._assign_valid_version_or_skip(A) |
| 117 | + |
| 118 | +defload_foo_1(type_): |
| 119 | +type_.foo |
| 120 | + |
| 121 | +self._check_specialization(load_foo_1,A,"LOAD_ATTR",should_specialize=True) |
| 122 | +delload_foo_1 |
| 123 | + |
| 124 | +self._assign_and_check_version_0(A) |
| 125 | + |
| 126 | +defload_foo_2(type_): |
| 127 | +returntype_.foo |
| 128 | + |
| 129 | +self._check_specialization(load_foo_2,A,"LOAD_ATTR",should_specialize=False) |
| 130 | + |
| 131 | +deftest_class_load_attr_specialization_static_type(self): |
| 132 | +self._assign_valid_version_or_skip(str) |
| 133 | +self._assign_valid_version_or_skip(bytes) |
| 134 | + |
| 135 | +defget_capitalize_1(type_): |
| 136 | +returntype_.capitalize |
| 137 | + |
| 138 | +self._check_specialization(get_capitalize_1,str,"LOAD_ATTR",should_specialize=True) |
| 139 | +self.assertEqual(get_capitalize_1(str)('hello'),'Hello') |
| 140 | +self.assertEqual(get_capitalize_1(bytes)(b'hello'),b'Hello') |
| 141 | +delget_capitalize_1 |
| 142 | + |
| 143 | +# Permanently overflow the static type version counter, and force str and bytes |
| 144 | +# to have tp_version_tag == 0 |
| 145 | +for_inrange(2**16): |
| 146 | +type_modified(str) |
| 147 | +type_assign_version(str) |
| 148 | +type_modified(bytes) |
| 149 | +type_assign_version(bytes) |
| 150 | + |
| 151 | +self.assertEqual(type_get_version(str),0) |
| 152 | +self.assertEqual(type_get_version(bytes),0) |
| 153 | + |
| 154 | +defget_capitalize_2(type_): |
| 155 | +returntype_.capitalize |
| 156 | + |
| 157 | +self._check_specialization(get_capitalize_2,str,"LOAD_ATTR",should_specialize=False) |
| 158 | +self.assertEqual(get_capitalize_2(str)('hello'),'Hello') |
| 159 | +self.assertEqual(get_capitalize_2(bytes)(b'hello'),b'Hello') |
| 160 | + |
| 161 | +deftest_property_load_attr_specialization_user_type(self): |
| 162 | +classG: |
| 163 | +@property |
| 164 | +defx(self): |
| 165 | +return9 |
| 166 | + |
| 167 | +self._assign_valid_version_or_skip(G) |
| 168 | + |
| 169 | +defload_x_1(instance): |
| 170 | +instance.x |
| 171 | + |
| 172 | +self._check_specialization(load_x_1,G(),"LOAD_ATTR",should_specialize=True) |
| 173 | +delload_x_1 |
| 174 | + |
| 175 | +self._assign_and_check_version_0(G) |
| 176 | + |
| 177 | +defload_x_2(instance): |
| 178 | +instance.x |
| 179 | + |
| 180 | +self._check_specialization(load_x_2,G(),"LOAD_ATTR",should_specialize=False) |
| 181 | + |
| 182 | +deftest_store_attr_specialization_user_type(self): |
| 183 | +classB: |
| 184 | +__slots__= ("bar",) |
| 185 | + |
| 186 | +self._assign_valid_version_or_skip(B) |
| 187 | + |
| 188 | +defstore_bar_1(type_): |
| 189 | +type_.bar=10 |
| 190 | + |
| 191 | +self._check_specialization(store_bar_1,B(),"STORE_ATTR",should_specialize=True) |
| 192 | +delstore_bar_1 |
| 193 | + |
| 194 | +self._assign_and_check_version_0(B) |
| 195 | + |
| 196 | +defstore_bar_2(type_): |
| 197 | +type_.bar=10 |
| 198 | + |
| 199 | +self._check_specialization(store_bar_2,B(),"STORE_ATTR",should_specialize=False) |
| 200 | + |
59 | 201 |
|
60 | 202 | if__name__=="__main__": |
61 | 203 | unittest.main() |