Coverage for pysource_minimize/_minimize_structure.py: 86%

442 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2024-03-25 08:14 +0100

1import ast 

2import sys 

3 

4from ._minimize_base import arguments 

5from ._minimize_base import coverage_required 

6from ._minimize_base import MinimizeBase 

7from ._minimize_base import ValueWrapper 

8 

9 

10def walk_until(node, stop=()): 

11 if isinstance(node, list): 

12 for e in node: 

13 yield from walk_until(e, stop) 

14 return 

15 yield node 

16 for child in ast.iter_child_nodes(node): 

17 if not isinstance(child, stop): 17 ↛ 16line 17 didn't jump to line 16, because the condition on line 17 was never false

18 yield from walk_until(child) 

19 

20 

21class MinimizeStructure(MinimizeBase): 

22 def minimize(self, o): 

23 if ( 

24 sys.version_info >= (3, 8) 

25 and isinstance(o, (ast.expr, ast.stmt)) 

26 and hasattr(o, "type_comment") 

27 ): 

28 self.try_attr(o, "type_comment", None) 

29 

30 if isinstance(o, ast.expr): 

31 return self.minimize_expr(o) 

32 elif isinstance(o, ast.stmt): 

33 return self.minimize_stmt(o) 

34 elif sys.version_info < (3, 9) and isinstance(o, ast.slice): 

35 return self.minimize_expr(o) 

36 elif isinstance(o, list): 

37 return self.minimize_list(o, self.minimize) 

38 elif isinstance(o, ast.arg): 38 ↛ 40line 38 didn't jump to line 40, because the condition on line 38 was never false

39 return self.minimize_arg(o) 

40 elif isinstance(o, ast.pattern): 

41 pass 

42 elif isinstance(o, ValueWrapper): 

43 pass 

44 else: 

45 raise TypeError(type(o)) 

46 

47 def minimize_comprehension(self, comp): 

48 self.minimize_expr(comp.iter) 

49 self.minimize_list(comp.ifs, terminal=self.minimize_expr) 

50 

51 def minimize_arg(self, arg: ast.arg): 

52 self.minimize_optional(arg.annotation) 

53 

54 def minimize_expr(self, node): 

55 if isinstance(node, ast.BoolOp): 

56 remaining = self.minimize_list(node.values, self.minimize_expr, 1) 

57 if len(remaining) == 1: 57 ↛ exitline 57 didn't return from function 'minimize_expr', because the condition on line 57 was never false

58 self.try_only(node, remaining[0]) 

59 

60 elif isinstance(node, ast.Compare): 

61 if self.try_only(node, node.left): 61 ↛ 62line 61 didn't jump to line 62, because the condition on line 61 was never true

62 self.minimize(node.left) 

63 return 

64 

65 for comp in node.comparators: 

66 if self.try_only(node, comp): 

67 self.minimize(comp) 

68 return 

69 

70 self.minimize_lists( 70 ↛ exitline 70 didn't jump to the function exit

71 (node.ops, node.comparators), (lambda _: None, self.minimize) 

72 ) 

73 

74 elif isinstance(node, ast.Subscript): 

75 self.try_only_minimize(node, node.value, node.slice) 

76 

77 elif isinstance(node, ast.FormattedValue): 

78 if isinstance(node.format_spec, ast.JoinedStr): 

79 # work around for https://github.com/python/cpython/issues/110309 

80 spec = [ 

81 v 

82 for v in node.format_spec.values 

83 if not (isinstance(v, ast.Constant) and v.value == "") 

84 ] 

85 if len(spec) == 1 and self.try_only(node, spec[0]): 85 ↛ 86line 85 didn't jump to line 86, because the condition on line 85 was never true

86 self.minimize(spec[0]) 

87 return 

88 

89 if not self.try_none(node.format_spec): 

90 self.minimize(node.format_spec) 

91 

92 self.minimize_expr(node.value) 

93 

94 elif isinstance(node, ast.JoinedStr): 

95 for v in node.values: 

96 if isinstance(v, ast.FormattedValue) and self.try_only(node, v.value): 

97 self.minimize(v.value) 

98 return 

99 if ( 

100 isinstance(v, ast.FormattedValue) 

101 and v.format_spec 

102 and self.try_only(node, v.format_spec) 

103 ): 

104 self.minimize(v.format_spec) 

105 return 

106 

107 self.minimize(node.values) 

108 # todo minimize values 

109 

110 elif isinstance(node, ast.Slice): 

111 if self.try_only(node, node.lower, node.upper, node.step): 

112 return 

113 

114 for child in (node.lower, node.upper, node.step): 114 ↛ exitline 114 didn't return from function 'minimize_expr', because the loop on line 114 didn't complete

115 if not self.try_none(child): 115 ↛ 116line 115 didn't jump to line 116, because the condition on line 115 was never true

116 self.minimize(child) 

117 

118 elif isinstance(node, ast.ExtSlice): 118 ↛ 119line 118 didn't jump to line 119, because the condition on line 118 was never true

119 self.minimize_list(node.dims, minimal=1) 

120 

121 elif isinstance(node, ast.Index): 

122 self.minimize(node.value) 

123 

124 elif isinstance(node, ast.Lambda): 

125 if self.try_only_minimize(node, node.body): 

126 return 

127 

128 if self.minimize_args_of(node): 

129 return 

130 

131 elif isinstance(node, ast.UnaryOp): 

132 self.try_only_minimize(node, node.operand) 

133 elif isinstance(node, ast.BinOp): 

134 self.try_only_minimize(node, node.left, node.right) 

135 elif isinstance(node, ast.Attribute): 

136 if not self.try_node(node, ast.Name(id="something", ctx=ast.Load())): 

137 self.try_only_minimize(node, node.value) 

138 elif isinstance(node, ast.IfExp): 

139 self.try_only_minimize(node, node.test, node.body, node.orelse) 

140 elif isinstance(node, ast.Await): 

141 self.try_only_minimize(node, node.value) 

142 elif isinstance(node, ast.Yield): 

143 if node.value is None: 

144 if self.try_node(node, ast.Constant(value=None)): 144 ↛ 146line 144 didn't jump to line 146, because the condition on line 144 was never false

145 return 

146 self.try_only_minimize(node, node.value) 

147 elif isinstance(node, ast.YieldFrom): 

148 self.try_only_minimize(node, node.value) 

149 elif isinstance(node, ast.Dict): 

150 remaining = self.minimize_lists( 

151 (node.keys, node.values), (self.minimize, self.minimize) 

152 ) 

153 if len(remaining) == 1: 

154 if self.try_only(node, remaining[0][0]): 

155 return 

156 

157 if self.try_only(node, remaining[0][1]): 157 ↛ exitline 157 didn't return from function 'minimize_expr', because the condition on line 157 was never false

158 return 

159 

160 elif isinstance(node, (ast.Set)): 

161 remaining = self.minimize_list(node.elts, self.minimize, 1) 

162 # TODO: min size 1? 

163 if len(remaining) == 1: 163 ↛ exitline 163 didn't return from function 'minimize_expr', because the condition on line 163 was never false

164 self.try_only(node, remaining[0]) 

165 elif isinstance(node, (ast.List, ast.Tuple)): 

166 remaining = self.minimize_list(node.elts, self.minimize, 1) 

167 # TODO: min size 1? 

168 if len(remaining) == 1: 168 ↛ exitline 168 didn't return from function 'minimize_expr', because the condition on line 168 was never false

169 self.try_only(node, remaining[0]) 

170 elif isinstance(node, ast.Name): 

171 pass 

172 elif isinstance(node, ast.Constant): 

173 pass 

174 elif isinstance(node, ast.Index): 174 ↛ 175line 174 didn't jump to line 175, because the condition on line 174 was never true

175 self.minimize(node.value) 

176 elif sys.version_info < (3, 8) and isinstance( 176 ↛ 179line 176 didn't jump to line 179, because the condition on line 176 was never true

177 node, (ast.Str, ast.Bytes, ast.Num, ast.NameConstant, ast.Ellipsis) 

178 ): 

179 pass 

180 elif isinstance(node, ast.Starred): 

181 self.try_only_minimize(node, node.value) 

182 elif isinstance(node, ast.Call): 

183 for e in [ 

184 node.func, 

185 *[kw.value for kw in node.keywords], 

186 *[ 

187 arg.value if isinstance(arg, ast.Starred) else arg 

188 for arg in node.args 

189 ], 

190 ]: 

191 if self.try_only(node, e): 

192 self.minimize(e) 

193 return 

194 

195 self.minimize(node.args) 

196 self.minimize_list( 196 ↛ exitline 196 didn't jump to the function exit

197 node.keywords, terminal=lambda kw: self.minimize(kw.value) 

198 ) 

199 

200 elif isinstance( 

201 node, (ast.ListComp, ast.SetComp, ast.GeneratorExp, ast.DictComp) 

202 ): 

203 for gen in node.generators: 

204 if self.try_only(node, gen.target): 

205 self.minimize_expr(gen.target) 

206 return 

207 

208 if self.try_only(node, gen.iter): 

209 self.minimize_expr(gen.iter) 

210 return 

211 

212 for if_ in gen.ifs: 

213 if self.try_only(node, if_): 

214 self.minimize_expr(if_) 

215 return 

216 

217 if isinstance(node, ast.DictComp): 

218 if self.try_only_minimize(node, node.key, node.value): 218 ↛ 224line 218 didn't jump to line 224, because the condition on line 218 was never false

219 return 

220 else: 

221 if self.try_only_minimize(node, node.elt): 

222 return 

223 

224 self.minimize_list(node.generators, self.minimize_comprehension, 1) 

225 

226 elif isinstance(node, ast.NamedExpr): 

227 self.try_only_minimize(node, node.target, node.value) 

228 else: 

229 assert False, "expression is not handled %s" % (node) 

230 

231 def minimize_optional(self, node): 

232 if not self.try_none(node): 

233 self.minimize(node) 

234 

235 if sys.version_info >= (3, 10): 

236 

237 def minimize_match_case(self, c: ast.match_case): 

238 def minimize_pattern(pattern): 

239 if isinstance(pattern, ast.MatchSequence): 239 ↛ 240line 239 didn't jump to line 240, because the condition on line 239 was never true

240 self.minimize_list(pattern.patterns, minimize_pattern) 

241 elif isinstance(pattern, ast.MatchOr): 241 ↛ 242line 241 didn't jump to line 242, because the condition on line 241 was never true

242 self.minimize_list(pattern.patterns, minimize_pattern, 1) 

243 

244 elif isinstance(pattern, ast.MatchAs): 244 ↛ 245line 244 didn't jump to line 245, because the condition on line 244 was never true

245 if pattern.pattern: 

246 self.try_only(pattern, pattern.pattern) 

247 elif isinstance(pattern, ast.MatchMapping): 247 ↛ 248line 247 didn't jump to line 248, because the condition on line 247 was never true

248 self.minimize_lists( 

249 (pattern.keys, pattern.patterns), 

250 (self.minimize, minimize_pattern), 

251 ) 

252 elif isinstance(pattern, ast.MatchClass): 

253 self.minimize(pattern.cls) 

254 self.minimize_list(pattern.patterns, minimize_pattern) 

255 self.minimize_lists((pattern.kwd_attrs, pattern.kwd_patterns)) 

256 

257 self.minimize(c.body) 

258 

259 if not self.try_none(c.guard): 259 ↛ 260line 259 didn't jump to line 260, because the condition on line 259 was never true

260 self.minimize(c.guard) 

261 

262 minimize_pattern(c.pattern) 

263 

264 def minimize_args_of(self, func): 

265 args = func.args 

266 

267 for child in [ 

268 *[arg.annotation for arg in arguments(func)], 

269 *func.args.defaults, 

270 *func.args.kw_defaults, 

271 getattr(func, "returns", None), 

272 ]: 

273 if child != None and self.try_only(func, child): 

274 self.minimize(child) 

275 return True 

276 

277 all_args = [] 

278 if sys.version_info >= (3, 8): 278 ↛ 280line 278 didn't jump to line 280, because the condition on line 278 was never false

279 all_args += args.posonlyargs 

280 all_args += args.args 

281 

282 split = len(all_args) - len(args.defaults) 

283 self.minimize_list(all_args[:split]) 

284 remaining = self.minimize_lists( 

285 (all_args[split:], args.defaults), (self.minimize, lambda a: None) 

286 ) 

287 

288 remove_defaults = True 

289 for _, default in remaining: 

290 if remove_defaults: 290 ↛ 296line 290 didn't jump to line 296, because the condition on line 290 was never false

291 if default is not None: 291 ↛ 289line 291 didn't jump to line 289, because the condition on line 291 was never false

292 if not self.try_without([default]): 292 ↛ 289,   292 ↛ 2932 missed branches: 1) line 292 didn't jump to line 289, because the condition on line 292 was never false, 2) line 292 didn't jump to line 293, because the condition on line 292 was never true

293 self.minimize(default) 

294 remove_defaults = False 

295 else: 

296 self.minimize(default) 

297 

298 remaining = self.minimize_lists( 

299 (args.kwonlyargs, args.kw_defaults), (self.minimize, lambda a: None) 

300 ) 

301 for _, default in remaining: 

302 self.minimize_optional(default) 

303 

304 self.minimize_optional(args.vararg) 

305 self.minimize_optional(args.kwarg) 

306 

307 return False 

308 

309 def minimize_stmt(self, node): 

310 if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): 

311 if self.try_only_minimize(node, node.decorator_list): 311 ↛ 312line 311 didn't jump to line 312, because the condition on line 311 was never true

312 return 

313 

314 self.minimize_list(node.body) 

315 body = self.get_ast(node).body 

316 

317 if not any( 317 ↛ 342line 317 didn't jump to line 342, because the condition on line 317 was never false

318 isinstance( 

319 n, 

320 ( 

321 ast.Return, 

322 ast.Yield, 

323 ast.YieldFrom, 

324 ast.Await, 

325 ast.AsyncFor, 

326 ast.AsyncWith, 

327 ), 

328 ) 

329 for n in walk_until( 

330 body, 

331 ( 

332 ast.GeneratorExp, 

333 ast.FunctionDef, 

334 ast.ClassDef, 

335 ast.AsyncFunctionDef, 

336 ), 

337 ) 

338 ): 

339 if self.try_only(node, node.body): 

340 return 

341 

342 if self.minimize_args_of(node): 

343 return 

344 

345 if sys.version_info >= (3, 12): 345 ↛ 356line 345 didn't jump to line 356, because the condition on line 345 was never false

346 for p in node.type_params: 346 ↛ 354line 346 didn't jump to line 354, because the loop on line 346 didn't complete

347 if ( 347 ↛ 346line 347 didn't jump to line 346

348 isinstance(p, ast.TypeVar) 

349 and p.bound is not None 

350 and self.try_only(node, p.bound) 

351 ): 

352 self.minimize(p.bound) 

353 return 

354 self.minimize_list(node.type_params, self.minimize_type_param) 

355 

356 if node.returns: 

357 if not self.try_none(node.returns): 

358 self.minimize_expr(node.returns) 

359 

360 elif isinstance(node, ast.ClassDef): 

361 if self.try_only_minimize(node, node.decorator_list): 361 ↛ 362line 361 didn't jump to line 362, because the condition on line 361 was never true

362 return 

363 

364 if self.try_only_minimize(node, node.body): 364 ↛ 365line 364 didn't jump to line 365, because the condition on line 364 was never true

365 return 

366 

367 if sys.version_info >= (3, 12): 

368 for p in node.type_params: 

369 if ( 369 ↛ 368line 369 didn't jump to line 368

370 isinstance(p, ast.TypeVar) 

371 and p.bound is not None 

372 and self.try_only(node, p.bound) 

373 ): 

374 self.minimize(p.bound) 

375 return 

376 self.minimize_list(node.type_params, self.minimize_type_param) 

377 

378 for e in [ 

379 *[kw.value for kw in node.keywords], 

380 *[ 

381 arg.value if isinstance(arg, ast.Starred) else arg 

382 for arg in node.bases 

383 ], 

384 ]: 

385 if self.try_only(node, e): 

386 self.minimize(e) 

387 return 

388 

389 self.minimize(node.bases) 

390 self.minimize_list( 

391 node.keywords, terminal=lambda kw: self.minimize(kw.value) 

392 ) 

393 return 

394 

395 elif isinstance(node, ast.Return): 

396 self.try_only_minimize(node, node.value) 

397 

398 elif isinstance(node, ast.Delete): 

399 for t in node.targets: 399 ↛ 404line 399 didn't jump to line 404, because the loop on line 399 didn't complete

400 if self.try_only(node, t): 

401 self.minimize(t) 

402 return 

403 

404 self.minimize_list(node.targets, self.minimize, 1) 

405 

406 elif isinstance(node, ast.Assign): 

407 self.try_only_minimize(node, node.value, node.targets) 

408 

409 elif isinstance(node, ast.AugAssign): 

410 self.try_only_minimize(node, node.target, node.value) 

411 

412 elif isinstance(node, ast.AnnAssign): 

413 for child in [node.target, node.value, node.annotation]: 

414 if child is not None and self.try_only(node, child): 

415 self.minimize(child) 

416 return 

417 

418 if not self.try_node( 

419 node, 

420 ast.Assign( 

421 targets=[node.target], 

422 value=node.value, 

423 **(dict(type_comment="") if sys.version_info >= (3, 8) else {}), 

424 ), 

425 ): 

426 self.minimize(node.target) 

427 self.minimize_optional(node.value) 

428 self.minimize(node.annotation) 

429 

430 elif isinstance(node, (ast.For, ast.AsyncFor)): 

431 if self.try_only(node, node.target): 

432 self.minimize(node.target) 

433 return 

434 

435 self.minimize_list(node.body) 

436 body = self.get_ast(node) 

437 if not any( 437 ↛ 443line 437 didn't jump to line 443, because the condition on line 437 was never false

438 isinstance(n, (ast.Break, ast.Continue)) for n in ast.walk(body) 

439 ): 

440 if self.try_only(node, node.body): 

441 return 

442 

443 self.try_only_minimize(node, node.iter, node.orelse) 

444 self.minimize(node.target) 

445 

446 elif isinstance(node, ast.While): 

447 self.minimize_list(node.body) 

448 body = self.get_ast(node) 

449 if not any( 449 ↛ 455line 449 didn't jump to line 455, because the condition on line 449 was never false

450 isinstance(n, (ast.Break, ast.Continue)) for n in ast.walk(body) 

451 ): 

452 if self.try_only(node, node.body): 

453 return 

454 

455 self.try_only_minimize(node, node.test, node.orelse) 

456 

457 elif isinstance(node, (ast.Break, ast.Continue)): 457 ↛ 458line 457 didn't jump to line 458, because the condition on line 457 was never true

458 pass 

459 

460 elif isinstance(node, ast.If): 

461 self.try_only_minimize(node, node.test, node.body, node.orelse) 

462 

463 elif isinstance(node, (ast.With, ast.AsyncWith)): 

464 if self.try_only_minimize(node, node.body): 

465 return 

466 

467 for item in node.items: 

468 if self.try_only(node, item.context_expr): 

469 self.minimize(item.context_expr) 

470 return 

471 

472 if item.optional_vars is not None and self.try_only( 

473 node, item.optional_vars 

474 ): 

475 self.minimize(item.optional_vars) 

476 return 

477 

478 def minimize_item(item: ast.withitem): 

479 self.minimize(item.context_expr) 

480 self.minimize_optional(item.optional_vars) 

481 

482 self.minimize_list(node.items, minimize_item, minimal=1) 

483 

484 elif sys.version_info >= (3, 10) and isinstance(node, ast.Match): 

485 if self.try_only_minimize(node, node.subject): 

486 return 

487 

488 for case_ in node.cases: 

489 for e in [case_.guard, case_.body]: 

490 if e is not None and self.try_only(node, e): 

491 self.minimize(e) 

492 return 

493 

494 if isinstance(case_.pattern, ast.MatchValue): 

495 if self.try_only(node, case_.pattern.value): 

496 self.minimize(case_.pattern.value) 

497 return 

498 

499 self.minimize_list(node.cases, self.minimize_match_case, 1) 

500 

501 elif isinstance(node, ast.Raise): 

502 if node.exc and self.try_only(node, node.exc): 

503 self.minimize(node.exc) 

504 return 

505 

506 if node.cause and not self.try_only(node, node.cause): 

507 self.minimize_optional(node.cause) 

508 # cause requires exc 

509 # `raise from cause` is not valid 

510 if self.get_ast(node).cause: 510 ↛ 513line 510 didn't jump to line 513, because the condition on line 510 was never false

511 self.minimize(node.exc) 

512 else: 

513 coverage_required() 

514 self.minimize_optional(node.exc) 

515 

516 elif isinstance(node, ast.Try) or ( 

517 sys.version_info >= (3, 11) and isinstance(node, ast.TryStar) 

518 ): 

519 try_star = sys.version_info >= (3, 11) and isinstance(node, ast.TryStar) 

520 

521 if self.try_only(node, node.body): 

522 self.minimize(node.body) 

523 return 

524 

525 if node.orelse and self.try_only(node, node.orelse): 

526 self.minimize(node.orelse) 

527 return 

528 

529 if node.finalbody and self.try_only(node, node.finalbody): 

530 self.minimize(node.finalbody) 

531 return 

532 

533 for h in node.handlers: 

534 if self.try_only(node, h.body): 

535 self.minimize(h.body) 

536 return 

537 if h.type is not None and self.try_only(node, h.type): 

538 self.minimize(h.type) 

539 return 

540 

541 def minimize_except_handler(handler): 

542 self.minimize_list(handler.body) 

543 

544 if not handler.name and not try_star: 

545 self.minimize_optional(handler.type) 

546 elif handler.type is not None: 546 ↛ 549line 546 didn't jump to line 549, because the condition on line 546 was never false

547 self.minimize(handler.type) 

548 

549 if handler.name: 

550 self.try_attr(handler, "name", None) 

551 

552 self.minimize_list( 

553 node.handlers, minimize_except_handler, 1 # 0 if node.finalbody else 1 

554 ) 

555 

556 self.minimize(node.body) 

557 self.minimize(node.orelse) 

558 self.minimize(node.finalbody) 

559 

560 # if try_star and self.try_node( 

561 # node, 

562 # ast.Try( 

563 # body=node.body, 

564 # handlers=node.handlers, 

565 # orelse=node.orelse, 

566 # finalbody=node.finalbody, 

567 # ), 

568 # ): 

569 # return 

570 

571 elif isinstance(node, ast.Assert): 

572 if node.msg: 

573 if self.try_only(node, node.msg): 

574 self.minimize(node.msg) 

575 return 

576 

577 if not self.try_none(node.msg): 577 ↛ 580line 577 didn't jump to line 580, because the condition on line 577 was never false

578 self.minimize(node.msg) 

579 

580 if self.try_only_minimize(node, node.test): 580 ↛ 581line 580 didn't jump to line 581, because the condition on line 580 was never true

581 return 

582 

583 elif isinstance(node, (ast.Import, ast.ImportFrom)): 583 ↛ 584line 583 didn't jump to line 584, because the condition on line 583 was never true

584 self.minimize_list(node.names, lambda e: None, 1) 

585 

586 elif isinstance(node, (ast.Global, ast.Nonlocal)): 

587 self.minimize_list(node.names) 

588 

589 elif isinstance(node, ast.Expr): 

590 self.minimize_expr(node.value) 

591 

592 elif isinstance(node, ast.Module): 

593 self.minimize(node.body) 

594 if sys.version_info >= (3, 8): 594 ↛ exitline 594 didn't return from function 'minimize_stmt', because the condition on line 594 was never false

595 self.minimize_list(node.type_ignores, lambda e: None) 595 ↛ exitline 595 didn't run the lambda on line 595

596 elif sys.version_info >= (3, 12) and isinstance(node, ast.TypeAlias): 596 ↛ 609line 596 didn't jump to line 609, because the condition on line 596 was never false

597 for p in node.type_params: 

598 if ( 598 ↛ 597line 598 didn't jump to line 597

599 isinstance(p, ast.TypeVar) 

600 and p.bound is not None 

601 and self.try_only(node, p.bound) 

602 ): 

603 self.minimize(p.bound) 

604 return 

605 if self.try_only_minimize(node, node.name, node.value): 605 ↛ 607line 605 didn't jump to line 607, because the condition on line 605 was never false

606 return 

607 self.minimize_list(node.type_params, self.minimize_type_param) 

608 

609 elif isinstance(node, ast.Pass): 

610 pass 

611 else: 

612 raise TypeError(node) # Stmt 

613 

614 def minimize_type_param(self, node): 

615 assert sys.version_info >= (3, 12) 

616 if isinstance(node, ast.TypeVar): 

617 self.minimize_optional(node.bound) 

618 

619 def minimize_lists(self, lists, terminals=None, minimal=0): 

620 if terminals is None: 

621 terminals = [self.minimize for _ in lists] 

622 

623 lists = list(zip(*lists)) 

624 max_remove = len(lists) - minimal 

625 

626 import itertools 

627 

628 def try_without(l): 

629 return self.try_without(itertools.chain.from_iterable(l)) 

630 

631 def wo(l): 

632 nonlocal max_remove 

633 

634 if max_remove < len(l) or not try_without(l): 

635 devide(l) 

636 else: 

637 max_remove -= len(l) 

638 

639 def devide(l): 

640 nonlocal max_remove, remaining 

641 if not l: 

642 return 

643 

644 if len(l) == 1: 

645 if max_remove >= 1 and try_without(l): 645 ↛ 646line 645 didn't jump to line 646, because the condition on line 645 was never true

646 max_remove -= 1 

647 else: 

648 remaining.append(l[0]) 

649 else: 

650 mid = len(l) // 2 

651 

652 # remove in reverse order 

653 # this is a good heuristic, because it removes the usage before the definition 

654 wo(l[mid:]) 

655 wo(l[:mid]) 

656 

657 remaining = [] 

658 devide(lists) 

659 

660 for nodes in remaining: 

661 for terminal, node in zip(terminals, nodes): 

662 terminal(node) 

663 

664 return remaining 

665 

666 def minimize_list(self, stmts, terminal=None, minimal=0): 

667 if terminal is None: 

668 terminal = self.minimize 

669 

670 # result= self.minimize_lists((stmts,),(terminal,),minimal=0) 

671 # return [e[0] for e in result] 

672 

673 stmts = list(stmts) 

674 max_remove = len(stmts) - minimal 

675 

676 def wo(l): 

677 nonlocal max_remove 

678 

679 if max_remove < len(l) or not self.try_without(l): 

680 devide(l) 

681 else: 

682 max_remove -= len(l) 

683 

684 def devide(l): 

685 nonlocal max_remove, remaining 

686 if not l: 

687 return 

688 

689 if len(l) == 1: 

690 if max_remove >= 1 and self.try_without(l): 

691 max_remove -= 1 

692 else: 

693 remaining.append(l[0]) 

694 else: 

695 mid = len(l) // 2 

696 

697 # remove in reverse order 

698 # this is a good heuristic, because it removes the usage before the definition 

699 wo(l[mid:]) 

700 wo(l[:mid]) 

701 

702 remaining = [] 

703 devide(stmts) 

704 

705 for node in remaining: 

706 terminal(node) 

707 

708 return remaining