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
« 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
8from inline_snapshot import snapshot
9from lazy_imports_lite._transformer import TransformModuleImports
10from lazy_imports_lite._utils import unparse
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 }
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)
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
42 assert stderr == normalize_output(result.stderr)
43 assert stdout == normalize_output(result.stdout)
45 with TemporaryDirectory() as d:
46 d = Path(d)
48 test(d / "original", code)
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:")
56 if sys.version_info >= (3, 9):
57 # unparse does not produce the same code for 3.8
58 assert new_code == transformed_code
60 test(d / "transformed", new_code)
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 )
94def test_import_from():
95 check_transform(
96 """
97from bar.foo import a
99print(a)
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 )
119def test_function_lazy():
120 check_transform(
121 """
122from bar.foo import a
124def f():
125 return a
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')
135def f():
136 return a.v
137print(f())\
138"""
139 ),
140 snapshot(
141 """\
142bar.foo.a
143"""
144 ),
145 snapshot(""),
146 )
149def test_function_override():
150 check_transform(
151 """
152from bar.foo import a
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')
165def f():
166 a = 5
167 return a
168print(f())\
169"""
170 ),
171 snapshot(
172 """\
1735
174"""
175 ),
176 snapshot(""),
177 )
180def test_function_override_global():
181 check_transform(
182 """
183from bar.foo import a
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')
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 )
213def test_function_arg():
214 check_transform(
215 """
216from bar.foo import a
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')
228def f(a=5):
229 return a
230print(f())\
231"""
232 ),
233 snapshot(
234 """\
2355
236"""
237 ),
238 snapshot(""),
239 )
242def test_function_default_arg():
243 check_transform(
244 """
245from bar.foo import a
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')
257def f(b=a.v):
258 return b
259print(f())\
260"""
261 ),
262 snapshot(
263 """\
264bar.foo.a
265"""
266 ),
267 snapshot(""),
268 )
271def test_globals():
272 check_transform(
273 """
274from bar.foo import a
276for e in sorted(globals().items()):
277 if e[0]!="__file__":
278 print(*e)
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 )
308def test_import():
309 check_transform(
310 """
311import bar
312print(bar.foo)
313import bar.foo
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 )
336 check_transform(
337 """
338import bar.foo
339import bar
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 )
361def test_import_as():
362 check_transform(
363 """
364import bar.foo as f
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 )
385def test_lambda():
386 check_transform(
387 """
388import bar.foo as f
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 )
409def test_async_function():
410 check_transform(
411 """
412import bar.foo as f
414async def foo():
415 print(f.a)
417import asyncio
419asyncio.run(foo())
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')
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 )
443def test_import_from_future():
444 check_transform(
445 """
446"doc string"
447from __future__ import annotations
448import bar.foo as f
450print(f.a)
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 )
472def test_transform_default_argument():
473 check_transform(
474 """
475"doc string"
476from __future__ import annotations
477import bar.foo as f
479def foo(a=lambda:f.a):
480 print(a())
481foo()
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')
492def foo(a=lambda: f.v.a):
493 print(a())
494foo()\
495'''
496 ),
497 snapshot(
498 """\
499bar.foo.a
500"""
501 ),
502 snapshot(""),
503 )
506def test_transform_decorators():
507 check_transform(
508 """
509"doc string"
510from __future__ import annotations
511import bar.foo as f
513def deco(thing):
514 def w(f):
515 print("in w",thing.a)
516 return f
517 return w
520@deco(f)
521def foo():
522 print("in f",f.a)
524print("call")
526foo()
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')
537def deco(thing):
539 def w(f):
540 print('in w', thing.a)
541 return f
542 return w
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 )