MCPcopy Index your code
hub / github.com/python/mypy / solve_iteratively

Function solve_iteratively

mypy/solve.py:191–248  ·  view source on GitHub ↗

Solve transitive closure sequentially, updating upper/lower bounds after each step. Transitive closure is represented as a linear graph plus lower/upper bounds for each type variable, see transitive_closure() docstring for details. We solve for type variables that appear in `batch`. If

(
    batch: list[TypeVarId], graph: Graph, lowers: Bounds, uppers: Bounds
)

Source from the content-addressed store, hash-verified

189
190
191def solve_iteratively(
192 batch: list[TypeVarId], graph: Graph, lowers: Bounds, uppers: Bounds
193) -> Solutions:
194 """Solve transitive closure sequentially, updating upper/lower bounds after each step.
195
196 Transitive closure is represented as a linear graph plus lower/upper bounds for each
197 type variable, see transitive_closure() docstring for details.
198
199 We solve for type variables that appear in `batch`. If a bound is not constant (i.e. it
200 looks like T :> F[S, ...]), we substitute solutions found so far in the target F[S, ...]
201 after solving the batch.
202
203 Importantly, after solving each variable in a batch, we move it from linear graph to
204 upper/lower bounds, this way we can guarantee consistency of solutions (see comment below
205 for an example when this is important).
206 """
207 solutions = {}
208 s_batch = set(batch)
209 while s_batch:
210 for tv in sorted(s_batch, key=lambda x: x.raw_id):
211 if lowers[tv] or uppers[tv]:
212 solvable_tv = tv
213 break
214 else:
215 break
216 # Solve each solvable type variable separately.
217 s_batch.remove(solvable_tv)
218 result = solve_one(lowers[solvable_tv], uppers[solvable_tv])
219 solutions[solvable_tv] = result
220 if result is None:
221 # TODO: support backtracking lower/upper bound choices and order within SCCs.
222 # (will require switching this function from iterative to recursive).
223 continue
224
225 # Update the (transitive) bounds from graph if there is a solution.
226 # This is needed to guarantee solutions will never contradict the initial
227 # constraints. For example, consider {T <: S, T <: A, S :> B} with A :> B.
228 # If we would not update the uppers/lowers from graph, we would infer T = A, S = B
229 # which is not correct.
230 for l, u in graph.copy():
231 if l == u:
232 continue
233 if l == solvable_tv:
234 lowers[u].add(result)
235 graph.remove((l, u))
236 if u == solvable_tv:
237 uppers[l].add(result)
238 graph.remove((l, u))
239
240 # We can update uppers/lowers only once after solving the whole SCC,
241 # since uppers/lowers can't depend on type variables in the SCC
242 # (and we would reject such SCC as non-linear and therefore not solvable).
243 subs = {tv: s for (tv, s) in solutions.items() if s is not None}
244 for tv in lowers:
245 lowers[tv] = {expand_type(lt, subs) for lt in lowers[tv]}
246 for tv in uppers:
247 uppers[tv] = {expand_type(ut, subs) for ut in uppers[tv]}
248 return solutions

Callers 1

solve_with_dependentFunction · 0.85

Calls 8

expand_typeFunction · 0.90
setClass · 0.85
sortedFunction · 0.85
solve_oneFunction · 0.85
removeMethod · 0.45
copyMethod · 0.45
addMethod · 0.45
itemsMethod · 0.45

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…