blob: d206022b8fff356ba7212bb0b1901541957feb0f [file] [log] [blame]
"""
'tests.testdata.python3.data.fake_module_with_warnings' and
'tests.testdata.python3.data.fake_module_with_warnings' are fake modules
to simulate issues in unittest below
"""
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
import logging
import os
import sys
import types
import unittest
from typing import Any
from unittest import mock
import _io
import pytest
import tests.testdata.python3.data.fake_module_with_broken_getattr as fm_getattr
import tests.testdata.python3.data.fake_module_with_warnings as fm
from astroid.builder import AstroidBuilder
from astroid.const import IS_PYPY, PY312_PLUS
from astroid.raw_building import (
attach_dummy_node,
build_class,
build_from_import,
build_function,
build_module,
)
class RawBuildingTC(unittest.TestCase):
def test_attach_dummy_node(self) -> None:
node = build_module("MyModule")
attach_dummy_node(node, "DummyNode")
self.assertEqual(1, len(list(node.get_children())))
def test_build_module(self) -> None:
node = build_module("MyModule")
self.assertEqual(node.name, "MyModule")
self.assertEqual(node.pure_python, False)
self.assertEqual(node.package, False)
self.assertEqual(node.parent, None)
def test_build_class(self) -> None:
node = build_class("MyClass")
self.assertEqual(node.name, "MyClass")
self.assertEqual(node.doc_node, None)
def test_build_function(self) -> None:
node = build_function("MyFunction")
self.assertEqual(node.name, "MyFunction")
self.assertEqual(node.doc_node, None)
def test_build_function_args(self) -> None:
args = ["myArgs1", "myArgs2"]
node = build_function("MyFunction", args)
self.assertEqual("myArgs1", node.args.args[0].name)
self.assertEqual("myArgs2", node.args.args[1].name)
self.assertEqual(2, len(node.args.args))
def test_build_function_defaults(self) -> None:
defaults = ["defaults1", "defaults2"]
node = build_function(name="MyFunction", args=None, defaults=defaults)
self.assertEqual(2, len(node.args.defaults))
def test_build_function_posonlyargs(self) -> None:
node = build_function(name="MyFunction", posonlyargs=["a", "b"])
self.assertEqual(2, len(node.args.posonlyargs))
def test_build_function_kwonlyargs(self) -> None:
node = build_function(name="MyFunction", kwonlyargs=["a", "b"])
assert len(node.args.kwonlyargs) == 2
assert node.args.kwonlyargs[0].name == "a"
assert node.args.kwonlyargs[1].name == "b"
def test_build_from_import(self) -> None:
names = ["exceptions, inference, inspector"]
node = build_from_import("astroid", names)
self.assertEqual(len(names), len(node.names))
@unittest.skipIf(IS_PYPY, "Only affects CPython")
def test_io_is__io(self):
# _io module calls itself io before Python 3.12. This leads
# to cyclic dependencies when astroid tries to resolve
# what io.BufferedReader is. The code that handles this
# is in astroid.raw_building.imported_member, which verifies
# the true name of the module.
builder = AstroidBuilder()
module = builder.inspect_build(_io)
buffered_reader = module.getattr("BufferedReader")[0]
expected = "_io" if PY312_PLUS else "io"
self.assertEqual(buffered_reader.root().name, expected)
def test_build_function_deepinspect_deprecation(self) -> None:
# Tests https://github.com/pylint-dev/astroid/issues/1717
# When astroid deep inspection of modules raises
# attribute errors when getting all attributes
# Create a mock module to simulate a Cython module
m = types.ModuleType("test")
# Attach a mock of pandas with the same behavior
m.pd = fm
# This should not raise an exception
AstroidBuilder().module_build(m, "test")
def test_module_object_with_broken_getattr(self) -> None:
# Tests https://github.com/pylint-dev/astroid/issues/1958
# When astroid deep inspection of modules raises
# errors when using hasattr().
# This should not raise an exception
AstroidBuilder().inspect_build(fm_getattr, "test")
@pytest.mark.skipif(
"posix" not in sys.builtin_module_names, reason="Platform doesn't support posix"
)
def test_build_module_getattr_catch_output(
capsys: pytest.CaptureFixture[str],
caplog: pytest.LogCaptureFixture,
) -> None:
"""Catch stdout and stderr in module __getattr__ calls when building a module.
Usually raised by DeprecationWarning or FutureWarning.
"""
caplog.set_level(logging.INFO)
original_sys = sys.modules
original_module = sys.modules["posix"]
expected_out = "INFO (TEST): Welcome to posix!"
expected_err = "WARNING (TEST): Monkey-patched version of posix - module getattr"
class CustomGetattr:
def __getattr__(self, name: str) -> Any:
print(f"{expected_out}")
print(expected_err, file=sys.stderr)
return getattr(original_module, name)
def mocked_sys_modules_getitem(name: str) -> types.ModuleType | CustomGetattr:
if name != "posix":
return original_sys[name]
return CustomGetattr()
with mock.patch("astroid.raw_building.sys.modules") as sys_mock:
sys_mock.__getitem__.side_effect = mocked_sys_modules_getitem
builder = AstroidBuilder()
builder.inspect_build(os)
out, err = capsys.readouterr()
assert expected_out in caplog.text
assert expected_err in caplog.text
assert not out
assert not err