lambda_expressions.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  2. # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
  3. # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
  4. from __future__ import annotations
  5. from itertools import zip_longest
  6. from typing import TYPE_CHECKING
  7. from astroid import nodes
  8. from pylint.checkers import BaseChecker
  9. from pylint.interfaces import HIGH
  10. if TYPE_CHECKING:
  11. from pylint.lint import PyLinter
  12. class LambdaExpressionChecker(BaseChecker):
  13. """Check for unnecessary usage of lambda expressions."""
  14. name = "lambda-expressions"
  15. msgs = {
  16. "C3001": (
  17. "Lambda expression assigned to a variable. "
  18. 'Define a function using the "def" keyword instead.',
  19. "unnecessary-lambda-assignment",
  20. "Used when a lambda expression is assigned to variable "
  21. 'rather than defining a standard function with the "def" keyword.',
  22. ),
  23. "C3002": (
  24. "Lambda expression called directly. Execute the expression inline instead.",
  25. "unnecessary-direct-lambda-call",
  26. "Used when a lambda expression is directly called "
  27. "rather than executing its contents inline.",
  28. ),
  29. }
  30. options = ()
  31. def visit_assign(self, node: nodes.Assign) -> None:
  32. """Check if lambda expression is assigned to a variable."""
  33. if isinstance(node.targets[0], nodes.AssignName) and isinstance(
  34. node.value, nodes.Lambda
  35. ):
  36. self.add_message(
  37. "unnecessary-lambda-assignment",
  38. node=node.value,
  39. confidence=HIGH,
  40. )
  41. elif isinstance(node.targets[0], nodes.Tuple) and isinstance(
  42. node.value, (nodes.Tuple, nodes.List)
  43. ):
  44. # Iterate over tuple unpacking assignment elements and
  45. # see if any lambdas are assigned to a variable.
  46. # N.B. We may encounter W0632 (unbalanced-tuple-unpacking)
  47. # and still need to flag the lambdas that are being assigned.
  48. for lhs_elem, rhs_elem in zip_longest(
  49. node.targets[0].elts, node.value.elts
  50. ):
  51. if lhs_elem is None or rhs_elem is None:
  52. # unbalanced tuple unpacking. stop checking.
  53. break
  54. if isinstance(lhs_elem, nodes.AssignName) and isinstance(
  55. rhs_elem, nodes.Lambda
  56. ):
  57. self.add_message(
  58. "unnecessary-lambda-assignment",
  59. node=rhs_elem,
  60. confidence=HIGH,
  61. )
  62. def visit_namedexpr(self, node: nodes.NamedExpr) -> None:
  63. if isinstance(node.target, nodes.AssignName) and isinstance(
  64. node.value, nodes.Lambda
  65. ):
  66. self.add_message(
  67. "unnecessary-lambda-assignment",
  68. node=node.value,
  69. confidence=HIGH,
  70. )
  71. def visit_call(self, node: nodes.Call) -> None:
  72. """Check if lambda expression is called directly."""
  73. if isinstance(node.func, nodes.Lambda):
  74. self.add_message(
  75. "unnecessary-direct-lambda-call",
  76. node=node,
  77. confidence=HIGH,
  78. )
  79. def register(linter: PyLinter) -> None:
  80. linter.register_checker(LambdaExpressionChecker(linter))