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
« 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
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("\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
43 assert stderr == normalize_output(result.stderr)
44 assert stdout == normalize_output(result.stdout)
46 with TemporaryDirectory() as d:
47 d = Path(d)
49 test(d / "original", code)
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:")
57 if sys.version_info >= (3, 9):
58 # unparse does not produce the same code for 3.8
59 assert new_code == transformed_code
61 test(d / "transformed", new_code)
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 )
95def test_import_from():
96 check_transform(
97 """
98from bar.foo import a
100print(a)
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 )
120def test_function_lazy():
121 check_transform(
122 """
123from bar.foo import a
125def f():
126 return a
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')
136def f():
137 return a._lazy_value
138print(f())\
139"""
140 ),
141 snapshot(
142 """\
143bar.foo.a
144"""
145 ),
146 snapshot(""),
147 )
150def test_function_override():
151 check_transform(
152 """
153from bar.foo import a
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')
166def f():
167 a = 5
168 return a
169print(f())\
170"""
171 ),
172 snapshot(
173 """\
1745
175"""
176 ),
177 snapshot(""),
178 )
181def test_function_override_global():
182 check_transform(
183 """
184from bar.foo import a
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')
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 )
214def test_function_arg():
215 check_transform(
216 """
217from bar.foo import a
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')
229def f(a=5):
230 return a
231print(f())\
232"""
233 ),
234 snapshot(
235 """\
2365
237"""
238 ),
239 snapshot(""),
240 )
243def test_function_default_arg():
244 check_transform(
245 """
246from bar.foo import a
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')
258def f(b=a._lazy_value):
259 return b
260print(f())\
261"""
262 ),
263 snapshot(
264 """\
265bar.foo.a
266"""
267 ),
268 snapshot(""),
269 )
272def test_globals():
273 check_transform(
274 """
275from bar.foo import a
277for e in sorted(globals().items()):
278 if e[0]!="__file__":
279 print(*e)
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 )
309def test_import():
310 check_transform(
311 """
312import bar
313print(bar.foo)
314import bar.foo
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 )
337 check_transform(
338 """
339import bar.foo
340import bar
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 )
362def test_import_as():
363 check_transform(
364 """
365import bar.foo as f
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 )
386def test_lambda():
387 check_transform(
388 """
389import bar.foo as f
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 )
410def test_async_function():
411 check_transform(
412 """
413import bar.foo as f
415async def foo():
416 print(f.a)
418import asyncio
420asyncio.run(foo())
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')
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 )
444def test_import_from_future():
445 check_transform(
446 """
447"doc string"
448from __future__ import annotations
449import bar.foo as f
451print(f.a)
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 )
473def test_transform_default_argument():
474 check_transform(
475 """
476"doc string"
477from __future__ import annotations
478import bar.foo as f
480def foo(a=lambda:f.a):
481 print(a())
482foo()
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')
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 )
507def test_transform_decorators():
508 check_transform(
509 """
510"doc string"
511from __future__ import annotations
512import bar.foo as f
514def deco(thing):
515 def w(f):
516 print("in w",thing.a)
517 return f
518 return w
521@deco(f)
522def foo():
523 print("in f",f.a)
525print("call")
527foo()
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')
538def deco(thing):
540 def w(f):
541 print('in w', thing.a)
542 return f
543 return w
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 )