brain_statistics.py 2.7 KB

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