lambda_expressions.py 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  2. # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
  3. # Copyright (c) https://github.com/pylint-dev/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. match node:
  34. case nodes.Assign(
  35. targets=[nodes.AssignName(), *_], value=nodes.Lambda() as value
  36. ):
  37. self.add_message(
  38. "unnecessary-lambda-assignment",
  39. node=value,
  40. confidence=HIGH,
  41. )
  42. case nodes.Assign(
  43. targets=[nodes.Tuple() as target, *_],
  44. value=nodes.Tuple() | nodes.List() as value,
  45. ):
  46. # Iterate over tuple unpacking assignment elements and
  47. # see if any lambdas are assigned to a variable.
  48. # N.B. We may encounter W0632 (unbalanced-tuple-unpacking)
  49. # and still need to flag the lambdas that are being assigned.
  50. for lhs_elem, rhs_elem in zip_longest(target.elts, value.elts):
  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. match node:
  64. case nodes.NamedExpr(
  65. target=nodes.AssignName(), value=nodes.Lambda() as value
  66. ):
  67. self.add_message(
  68. "unnecessary-lambda-assignment",
  69. node=value,
  70. confidence=HIGH,
  71. )
  72. def visit_call(self, node: nodes.Call) -> None:
  73. """Check if lambda expression is called directly."""
  74. if isinstance(node.func, nodes.Lambda):
  75. self.add_message(
  76. "unnecessary-direct-lambda-call",
  77. node=node,
  78. confidence=HIGH,
  79. )
  80. def register(linter: PyLinter) -> None:
  81. linter.register_checker(LambdaExpressionChecker(linter))