Coverage for tests/test_transformer.py: 100%

68 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-11 15:02 +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("\r\n", "\n") 

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

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

41 return text 

42 

43 assert stderr == normalize_output(result.stderr) 

44 assert stdout == normalize_output(result.stdout) 

45 

46 with TemporaryDirectory() as d: 

47 d = Path(d) 

48 

49 test(d / "original", code) 

50 

51 transformer = TransformModuleImports() 

52 tree = ast.parse(code) 

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

54 new_code = unparse(new_tree) 

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

56 

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

58 # unparse does not produce the same code for 3.8 

59 assert new_code == transformed_code 

60 

61 test(d / "transformed", new_code) 

62 

63 

64def test_transform_module_imports(): 

65 check_transform( 

66 """ 

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

68import bar as baz 

69import bar.foo as f 

70import bar 

71if True: 

72 from x import y 

73 import z 

74 """, 

75 snapshot( 

76 """\ 

77import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

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

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

82baz = __lazy_imports_lite__.ImportAs('bar') 

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

84bar = __lazy_imports_lite__.Import('bar') 

85if True: 

86 from x import y 

87 import z\ 

88""" 

89 ), 

90 snapshot(""), 

91 snapshot(""), 

92 ) 

93 

94 

95def test_import_from(): 

96 check_transform( 

97 """ 

98from bar.foo import a 

99 

100print(a) 

101 

102 """, 

103 snapshot( 

104 """\ 

105import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

108print(a._lazy_value)\ 

109""" 

110 ), 

111 snapshot( 

112 """\ 

113bar.foo.a 

114""" 

115 ), 

116 snapshot(""), 

117 ) 

118 

119 

120def test_function_lazy(): 

121 check_transform( 

122 """ 

123from bar.foo import a 

124 

125def f(): 

126 return a 

127 

128print(f()) 

129 """, 

130 snapshot( 

131 """\ 

132import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

135 

136def f(): 

137 return a._lazy_value 

138print(f())\ 

139""" 

140 ), 

141 snapshot( 

142 """\ 

143bar.foo.a 

144""" 

145 ), 

146 snapshot(""), 

147 ) 

148 

149 

150def test_function_override(): 

151 check_transform( 

152 """ 

153from bar.foo import a 

154 

155def f(): 

156 a=5 

157 return a 

158print(f()) 

159 """, 

160 snapshot( 

161 """\ 

162import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

165 

166def f(): 

167 a = 5 

168 return a 

169print(f())\ 

170""" 

171 ), 

172 snapshot( 

173 """\ 

1745 

175""" 

176 ), 

177 snapshot(""), 

178 ) 

179 

180 

181def test_function_override_global(): 

182 check_transform( 

183 """ 

184from bar.foo import a 

185 

186def f(): 

187 global a 

188 a=5 

189 return a 

190print(f()) 

191 """, 

192 snapshot( 

193 """\ 

194import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

197 

198def f(): 

199 global a 

200 a._lazy_value = 5 

201 return a._lazy_value 

202print(f())\ 

203""" 

204 ), 

205 snapshot( 

206 """\ 

2075 

208""" 

209 ), 

210 snapshot(""), 

211 ) 

212 

213 

214def test_function_arg(): 

215 check_transform( 

216 """ 

217from bar.foo import a 

218 

219def f(a=5): 

220 return a 

221print(f()) 

222 """, 

223 snapshot( 

224 """\ 

225import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

228 

229def f(a=5): 

230 return a 

231print(f())\ 

232""" 

233 ), 

234 snapshot( 

235 """\ 

2365 

237""" 

238 ), 

239 snapshot(""), 

240 ) 

241 

242 

243def test_function_default_arg(): 

244 check_transform( 

245 """ 

246from bar.foo import a 

247 

248def f(b=a): 

249 return b 

250print(f()) 

251 """, 

252 snapshot( 

253 """\ 

254import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

257 

258def f(b=a._lazy_value): 

259 return b 

260print(f())\ 

261""" 

262 ), 

263 snapshot( 

264 """\ 

265bar.foo.a 

266""" 

267 ), 

268 snapshot(""), 

269 ) 

270 

271 

272def test_globals(): 

273 check_transform( 

274 """ 

275from bar.foo import a 

276 

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

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

279 print(*e) 

280 

281 """, 

282 snapshot( 

283 """\ 

284import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

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

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

289 print(*e)\ 

290""" 

291 ), 

292 snapshot( 

293 """\ 

294__annotations__ {} 

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

296__cached__ None 

297__doc__ None 

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

299__name__ __main__ 

300__package__ None 

301__spec__ None 

302a bar.foo.a 

303""" 

304 ), 

305 snapshot(""), 

306 ) 

307 

308 

309def test_import(): 

310 check_transform( 

311 """ 

312import bar 

313print(bar.foo) 

314import bar.foo 

315 

316print(bar.foo.a) 

317 """, 

318 snapshot( 

319 """\ 

320import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

322bar = __lazy_imports_lite__.Import('bar') 

323print(bar._lazy_value.foo) 

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

325print(bar._lazy_value.foo.a)\ 

326""" 

327 ), 

328 snapshot( 

329 """\ 

330bar.foo 

331bar.foo.a 

332""" 

333 ), 

334 snapshot(""), 

335 ) 

336 

337 check_transform( 

338 """ 

339import bar.foo 

340import bar 

341 

342print(bar.foo.a) 

343 """, 

344 snapshot( 

345 """\ 

346import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

349bar = __lazy_imports_lite__.Import('bar') 

350print(bar._lazy_value.foo.a)\ 

351""" 

352 ), 

353 snapshot( 

354 """\ 

355bar.foo.a 

356""" 

357 ), 

358 snapshot(""), 

359 ) 

360 

361 

362def test_import_as(): 

363 check_transform( 

364 """ 

365import bar.foo as f 

366 

367print(f.a) 

368 """, 

369 snapshot( 

370 """\ 

371import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

374print(f._lazy_value.a)\ 

375""" 

376 ), 

377 snapshot( 

378 """\ 

379bar.foo.a 

380""" 

381 ), 

382 snapshot(""), 

383 ) 

384 

385 

386def test_lambda(): 

387 check_transform( 

388 """ 

389import bar.foo as f 

390 

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

392 """, 

393 snapshot( 

394 """\ 

395import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

398print((lambda: f._lazy_value.a)())\ 

399""" 

400 ), 

401 snapshot( 

402 """\ 

403bar.foo.a 

404""" 

405 ), 

406 snapshot(""), 

407 ) 

408 

409 

410def test_async_function(): 

411 check_transform( 

412 """ 

413import bar.foo as f 

414 

415async def foo(): 

416 print(f.a) 

417 

418import asyncio 

419 

420asyncio.run(foo()) 

421 

422 """, 

423 snapshot( 

424 """\ 

425import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

428 

429async def foo(): 

430 print(f._lazy_value.a) 

431asyncio = __lazy_imports_lite__.Import('asyncio') 

432asyncio._lazy_value.run(foo())\ 

433""" 

434 ), 

435 snapshot( 

436 """\ 

437bar.foo.a 

438""" 

439 ), 

440 snapshot(""), 

441 ) 

442 

443 

444def test_import_from_future(): 

445 check_transform( 

446 """ 

447"doc string" 

448from __future__ import annotations 

449import bar.foo as f 

450 

451print(f.a) 

452 

453 """, 

454 snapshot( 

455 '''\ 

456"""doc string""" 

457from __future__ import annotations 

458import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

461print(f._lazy_value.a)\ 

462''' 

463 ), 

464 snapshot( 

465 """\ 

466bar.foo.a 

467""" 

468 ), 

469 snapshot(""), 

470 ) 

471 

472 

473def test_transform_default_argument(): 

474 check_transform( 

475 """ 

476"doc string" 

477from __future__ import annotations 

478import bar.foo as f 

479 

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

481 print(a()) 

482foo() 

483 

484 """, 

485 snapshot( 

486 '''\ 

487"""doc string""" 

488from __future__ import annotations 

489import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

492 

493def foo(a=lambda: f._lazy_value.a): 

494 print(a()) 

495foo()\ 

496''' 

497 ), 

498 snapshot( 

499 """\ 

500bar.foo.a 

501""" 

502 ), 

503 snapshot(""), 

504 ) 

505 

506 

507def test_transform_decorators(): 

508 check_transform( 

509 """ 

510"doc string" 

511from __future__ import annotations 

512import bar.foo as f 

513 

514def deco(thing): 

515 def w(f): 

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

517 return f 

518 return w 

519 

520 

521@deco(f) 

522def foo(): 

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

524 

525print("call") 

526 

527foo() 

528 

529 """, 

530 snapshot( 

531 '''\ 

532"""doc string""" 

533from __future__ import annotations 

534import lazy_imports_lite._hooks as __lazy_imports_lite__ 

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

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

537 

538def deco(thing): 

539 

540 def w(f): 

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

542 return f 

543 return w 

544 

545@deco(f._lazy_value) 

546def foo(): 

547 print('in f', f._lazy_value.a) 

548print('call') 

549foo()\ 

550''' 

551 ), 

552 snapshot( 

553 """\ 

554in w bar.foo.a 

555call 

556in f bar.foo.a 

557""" 

558 ), 

559 snapshot(""), 

560 )