Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions tests/test_apt_pkg_not_iterable_7122.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Diagnostic for https://github.com/pylint-dev/pylint/issues/7122

``src_records.binaries`` and ``fetcher.items`` from ``apt_pkg`` get a
false ``not-an-iterable`` (E1133) even though the stubs declare them as
``List[str]``. This test asserts the FP is **present** today: when CI is
red, the bug is still real. Linux-only since apt_pkg is Debian/Ubuntu.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path

import pytest

pytest.importorskip("apt", reason="python3-apt is required to reproduce #7122")
pytest.importorskip("apt_pkg", reason="python3-apt is required to reproduce #7122")


def test_apt_pkg_iterable_attributes_trigger_not_an_iterable(tmp_path: Path) -> None:
"""``apt_pkg`` attributes typed as List in stubs still trip not-an-iterable."""
(tmp_path / "example.py").write_text(
"#!/usr/bin/python3\n"
"# pylint: disable=missing-docstring\n"
"\n"
"import os\n"
"import apt\n"
"\n"
"src_records = apt.apt_pkg.SourceRecords()\n"
'src_records.lookup("bash")\n'
'pkgs = [p for p in src_records.binaries if not p.endswith("-doc")]\n'
"print(pkgs)\n"
"\n"
"fetcher = apt.apt_pkg.Acquire(apt.progress.text.AcquireProgress())\n"
"cache = apt.Cache(rootdir=os.getcwd())\n"
"cache.fetch_archives(fetcher=fetcher)\n"
"for i in fetcher.items:\n"
" print(i)\n"
)

process = subprocess.run(
[
sys.executable,
"-m",
"pylint",
"--extension-pkg-allow-list=apt_pkg",
"--disable=all",
"--enable=not-an-iterable",
"example.py",
],
cwd=tmp_path,
capture_output=True,
text=True,
check=False,
)
output = process.stdout + process.stderr
# "Bug still real" diagnostic: assert the FP IS present.
assert "not-an-iterable" in output, (
f"#7122 appears to be fixed on {sys.platform}. "
f"Promote this test to assert the FP is ABSENT and close. Output: {output!r}"
)
65 changes: 65 additions & 0 deletions tests/test_argparse_subclass_4667.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

Check notice on line 3 in tests/test_argparse_subclass_4667.py

View workflow job for this annotation

GitHub Actions / pylint

C0402

Wrong spelling of a word 'Subclassing' in a docstring:

"""Diagnostic for https://github.com/pylint-dev/pylint/issues/4667

Subclassing ``argparse.ArgumentParser`` once made pylint emit ``no-member``
on ``parsed_args.logdir`` (Py3.9-specific at the time). Pylint no longer
supports Py3.9, but the inference of ``Namespace`` attributes is general
enough that we verify the FP stays gone on every supported interpreter.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path


def test_argparse_subclass_no_member_absent(tmp_path: Path) -> None:
"""Subclassing ArgumentParser must not poison Namespace attribute inference."""

Check notice on line 21 in tests/test_argparse_subclass_4667.py

View workflow job for this annotation

GitHub Actions / pylint

C0402

Wrong spelling of a word 'Subclassing' in a docstring:
(tmp_path / "cli.py").write_text(
'"""CLI."""\n'
"import argparse\n"
"\n"
"\n"
"class SilentArgumentParser(argparse.ArgumentParser):\n"
' """Silent parser."""\n'
"\n"
" def error(self, message=None):\n"
" raise SystemExit(2)\n"
"\n"
" def exit(self, status=0, message=None):\n"
" raise SystemExit(status)\n"
"\n"
"\n"
"def parse_args(argv):\n"
' """Parse."""\n'
" parser = SilentArgumentParser()\n"
' parser.add_argument("--logdir", type=str, default=None)\n'
' parser.add_argument("name", type=str, default=None)\n'
" return parser.parse_args(argv)\n"
"\n"
"\n"
'args = parse_args(["foo"])\n'
"print(args.name)\n"
"print(args.logdir)\n"
)

process = subprocess.run(
[
sys.executable,
"-m",
"pylint",
"--disable=all",
"--enable=no-member",
"cli.py",
],
cwd=tmp_path,
capture_output=True,
text=True,
check=False,
)
output = process.stdout + process.stderr
assert "no-member" not in output, f"#4667 regression on {sys.version}: {output!r}"
62 changes: 62 additions & 0 deletions tests/test_azure_namespace_8980.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Diagnostic for https://github.com/pylint-dev/pylint/issues/8980

When both ``azure-monitor-opentelemetry`` and
``azure-monitor-opentelemetry-exporter`` are installed (separate
distributions sharing the ``azure.monitor.opentelemetry`` namespace),
pylint used to emit ``no-name-in-module`` for the second distribution's
names. Confirm namespace-package resolution now works.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path

import pytest

pytest.importorskip(
"azure.monitor.opentelemetry.exporter",
reason="azure-monitor-opentelemetry-exporter is required to reproduce #8980",
)


def test_azure_namespace_package_resolves(tmp_path: Path) -> None:
"""Imports from the namespace-shared exporter package must resolve."""
(tmp_path / "use.py").write_text(
'"""Use the namespace import."""\n'
"from azure.monitor.opentelemetry.exporter import (\n"
" ApplicationInsightsSampler,\n"
" AzureMonitorLogExporter,\n"
" AzureMonitorMetricExporter,\n"
" AzureMonitorTraceExporter,\n"
")\n"
"\n"
"print(\n"
" ApplicationInsightsSampler,\n"
" AzureMonitorLogExporter,\n"
" AzureMonitorMetricExporter,\n"
" AzureMonitorTraceExporter,\n"
")\n"
)

process = subprocess.run(
[
sys.executable,
"-m",
"pylint",
"--disable=all",
"--enable=no-name-in-module",
"use.py",
],
cwd=tmp_path,
capture_output=True,
text=True,
check=False,
)
output = process.stdout + process.stderr
assert "no-name-in-module" not in output, f"#8980 regression: {output!r}"
50 changes: 50 additions & 0 deletions tests/test_bson_with_options_9137.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Diagnostic for https://github.com/pylint-dev/pylint/issues/9137

Check notice on line 5 in tests/test_bson_with_options_9137.py

View workflow job for this annotation

GitHub Actions / pylint

C0402

Wrong spelling of a word 'bson' in a docstring:

``LEGACY_JSON_OPTIONS.with_options(tz_aware=True, tzinfo=...)`` used to
crash pylint with ``'UninferableBase' object is not iterable``. Confirm
inference no longer blows up when bson is available.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path

import pytest

pytest.importorskip(
"bson.json_util", reason="pymongo (bson) is required to reproduce #9137"
)
pytest.importorskip("pytz", reason="pytz is required to reproduce #9137")


def test_bson_with_options_does_not_crash(tmp_path: Path) -> None:
"""``LEGACY_JSON_OPTIONS.with_options(...)`` must not crash inference."""
(tmp_path / "json_util.py").write_text(
'"""JSON util."""\n'
"import pytz\n"
"from bson.json_util import LEGACY_JSON_OPTIONS\n"
"\n"
"CUSTOM_JSON_OPTIONS = LEGACY_JSON_OPTIONS.with_options(\n"
" tz_aware=True, tzinfo=pytz.UTC\n"
")\n"
)

process = subprocess.run(
[sys.executable, "-m", "pylint", "json_util.py"],
cwd=tmp_path,
capture_output=True,
text=True,
check=False,
)
output = process.stdout + process.stderr
assert "UninferableBase" not in output, f"#9137 regression: {output!r}"
assert "Traceback" not in output, f"#9137 regression (crash): {output!r}"
assert (
"astroid-error" not in output
), f"#9137 regression (astroid-error): {output!r}"
76 changes: 76 additions & 0 deletions tests/test_context_manager_no_member_4917.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Diagnostic for https://github.com/pylint-dev/pylint/issues/4917

A variable bound by ``with CtxMgr() as x: x = OtherClass(...)`` was once
treated as an instance of the context-manager class, producing a false
``no-member`` on ``x.method_of_other_class()``. Confirm pylint now infers
the rebinding correctly.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path


def test_with_block_rebinding_no_false_no_member(tmp_path: Path) -> None:
"""Rebinding inside a ``with`` block must not trigger no-member on the new type."""
(tmp_path / "animal_api.py").write_text(
'"""Module."""\n'
"\n"
"\n"
"class Animal:\n"
' """Animal."""\n'
' def __init__(self, name=""):\n'
" self.name = name\n"
"\n"
" def add_animal_to_db(self, _uow):\n"
' """Persist."""\n'
" return self\n"
"\n"
" def as_dict(self):\n"
' """Serialize."""\n'
' return {"name": self.name}\n'
"\n"
"\n"
"class SQLUnitOfWork:\n"
' """SQLUnitOfWork."""\n'
" def __init__(self, config):\n"
" self.config = config\n"
"\n"
" def __enter__(self):\n"
" return self\n"
"\n"
" def __exit__(self, *args):\n"
" return None\n"
"\n"
"\n"
"def handler(data, config, body):\n"
' """Handler."""\n'
" with SQLUnitOfWork(config) as uow:\n"
" animal = Animal(**data).add_animal_to_db(uow)\n"
" if not animal:\n"
' return {"status": "Incorrect values", "values": body}, 405\n'
" return animal.as_dict(), 201\n"
)

process = subprocess.run(
[
sys.executable,
"-m",
"pylint",
"--disable=all",
"--enable=no-member",
"animal_api.py",
],
cwd=tmp_path,
capture_output=True,
text=True,
check=False,
)
output = process.stdout + process.stderr
assert "no-member" not in output, f"#4917 regression: {output!r}"
63 changes: 63 additions & 0 deletions tests/test_cyclic_import_9168.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Diagnostic for https://github.com/pylint-dev/pylint/issues/9168

The reporter says `cyclic-import` is detected on Linux but not on macOS
for the same project layout. This test exercises the layout on whatever
platform the CI runs on; if the bug is real, the assertion will fail on
macOS only.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path


def test_cyclic_import_detected_in_package(tmp_path: Path) -> None:
"""Reproduce the directory layout from #9168 and assert cyclic-import fires."""
pkg = tmp_path / "module1"
pkg.mkdir()
(pkg / "__init__.py").write_text(
"from module1.base import Base\n" "from module1.derived import Derived\n"

Check warning on line 25 in tests/test_cyclic_import_9168.py

View workflow job for this annotation

GitHub Actions / pylint

W1404

Implicit string concatenation found in call
)
(pkg / "base.py").write_text(
"class Base:\n" " def __init__(self):\n" " print('hello from base')\n"

Check warning on line 28 in tests/test_cyclic_import_9168.py

View workflow job for this annotation

GitHub Actions / pylint

W1404

Implicit string concatenation found in call
)
(pkg / "derived.py").write_text(
"from module1 import Base\n"
"\n"
"class Derived(Base):\n"
" def __init__(self):\n"
" super().__init__()\n"
" print('hello from derived')\n"
)
(tmp_path / "main.py").write_text(
"from module1 import Derived\n"
"\n"
"if __name__ == '__main__':\n"
" Derived()\n"
)

process = subprocess.run(
[
sys.executable,
"-m",
"pylint",
"--recursive=y",
"--disable=W,C0114,C0115,C0116,R0903",
"-s=n",
".",
],
cwd=tmp_path,
capture_output=True,
text=True,
check=False,
)
output = process.stdout + process.stderr
assert "cyclic-import" in output, (
f"Expected cyclic-import to be reported on {sys.platform}, " f"got: {output!r}"
)
Loading
Loading