Skip to content

Commit b941336

Browse files
jacobtylerwallsPierre-Sassoulas
authored andcommitted
logging
1 parent cc1e6f6 commit b941336

File tree

5 files changed

+25
-27
lines changed

5 files changed

+25
-27
lines changed

ChangeLog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ Release date: TBA
1515

1616
Closes #1512
1717

18+
* Capture and log messages emitted by C extensions when importing them.
19+
This prevents contaminating programmatic output, e.g. pylint's JSON reporter.
20+
1821
* Pipe stdout to stderr when importing C modules. This prevents contaminating programmatic output,
1922
e.g. pylint's JSON reporter.
2023

astroid/manager.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
from __future__ import annotations
1111

1212
import collections
13+
import io
14+
import logging
1315
import os
1416
import types
1517
import zipimport
18+
from contextlib import redirect_stderr, redirect_stdout
1619
from importlib.util import find_spec, module_from_spec
1720
from typing import TYPE_CHECKING, ClassVar
1821

@@ -39,6 +42,9 @@
3942
ZIP_IMPORT_EXTS = (".zip", ".egg", ".whl", ".pyz", ".pyzw")
4043

4144

45+
logger = logging.getLogger(__name__)
46+
47+
4248
def safe_repr(obj):
4349
try:
4450
return repr(obj)
@@ -173,13 +179,27 @@ def ast_from_module_name(self, modname, context_file=None):
173179
):
174180
return self._build_stub_module(modname)
175181
try:
176-
module = load_module_from_name(modname)
182+
# Capture and log anything emitted during import to avoid
183+
# contaminating JSON reports in pylint
184+
with redirect_stderr(io.StringIO()) as stderr, redirect_stdout(
185+
io.StringIO()
186+
) as stdout:
187+
module = load_module_from_name(modname)
177188
except Exception as e:
178189
raise AstroidImportError(
179190
"Loading {modname} failed with:\n{error}",
180191
modname=modname,
181192
path=found_spec.location,
182193
) from e
194+
if stderr.getvalue():
195+
logger.error(
196+
f"Captured stderr from importing {modname}:\n{stdout.getvalue()}"
197+
)
198+
if stdout.getvalue():
199+
logger.info(
200+
f"Captured stdout from importing {modname}:\n{stdout.getvalue()}"
201+
)
202+
183203
return self.ast_from_module(module, modname)
184204

185205
elif found_spec.type == spec.ModuleType.PY_COMPILED:

astroid/modutils.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,15 +171,7 @@ def load_module_from_name(dotted_name: str) -> types.ModuleType:
171171
except KeyError:
172172
pass
173173

174-
# Pipe stdout to stderr to avoid contaminating programmatic output,
175-
# e.g. JSON reports in pylint
176-
original_stdout = sys.stdout
177-
sys.stdout = sys.stderr
178-
try:
179-
module = importlib.import_module(dotted_name)
180-
finally:
181-
sys.stdout = original_stdout
182-
return module
174+
return importlib.import_module(dotted_name)
183175

184176

185177
def load_module_from_modpath(parts):

requirements_test.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ coveralls~=3.3
55
coverage~=6.3.3
66
pre-commit~=2.19
77
pytest-cov~=3.0
8-
contributors-txt>=0.7.3
98
tbump~=6.9.0
109
types-typed-ast; implementation_name=="cpython" and python_version<"3.8"
1110
types-pkg_resources==0.1.3

tests/unittest_modutils.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
from xml import etree
1717
from xml.etree import ElementTree
1818

19-
from pytest import CaptureFixture
20-
2119
import astroid
2220
from astroid import modutils
2321
from astroid.interpreter._import import spec
@@ -368,20 +366,6 @@ def test_load_module_set_attribute(self) -> None:
368366
self.assertTrue(m is xml.etree.ElementTree)
369367

370368

371-
def test_stdout_piped_to_stderr_during_c_module_import(capsys: CaptureFixture) -> None:
372-
del xml.etree.ElementTree
373-
del sys.modules["xml.etree.ElementTree"]
374-
375-
with unittest.mock.patch("importlib.import_module", side_effect=print):
376-
# This string argument will also be sent to the print() side_effect
377-
modutils.load_module_from_name("xml.etree.ElementTree")
378-
379-
outerr = capsys.readouterr()
380-
assert outerr.out == ""
381-
# Message is preserved in stderr in case useful
382-
assert outerr.err.strip() == "xml.etree.ElementTree"
383-
384-
385369
class ExtensionPackageWhitelistTest(unittest.TestCase):
386370
def test_is_module_name_part_of_extension_package_whitelist_true(self) -> None:
387371
"""Test that the is_module_name_part_of_extension_package_whitelist function returns True when needed"""

0 commit comments

Comments
 (0)