2727
2828from mypy import util
2929from mypy .build import (
30+ SCC ,
3031 AckMessage ,
3132 BuildManager ,
33+ Graph ,
3234 GraphMessage ,
3335 SccRequestMessage ,
3436 SccResponseMessage ,
3537 SccsDataMessage ,
3638 SourcesDataMessage ,
37- load_graph ,
3839 load_plugins ,
3940 process_stale_scc ,
4041)
4142from mypy .defaults import RECURSION_LIMIT , WORKER_CONNECTION_TIMEOUT
42- from mypy .errors import CompileError , Errors , report_internal_error
43+ from mypy .errors import CompileError , ErrorInfo , Errors , report_internal_error
4344from mypy .fscache import FileSystemCache
4445from mypy .ipc import IPCException , IPCServer , receive , send
4546from mypy .modulefinder import BuildSource , BuildSourceSet , compute_search_paths
47+ from mypy .nodes import FileRawData
4648from mypy .options import Options
4749from mypy .util import read_py_file
4850from mypy .version import __version__
@@ -123,42 +125,24 @@ def serve(server: IPCServer, ctx: ServerContext) -> None:
123125 if manager is None :
124126 return
125127
126- # Mirror the GC freeze hack in the coordinator.
127- if platform .python_implementation () == "CPython" :
128- gc .disable ()
129- try :
130- graph = load_graph (sources , manager )
131- except CompileError :
132- # CompileError during loading will be reported by the coordinator.
133- return
134- if platform .python_implementation () == "CPython" :
135- gc .freeze ()
136- gc .unfreeze ()
137- gc .enable ()
138- for id in graph :
139- manager .import_map [id ] = graph [id ].dependencies_set
140- # Ignore errors during local graph loading to check that receiving
141- # early errors from coordinator works correctly.
142- manager .errors .reset ()
143-
144- # Notify worker we are done loading graph.
128+ # Notify coordinator we are done with setup.
145129 send (server , AckMessage ())
146-
147- # Compare worker graph and coordinator, with parallel parser we will only use the latter.
148130 graph_data = GraphMessage .read (receive (server ), manager )
149- assert set ( manager . missing_modules ) == graph_data . missing_modules
150- coordinator_graph = graph_data .graph
151- assert coordinator_graph . keys () == graph . keys ()
131+ # Update some manager data in-place as it has been passed to semantic analyzer.
132+ manager . missing_modules | = graph_data .missing_modules
133+ graph = graph_data . graph
152134 for id in graph :
153- assert graph [id ]. dependencies_set == coordinator_graph [id ].dependencies_set
154- assert graph [ id ]. suppressed_set == coordinator_graph [ id ]. suppressed_set
155- send ( server , AckMessage () )
135+ manager . import_map [id ] = graph [id ].dependencies_set
136+ # Link modules dicts, so that plugins will get access to ASTs as we parse them.
137+ manager . plugin . set_modules ( manager . modules )
156138
139+ # Notify coordinator we are ready to receive computed graph SCC structure.
140+ send (server , AckMessage ())
157141 sccs = SccsDataMessage .read (receive (server )).sccs
158142 manager .scc_by_id = {scc .id : scc for scc in sccs }
159143 manager .top_order = [scc .id for scc in sccs ]
160144
161- # Notify coordinator we are ready to process SCCs.
145+ # Notify coordinator we are ready to start processing SCCs.
162146 send (server , AckMessage ())
163147 while True :
164148 scc_message = SccRequestMessage .read (receive (server ))
@@ -169,20 +153,17 @@ def serve(server: IPCServer, ctx: ServerContext) -> None:
169153 scc = manager .scc_by_id [scc_id ]
170154 t0 = time .time ()
171155 try :
172- for id in scc .mod_ids :
173- state = graph [id ]
174- # Extra if below is needed only because we are using local graph.
175- # TODO: clone options when switching to coordinator graph.
176- if state .tree is None :
177- # Parse early to get errors related data, such as ignored
178- # and skipped lines before replaying the errors.
179- state .parse_file ()
180- else :
181- state .setup_errors ()
182- if id in scc_message .import_errors :
183- manager .errors .set_file (state .xpath , id , state .options )
184- for err_info in scc_message .import_errors [id ]:
185- manager .errors .add_error_info (err_info )
156+ if platform .python_implementation () == "CPython" :
157+ # Since we are splitting the GC freeze hack into multiple smaller freezes,
158+ # we should collect young generations to not accumulate accidental garbage.
159+ gc .collect (generation = 1 )
160+ gc .collect (generation = 0 )
161+ gc .disable ()
162+ load_states (scc , graph , manager , scc_message .import_errors , scc_message .mod_data )
163+ if platform .python_implementation () == "CPython" :
164+ gc .freeze ()
165+ gc .unfreeze ()
166+ gc .enable ()
186167 result = process_stale_scc (graph , scc , manager , from_cache = graph_data .from_cache )
187168 # We must commit after each SCC, otherwise we break --sqlite-cache.
188169 manager .metastore .commit ()
@@ -193,6 +174,34 @@ def serve(server: IPCServer, ctx: ServerContext) -> None:
193174 manager .add_stats (total_process_stale_time = time .time () - t0 , stale_sccs_processed = 1 )
194175
195176
177+ def load_states (
178+ scc : SCC ,
179+ graph : Graph ,
180+ manager : BuildManager ,
181+ import_errors : dict [str , list [ErrorInfo ]],
182+ mod_data : dict [str , tuple [bytes , FileRawData | None ]],
183+ ) -> None :
184+ """Re-create full state of an SCC as it would have been in coordinator."""
185+ for id in scc .mod_ids :
186+ state = graph [id ]
187+ # Re-clone options since we don't send them, it is usually faster than deserializing.
188+ state .options = state .options .clone_for_module (state .id )
189+ suppressed_deps_opts , raw_data = mod_data [id ]
190+ state .parse_file (raw_data = raw_data )
191+ # Set data that is needed to be written to cache meta.
192+ state .known_suppressed_deps_opts = suppressed_deps_opts
193+ assert state .tree is not None
194+ import_lines = {imp .line for imp in state .tree .imports }
195+ state .imports_ignored = {
196+ line : codes for line , codes in state .tree .ignored_lines .items () if line in import_lines
197+ }
198+ # Replay original errors encountered during graph loading in coordinator.
199+ if id in import_errors :
200+ manager .errors .set_file (state .xpath , id , state .options )
201+ for err_info in import_errors [id ]:
202+ manager .errors .add_error_info (err_info )
203+
204+
196205def setup_worker_manager (sources : list [BuildSource ], ctx : ServerContext ) -> BuildManager | None :
197206 data_dir = os .path .dirname (os .path .dirname (__file__ ))
198207 # This is used for testing only now.
0 commit comments