Coverage for tests/test_transformer.py: 100%

67 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-30 21:25 +0100

1import ast 

2import re 

3import subprocess as sp 

4import sys 

5from pathlib import Path 

6from tempfile import TemporaryDirectory 

7 

8from inline_snapshot import snapshot 

9from lazy_imports_lite._transformer import TransformModuleImports 

10from lazy_imports_lite._utils import unparse 

11 

12 

13def check_transform(code, transformed_code, stdout, stderr): 

14 content = { 

15 "bar/__init__.py": """ 

16foo='bar.foo' 

17baz='bar.baz' 

18""", 

19 "bar/foo.py": """ 

20a='bar.foo.a' 

21b='bar.foo.b' 

22c='bar.foo.c' 

23""", 

24 "x.py": "y='x.y'", 

25 "z.py": "", 

26 } 

27 

28 def test(dir: Path, code: str): 

29 for path, text in content.items(): 

30 path = dir / path 

31 path.parent.mkdir(exist_ok=True, parents=True) 

32 path.write_text(text) 

33 (dir / "script.py").write_text(code) 

34 result = sp.run([sys.executable, "script.py"], cwd=dir, capture_output=True) 

35 

36 def normalize_output(output: bytes): 

37 text = output.decode() 

38 text = text.replace(str(dir), "<dir>") 

39 text = re.sub("at 0x[0-9a-f]*>", "at <hex_value>>", text) 

40 return text 

41 

42 assert stderr == normalize_output(result.stderr) 

43 assert stdout == normalize_output(result.stdout) 

44 

45 with TemporaryDirectory() as d: 

46 d = Path(d) 

47 

48 test(d / "original", code) 

49 

50 transformer = TransformModuleImports() 

51 tree = ast.parse(code) 

52 new_tree = ast.fix_missing_locations(transformer.visit(tree)) 

53 new_code = unparse(new_tree) 

54 new_code = new_code.replace("lambda :", "lambda:") 

55 

56 if sys.version_info >= (3, 9): 

57 # unparse does not produce the same code for 3.8 

58 assert new_code == transformed_code 

59 

60 test(d / "transformed", new_code) 

61 

62 

63def test_transform_module_imports(): 

64 check_transform( 

65 """ 

66from bar.foo import a,b,c as d 

67import bar as baz 

68import bar.foo as f 

69import bar 

70if True: 

71 from x import y 

72 import z 

73 """, 

74 snapshot( 

75 """\ 

76import lazy_imports_lite._hooks as __lazy_imports_lite__ 

77globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

78a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') 

79b = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'b') 

80d = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'c') 

81baz = __lazy_imports_lite__.ImportAs('bar') 

82f = __lazy_imports_lite__.ImportAs('bar.foo') 

83bar = __lazy_imports_lite__.Import('bar') 

84if True: 

85 from x import y 

86 import z\ 

87""" 

88 ), 

89 snapshot(""), 

90 snapshot(""), 

91 ) 

92 

93 

94def test_import_from(): 

95 check_transform( 

96 """ 

97from bar.foo import a 

98 

99print(a) 

100 

101 """, 

102 snapshot( 

103 """\ 

104import lazy_imports_lite._hooks as __lazy_imports_lite__ 

105globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

106a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') 

107print(a.v)\ 

108""" 

109 ), 

110 snapshot( 

111 """\ 

112bar.foo.a 

113""" 

114 ), 

115 snapshot(""), 

116 ) 

117 

118 

119def test_function_lazy(): 

120 check_transform( 

121 """ 

122from bar.foo import a 

123 

124def f(): 

125 return a 

126 

127print(f()) 

128 """, 

129 snapshot( 

130 """\ 

131import lazy_imports_lite._hooks as __lazy_imports_lite__ 

132globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

133a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') 

134 

135def f(): 

136 return a.v 

137print(f())\ 

138""" 

139 ), 

140 snapshot( 

141 """\ 

142bar.foo.a 

143""" 

144 ), 

145 snapshot(""), 

146 ) 

147 

148 

149def test_function_override(): 

150 check_transform( 

151 """ 

152from bar.foo import a 

153 

154def f(): 

155 a=5 

156 return a 

157print(f()) 

158 """, 

159 snapshot( 

160 """\ 

161import lazy_imports_lite._hooks as __lazy_imports_lite__ 

162globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

163a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') 

164 

165def f(): 

166 a = 5 

167 return a 

168print(f())\ 

169""" 

170 ), 

171 snapshot( 

172 """\ 

1735 

174""" 

175 ), 

176 snapshot(""), 

177 ) 

178 

179 

180def test_function_override_global(): 

181 check_transform( 

182 """ 

183from bar.foo import a 

184 

185def f(): 

186 global a 

187 a=5 

188 return a 

189print(f()) 

190 """, 

191 snapshot( 

192 """\ 

193import lazy_imports_lite._hooks as __lazy_imports_lite__ 

194globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

195a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') 

196 

197def f(): 

198 global a 

199 a.v = 5 

200 return a.v 

201print(f())\ 

202""" 

203 ), 

204 snapshot( 

205 """\ 

2065 

207""" 

208 ), 

209 snapshot(""), 

210 ) 

211 

212 

213def test_function_arg(): 

214 check_transform( 

215 """ 

216from bar.foo import a 

217 

218def f(a=5): 

219 return a 

220print(f()) 

221 """, 

222 snapshot( 

223 """\ 

224import lazy_imports_lite._hooks as __lazy_imports_lite__ 

225globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

226a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') 

227 

228def f(a=5): 

229 return a 

230print(f())\ 

231""" 

232 ), 

233 snapshot( 

234 """\ 

2355 

236""" 

237 ), 

238 snapshot(""), 

239 ) 

240 

241 

242def test_function_default_arg(): 

243 check_transform( 

244 """ 

245from bar.foo import a 

246 

247def f(b=a): 

248 return b 

249print(f()) 

250 """, 

251 snapshot( 

252 """\ 

253import lazy_imports_lite._hooks as __lazy_imports_lite__ 

254globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

255a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') 

256 

257def f(b=a.v): 

258 return b 

259print(f())\ 

260""" 

261 ), 

262 snapshot( 

263 """\ 

264bar.foo.a 

265""" 

266 ), 

267 snapshot(""), 

268 ) 

269 

270 

271def test_globals(): 

272 check_transform( 

273 """ 

274from bar.foo import a 

275 

276for e in sorted(globals().items()): 

277 if e[0]!="__file__": 

278 print(*e) 

279 

280 """, 

281 snapshot( 

282 """\ 

283import lazy_imports_lite._hooks as __lazy_imports_lite__ 

284globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

285a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') 

286for e in sorted(globals().items()): 

287 if e[0] != '__file__': 

288 print(*e)\ 

289""" 

290 ), 

291 snapshot( 

292 """\ 

293__annotations__ {} 

294__builtins__ <module 'builtins' (built-in)> 

295__cached__ None 

296__doc__ None 

297__loader__ <_frozen_importlib_external.SourceFileLoader object at <hex_value>> 

298__name__ __main__ 

299__package__ None 

300__spec__ None 

301a bar.foo.a 

302""" 

303 ), 

304 snapshot(""), 

305 ) 

306 

307 

308def test_import(): 

309 check_transform( 

310 """ 

311import bar 

312print(bar.foo) 

313import bar.foo 

314 

315print(bar.foo.a) 

316 """, 

317 snapshot( 

318 """\ 

319import lazy_imports_lite._hooks as __lazy_imports_lite__ 

320globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

321bar = __lazy_imports_lite__.Import('bar') 

322print(bar.v.foo) 

323bar = __lazy_imports_lite__.Import('bar.foo') 

324print(bar.v.foo.a)\ 

325""" 

326 ), 

327 snapshot( 

328 """\ 

329bar.foo 

330bar.foo.a 

331""" 

332 ), 

333 snapshot(""), 

334 ) 

335 

336 check_transform( 

337 """ 

338import bar.foo 

339import bar 

340 

341print(bar.foo.a) 

342 """, 

343 snapshot( 

344 """\ 

345import lazy_imports_lite._hooks as __lazy_imports_lite__ 

346globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

347bar = __lazy_imports_lite__.Import('bar.foo') 

348bar = __lazy_imports_lite__.Import('bar') 

349print(bar.v.foo.a)\ 

350""" 

351 ), 

352 snapshot( 

353 """\ 

354bar.foo.a 

355""" 

356 ), 

357 snapshot(""), 

358 ) 

359 

360 

361def test_import_as(): 

362 check_transform( 

363 """ 

364import bar.foo as f 

365 

366print(f.a) 

367 """, 

368 snapshot( 

369 """\ 

370import lazy_imports_lite._hooks as __lazy_imports_lite__ 

371globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

372f = __lazy_imports_lite__.ImportAs('bar.foo') 

373print(f.v.a)\ 

374""" 

375 ), 

376 snapshot( 

377 """\ 

378bar.foo.a 

379""" 

380 ), 

381 snapshot(""), 

382 ) 

383 

384 

385def test_lambda(): 

386 check_transform( 

387 """ 

388import bar.foo as f 

389 

390print((lambda:f.a)()) 

391 """, 

392 snapshot( 

393 """\ 

394import lazy_imports_lite._hooks as __lazy_imports_lite__ 

395globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

396f = __lazy_imports_lite__.ImportAs('bar.foo') 

397print((lambda: f.v.a)())\ 

398""" 

399 ), 

400 snapshot( 

401 """\ 

402bar.foo.a 

403""" 

404 ), 

405 snapshot(""), 

406 ) 

407 

408 

409def test_async_function(): 

410 check_transform( 

411 """ 

412import bar.foo as f 

413 

414async def foo(): 

415 print(f.a) 

416 

417import asyncio 

418 

419asyncio.run(foo()) 

420 

421 """, 

422 snapshot( 

423 """\ 

424import lazy_imports_lite._hooks as __lazy_imports_lite__ 

425globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

426f = __lazy_imports_lite__.ImportAs('bar.foo') 

427 

428async def foo(): 

429 print(f.v.a) 

430asyncio = __lazy_imports_lite__.Import('asyncio') 

431asyncio.v.run(foo())\ 

432""" 

433 ), 

434 snapshot( 

435 """\ 

436bar.foo.a 

437""" 

438 ), 

439 snapshot(""), 

440 ) 

441 

442 

443def test_import_from_future(): 

444 check_transform( 

445 """ 

446"doc string" 

447from __future__ import annotations 

448import bar.foo as f 

449 

450print(f.a) 

451 

452 """, 

453 snapshot( 

454 '''\ 

455"""doc string""" 

456from __future__ import annotations 

457import lazy_imports_lite._hooks as __lazy_imports_lite__ 

458globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

459f = __lazy_imports_lite__.ImportAs('bar.foo') 

460print(f.v.a)\ 

461''' 

462 ), 

463 snapshot( 

464 """\ 

465bar.foo.a 

466""" 

467 ), 

468 snapshot(""), 

469 ) 

470 

471 

472def test_transform_default_argument(): 

473 check_transform( 

474 """ 

475"doc string" 

476from __future__ import annotations 

477import bar.foo as f 

478 

479def foo(a=lambda:f.a): 

480 print(a()) 

481foo() 

482 

483 """, 

484 snapshot( 

485 '''\ 

486"""doc string""" 

487from __future__ import annotations 

488import lazy_imports_lite._hooks as __lazy_imports_lite__ 

489globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

490f = __lazy_imports_lite__.ImportAs('bar.foo') 

491 

492def foo(a=lambda: f.v.a): 

493 print(a()) 

494foo()\ 

495''' 

496 ), 

497 snapshot( 

498 """\ 

499bar.foo.a 

500""" 

501 ), 

502 snapshot(""), 

503 ) 

504 

505 

506def test_transform_decorators(): 

507 check_transform( 

508 """ 

509"doc string" 

510from __future__ import annotations 

511import bar.foo as f 

512 

513def deco(thing): 

514 def w(f): 

515 print("in w",thing.a) 

516 return f 

517 return w 

518 

519 

520@deco(f) 

521def foo(): 

522 print("in f",f.a) 

523 

524print("call") 

525 

526foo() 

527 

528 """, 

529 snapshot( 

530 '''\ 

531"""doc string""" 

532from __future__ import annotations 

533import lazy_imports_lite._hooks as __lazy_imports_lite__ 

534globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) 

535f = __lazy_imports_lite__.ImportAs('bar.foo') 

536 

537def deco(thing): 

538 

539 def w(f): 

540 print('in w', thing.a) 

541 return f 

542 return w 

543 

544@deco(f.v) 

545def foo(): 

546 print('in f', f.v.a) 

547print('call') 

548foo()\ 

549''' 

550 ), 

551 snapshot( 

552 """\ 

553in w bar.foo.a 

554call 

555in f bar.foo.a 

556""" 

557 ), 

558 snapshot(""), 

559 )