| # 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 |
| |
| """Astroid hooks for understanding statistics library module. |
| |
| Provides inference improvements for statistics module functions that have |
| complex runtime behavior difficult to analyze statically. |
| """ |
| |
| from __future__ import annotations |
| |
| from collections.abc import Iterator |
| from typing import TYPE_CHECKING |
| |
| from astroid import nodes |
| from astroid.context import InferenceContext |
| from astroid.inference_tip import inference_tip |
| from astroid.manager import AstroidManager |
| from astroid.util import Uninferable |
| |
| if TYPE_CHECKING: |
| from astroid.typing import InferenceResult |
| |
| |
| def _looks_like_statistics_quantiles(node: nodes.Call) -> bool: |
| """Check if this is a call to statistics.quantiles.""" |
| match node.func: |
| case nodes.Attribute(expr=nodes.Name(name="statistics"), attrname="quantiles"): |
| # Case 1: statistics.quantiles(...) |
| return True |
| case nodes.Name(name="quantiles"): |
| # Case 2: from statistics import quantiles; quantiles(...) |
| # Check if quantiles was imported from statistics |
| try: |
| frame = node.frame() |
| if "quantiles" in frame.locals: |
| # Look for import from statistics |
| for stmt in frame.body: |
| if ( |
| isinstance(stmt, nodes.ImportFrom) |
| and stmt.modname == "statistics" |
| and any(name[0] == "quantiles" for name in stmt.names or []) |
| ): |
| return True |
| except (AttributeError, TypeError): |
| # If we can't determine the import context, be conservative |
| pass |
| return False |
| |
| |
| def infer_statistics_quantiles( |
| node: nodes.Call, context: InferenceContext | None = None |
| ) -> Iterator[InferenceResult]: |
| """Infer the result of statistics.quantiles() calls. |
| |
| Returns Uninferable because quantiles() has complex runtime behavior |
| that cannot be statically analyzed, preventing false positives in |
| pylint's unbalanced-tuple-unpacking checker. |
| |
| statistics.quantiles() returns a list with (n-1) elements, but static |
| analysis sees only the empty list initializations in the function body. |
| """ |
| yield Uninferable |
| |
| |
| def register(manager: AstroidManager) -> None: |
| """Register statistics-specific inference improvements.""" |
| manager.register_transform( |
| nodes.Call, |
| inference_tip(infer_statistics_quantiles), |
| _looks_like_statistics_quantiles, |
| ) |