1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
4
5"""Astroid hooks for understanding statistics library module.
6
7Provides inference improvements for statistics module functions that have
8complex runtime behavior difficult to analyze statically.
9"""
10
11from __future__ import annotations
12
13from collections.abc import Iterator
14from typing import TYPE_CHECKING
15
16from astroid import nodes
17from astroid.context import InferenceContext
18from astroid.inference_tip import inference_tip
19from astroid.manager import AstroidManager
20from astroid.util import Uninferable
21
22if TYPE_CHECKING:
23 from astroid.typing import InferenceResult
24
25
26def _looks_like_statistics_quantiles(node: nodes.Call) -> bool:
27 """Check if this is a call to statistics.quantiles."""
28 match node.func:
29 case nodes.Attribute(expr=nodes.Name(name="statistics"), attrname="quantiles"):
30 # Case 1: statistics.quantiles(...)
31 return True
32 case nodes.Name(name="quantiles"):
33 # Case 2: from statistics import quantiles; quantiles(...)
34 # Check if quantiles was imported from statistics
35 try:
36 frame = node.frame()
37 if "quantiles" in frame.locals:
38 # Look for import from statistics
39 for stmt in frame.body:
40 if (
41 isinstance(stmt, nodes.ImportFrom)
42 and stmt.modname == "statistics"
43 and any(name[0] == "quantiles" for name in stmt.names or [])
44 ):
45 return True
46 except (AttributeError, TypeError):
47 # If we can't determine the import context, be conservative
48 pass
49 return False
50
51
52def infer_statistics_quantiles(
53 node: nodes.Call, context: InferenceContext | None = None
54) -> Iterator[InferenceResult]:
55 """Infer the result of statistics.quantiles() calls.
56
57 Returns Uninferable because quantiles() has complex runtime behavior
58 that cannot be statically analyzed, preventing false positives in
59 pylint's unbalanced-tuple-unpacking checker.
60
61 statistics.quantiles() returns a list with (n-1) elements, but static
62 analysis sees only the empty list initializations in the function body.
63 """
64 yield Uninferable
65
66
67def register(manager: AstroidManager) -> None:
68 """Register statistics-specific inference improvements."""
69 manager.register_transform(
70 nodes.Call,
71 inference_tip(infer_statistics_quantiles),
72 _looks_like_statistics_quantiles,
73 )