1- """Import-cache ordering must not temporarily remove an exact-dict entry.
2-
3- The trace-visible presence assertion is a RustPython atomicity invariant, not
4- an assertion supplied by CPython's old pop/set bootstrap. CPython still checks
5- the fallback's value, ordering, exception and custom-mapping semantics here.
6- """
1+ """Private exact-dict relocation and callback-reentry regression coverage."""
72
83import _imp
9- import os
104import sys
11- import types
12- from importlib import _bootstrap as frozen
135
146
157def raises (kind , call , expected = None ):
@@ -22,258 +14,6 @@ def raises(kind, call, expected=None):
2214 raise AssertionError (f"{ kind .__name__ } was not raised" )
2315
2416
25- def source_bootstrap ():
26- path = os .path .join (
27- os .path .dirname (os .path .dirname (os .path .dirname (__file__ ))),
28- "Lib" ,
29- "importlib" ,
30- "_bootstrap.py" ,
31- )
32- source = types .ModuleType ("atomic_reorder_source_bootstrap" )
33- with open (path , encoding = "utf-8" ) as stream :
34- exec (compile (stream .read (), path , "exec" ), source .__dict__ )
35- for name in (
36- "sys" ,
37- "_imp" ,
38- "_thread" ,
39- "_warnings" ,
40- "_weakref" ,
41- "_bootstrap_external" ,
42- ):
43- setattr (source , name , getattr (frozen , name ))
44- source ._blocking_on = source ._WeakValueDictionary ()
45- return source
46-
47-
48- class Loader :
49- def __init__ (self , action ):
50- self .action = action
51-
52- def create_module (self , spec ):
53- return None
54-
55- def exec_module (self , module ):
56- self .action (module )
57-
58-
59- def invoke (bootstrap , action , mode = "load" ):
60- name = "_atomic_reorder_test"
61- spec = bootstrap .ModuleSpec (name , Loader (action ))
62- if mode == "exec" :
63- module = bootstrap .module_from_spec (spec )
64- sys .modules [name ] = module
65- return bootstrap ._exec (spec , module )
66- return bootstrap ._load_unlocked (spec )
67-
68-
69- def check_trace_presence (bootstrap , mode ):
70- original = sys .modules
71- old_trace = sys .gettrace ()
72- name = "_atomic_reorder_trace"
73- expected = types .ModuleType (name )
74- after = "_atomic_reorder_trace_after"
75- state = {"armed" : False , "events" : 0 }
76- failure = RuntimeError ("legacy loader failed with its entry still present" )
77- codes = {
78- bootstrap ._exec .__code__ ,
79- bootstrap ._load_unlocked .__code__ ,
80- }
81- if hasattr (bootstrap , "_load_backward_compatible" ):
82- codes .add (bootstrap ._load_backward_compatible .__code__ )
83- if hasattr (bootstrap , "_reorder_module" ):
84- codes .add (bootstrap ._reorder_module .__code__ )
85-
86- def trace (frame , event , arg ):
87- if event == "line" and state ["armed" ] and frame .f_code in codes :
88- state ["events" ] += 1
89- assert name in sys .modules , (mode , frame .f_code .co_name , frame .f_lineno )
90- assert sys .modules [name ] is expected
91- return trace
92-
93- def finish (module ):
94- sys .modules [name ] = expected
95- sys .modules [after ] = object ()
96- state ["armed" ] = True
97-
98- class LegacyLoader :
99- def load_module (self , fullname ):
100- assert fullname == name
101- finish (expected )
102- if mode == "legacy_error" :
103- raise failure
104- return expected
105-
106- try :
107- sys .modules = original .copy ()
108- loader = LegacyLoader () if mode .startswith ("legacy" ) else Loader (finish )
109- spec = bootstrap .ModuleSpec (name , loader )
110- if mode == "exec" :
111- initial = bootstrap .module_from_spec (spec )
112- sys .modules [name ] = initial
113- call = lambda : bootstrap ._exec (spec , initial )
114- elif mode .startswith ("legacy" ):
115- call = lambda : bootstrap ._load_backward_compatible (spec )
116- else :
117- call = lambda : bootstrap ._load_unlocked (spec )
118- sys .settrace (trace )
119- if mode == "legacy_error" :
120- raises (RuntimeError , call , failure )
121- else :
122- assert call () is expected
123- sys .settrace (old_trace )
124- assert state ["events" ] > 0 , "the successful loader window was not traced"
125- assert list (sys .modules )[- 2 :] == [after , name ]
126- finally :
127- sys .settrace (old_trace )
128- sys .modules = original
129-
130-
131- def check_loader_semantics (bootstrap ):
132- original = sys .modules
133- name = "_atomic_reorder_test"
134- after = "_atomic_reorder_after"
135- try :
136- for mode in ("load" , "exec" ):
137- for replacement in (object (), None ):
138- sys .modules = original .copy ()
139-
140- def replace (module ):
141- sys .modules [name ] = replacement
142- sys .modules [after ] = object ()
143-
144- assert invoke (bootstrap , replace , mode ) is replacement
145- assert sys .modules [name ] is replacement
146- assert list (sys .modules )[- 2 :] == [after , name ]
147-
148- sys .modules = original .copy ()
149-
150- def remove (module ):
151- del sys .modules [name ]
152-
153- exc = raises (KeyError , lambda : invoke (bootstrap , remove , mode ))
154- assert exc .args == (name ,)
155- assert name not in sys .modules
156-
157- sys .modules = original .copy ()
158- replacement = object ()
159- rebound = {"before" : object (), name : replacement , after : object ()}
160-
161- def rebind (module ):
162- sys .modules = rebound
163-
164- assert invoke (bootstrap , rebind , mode ) is replacement
165- assert sys .modules is rebound
166- assert list (rebound ) == ["before" , after , name ]
167-
168- sys .modules = original .copy ()
169- failure = RuntimeError ("loader failure after deliberate removal" )
170-
171- def remove_and_fail (module ):
172- del sys .modules [name ]
173- raise failure
174-
175- raises (RuntimeError , lambda : invoke (bootstrap , remove_and_fail ), failure )
176- assert name not in sys .modules
177-
178- sys .modules = original .copy ()
179-
180- def fail_with_entry (module ):
181- sys .modules [after ] = object ()
182- raise failure
183-
184- raises (
185- RuntimeError , lambda : invoke (bootstrap , fail_with_entry , "exec" ), failure
186- )
187- assert list (sys .modules )[- 2 :] == [after , name ]
188-
189- if hasattr (bootstrap , "_load_backward_compatible" ):
190-
191- class LegacyLoader :
192- def load_module (self , fullname ):
193- sys .modules [fullname ] = None
194- del sys .modules [fullname ]
195- raise failure
196-
197- spec = bootstrap .ModuleSpec (name , LegacyLoader ())
198- sys .modules = original .copy ()
199- raises (
200- RuntimeError , lambda : bootstrap ._load_backward_compatible (spec ), failure
201- )
202- assert name not in sys .modules
203- finally :
204- sys .modules = original
205-
206-
207- def check_custom_mappings (bootstrap ):
208- original = sys .modules
209- name = "_atomic_reorder_custom"
210- value = object ()
211- events = []
212-
213- class Subclass (dict ):
214- def pop (self , key ):
215- events .append (("pop" , key ))
216- return super ().pop (key )
217-
218- def __setitem__ (self , key , value ):
219- events .append (("set" , key , value ))
220- super ().__setitem__ (key , value )
221-
222- class Mapping :
223- def __init__ (self , pairs ):
224- self .data = dict (pairs )
225-
226- def pop (self , key ):
227- events .append (("pop" , key ))
228- return self .data .pop (key )
229-
230- def __setitem__ (self , key , value ):
231- events .append (("set" , key , value ))
232- self .data [key ] = value
233-
234- try :
235- spec = bootstrap .ModuleSpec (name , None )
236- for cls in (Subclass , Mapping ):
237- events .clear ()
238- modules = cls ([(name , value ), ("after" , None )])
239- sys .modules = modules
240- assert bootstrap ._reorder_module (spec ) is value
241- assert events == [("pop" , name ), ("set" , name , value )]
242- assert list (modules if cls is Subclass else modules .data ) == ["after" , name ]
243-
244- rebound = Subclass ()
245- new_name = "_atomic_reorder_rebound"
246-
247- class Rebinding (Subclass ):
248- def pop (self , key ):
249- result = super ().pop (key )
250- sys .modules = rebound
251- spec .name = new_name
252- return result
253-
254- events .clear ()
255- modules = Rebinding ({name : value })
256- sys .modules = modules
257- assert bootstrap ._reorder_module (spec ) is value
258- assert events == [("pop" , name ), ("set" , new_name , value )]
259- assert sys .modules is rebound
260- assert rebound [new_name ] is value
261- assert name not in modules
262-
263- failure = KeyError ("custom pop exception" )
264-
265- class Raising (Subclass ):
266- def pop (self , key ):
267- raise failure
268-
269- events .clear ()
270- sys .modules = Raising ({new_name : value })
271- raises (KeyError , lambda : bootstrap ._reorder_module (spec ), failure )
272- assert events == []
273- finally :
274- sys .modules = original
275-
276-
27717def check_native_helper ():
27818 move = _imp ._dict_move_to_end
27919 first , middle , last = object (), object (), object ()
@@ -445,19 +185,5 @@ def __del__(self):
445185 assert list (mapping )[- 1 ] == key
446186
447187
448- source = source_bootstrap ()
449- for bootstrap in (frozen , source ):
450- if sys .implementation .name == "rustpython" :
451- # Run this before inspecting the new helper so a baseline executable
452- # fails on the actual pop/set gap, not just a missing private API.
453- modes = ("load" , "exec" )
454- if hasattr (bootstrap , "_load_backward_compatible" ):
455- modes += ("legacy" , "legacy_error" )
456- for mode in modes :
457- check_trace_presence (bootstrap , mode )
458- check_loader_semantics (bootstrap )
459- if hasattr (bootstrap , "_reorder_module" ):
460- check_custom_mappings (bootstrap )
461-
462188if sys .implementation .name == "rustpython" :
463189 check_native_helper ()
0 commit comments