Skip to content

Commit a7d0a18

Browse files
Add regression test for changes to support hermetic interpreters (#1397)
* Add regression test for changes to support hermetic interpreters Test for changes made in 6655741. Co-authored-by: Pierre Sassoulas <[email protected]>
1 parent cfd9e74 commit a7d0a18

File tree

1 file changed

+66
-0
lines changed

1 file changed

+66
-0
lines changed

tests/unittest_builder.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@
2626
"""tests for the astroid builder and rebuilder module"""
2727

2828
import collections
29+
import importlib
2930
import os
31+
import pathlib
32+
import py_compile
3033
import socket
3134
import sys
35+
import tempfile
3236
import unittest
3337

3438
import pytest
@@ -790,5 +794,67 @@ def test_parse_module_with_invalid_type_comments_does_not_crash():
790794
assert isinstance(node, nodes.Module)
791795

792796

797+
class HermeticInterpreterTest(unittest.TestCase):
798+
"""Modeled on https://github.com/PyCQA/astroid/pull/1207#issuecomment-951455588"""
799+
800+
@classmethod
801+
def setUpClass(cls):
802+
"""Simulate a hermetic interpreter environment having no code on the filesystem."""
803+
with tempfile.TemporaryDirectory() as tmp_dir:
804+
sys.path.append(tmp_dir)
805+
806+
# Write a python file and compile it to .pyc
807+
# To make this test have even more value, we would need to come up with some
808+
# code that gets inferred differently when we get its "partial representation".
809+
# This code is too simple for that. But we can't use builtins either, because we would
810+
# have to delete builtins from the filesystem. But even if we engineered that,
811+
# the difference might evaporate over time as inference changes.
812+
cls.code_snippet = "def func(): return 42"
813+
with tempfile.NamedTemporaryFile(
814+
mode="w", dir=tmp_dir, suffix=".py", delete=False
815+
) as tmp:
816+
tmp.write(cls.code_snippet)
817+
pyc_file = py_compile.compile(tmp.name)
818+
cls.pyc_name = tmp.name.replace(".py", ".pyc")
819+
os.remove(tmp.name)
820+
os.rename(pyc_file, cls.pyc_name)
821+
822+
# Import the module
823+
cls.imported_module_path = pathlib.Path(cls.pyc_name)
824+
cls.imported_module = importlib.import_module(cls.imported_module_path.stem)
825+
826+
# Delete source code from module object, filesystem, and path
827+
del cls.imported_module.__file__
828+
os.remove(cls.imported_module_path)
829+
sys.path.remove(tmp_dir)
830+
831+
def test_build_from_live_module_without_source_file(self) -> None:
832+
"""Assert that inspect_build() is not called.
833+
See comment in module_build() before the call to inspect_build():
834+
"get a partial representation by introspection"
835+
836+
This "partial representation" was presumably causing unexpected behavior.
837+
"""
838+
# Sanity check
839+
self.assertIsNone(
840+
self.imported_module.__loader__.get_source(self.imported_module_path.stem)
841+
)
842+
with self.assertRaises(AttributeError):
843+
_ = self.imported_module.__file__
844+
845+
my_builder = builder.AstroidBuilder()
846+
with unittest.mock.patch.object(
847+
self.imported_module.__loader__,
848+
"get_source",
849+
return_value=self.code_snippet,
850+
):
851+
with unittest.mock.patch.object(
852+
my_builder, "inspect_build", side_effect=AssertionError
853+
):
854+
my_builder.module_build(
855+
self.imported_module, modname=self.imported_module_path.stem
856+
)
857+
858+
793859
if __name__ == "__main__":
794860
unittest.main()

0 commit comments

Comments
 (0)