Skip to content

Commit 95cf36e

Browse files
authored
Use match statement in checkers (3) (#10529)
1 parent 90c09a3 commit 95cf36e

File tree

8 files changed

+371
-386
lines changed

8 files changed

+371
-386
lines changed

pylint/checkers/base/basic_checker.py

Lines changed: 64 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,7 @@ def _check_using_constant_test(
367367
# Just forcing the generator to infer all elements.
368368
# astroid.exceptions.InferenceError are false positives
369369
# see https://github.com/pylint-dev/pylint/pull/8185
370-
if isinstance(inferred, nodes.FunctionDef):
371-
call_inferred = list(inferred.infer_call_result(node))
372-
elif isinstance(inferred, nodes.Lambda):
370+
if isinstance(inferred, (nodes.FunctionDef, nodes.Lambda)):
373371
call_inferred = list(inferred.infer_call_result(node))
374372
except astroid.InferenceError:
375373
call_inferred = None
@@ -711,37 +709,36 @@ def visit_call(self, node: nodes.Call) -> None:
711709
# ignore the name if it's not a builtin (i.e. not defined in the
712710
# locals nor globals scope)
713711
if not (name in node.frame() or name in node.root()):
714-
if name == "exec":
715-
self.add_message("exec-used", node=node)
716-
elif name == "reversed":
717-
self._check_reversed(node)
718-
elif name == "eval":
719-
self.add_message("eval-used", node=node)
712+
match name:
713+
case "exec":
714+
self.add_message("exec-used", node=node)
715+
case "reversed":
716+
self._check_reversed(node)
717+
case "eval":
718+
self.add_message("eval-used", node=node)
720719

721720
@utils.only_required_for_messages("assert-on-tuple", "assert-on-string-literal")
722721
def visit_assert(self, node: nodes.Assert) -> None:
723722
"""Check whether assert is used on a tuple or string literal."""
724-
if isinstance(node.test, nodes.Tuple) and len(node.test.elts) > 0:
725-
self.add_message("assert-on-tuple", node=node, confidence=HIGH)
726-
727-
if isinstance(node.test, nodes.Const) and isinstance(node.test.value, str):
728-
if node.test.value:
729-
when = "never"
730-
else:
731-
when = "always"
732-
self.add_message("assert-on-string-literal", node=node, args=(when,))
723+
match node.test:
724+
case nodes.Tuple(elts=elts) if len(elts) > 0:
725+
self.add_message("assert-on-tuple", node=node, confidence=HIGH)
726+
case nodes.Const(value=str(val)):
727+
when = "never" if val else "always"
728+
self.add_message("assert-on-string-literal", node=node, args=(when,))
733729

734730
@utils.only_required_for_messages("duplicate-key")
735731
def visit_dict(self, node: nodes.Dict) -> None:
736732
"""Check duplicate key in dictionary."""
737733
keys = set()
738734
for k, _ in node.items:
739-
if isinstance(k, nodes.Const):
740-
key = k.value
741-
elif isinstance(k, nodes.Attribute):
742-
key = k.as_string()
743-
else:
744-
continue
735+
match k:
736+
case nodes.Const():
737+
key = k.value
738+
case nodes.Attribute():
739+
key = k.as_string()
740+
case _:
741+
continue
745742
if key in keys:
746743
self.add_message("duplicate-key", node=node, args=key)
747744
keys.add(key)
@@ -827,41 +824,42 @@ def _check_reversed(self, node: nodes.Call) -> None:
827824
except utils.NoSuchArgumentError:
828825
pass
829826
else:
830-
if isinstance(argument, util.UninferableBase):
831-
return
832-
if argument is None:
833-
# Nothing was inferred.
834-
# Try to see if we have iter().
835-
if isinstance(node.args[0], nodes.Call):
836-
try:
837-
func = next(node.args[0].func.infer())
838-
except astroid.InferenceError:
839-
return
840-
if getattr(
841-
func, "name", None
842-
) == "iter" and utils.is_builtin_object(func):
843-
self.add_message("bad-reversed-sequence", node=node)
844-
return
845-
846-
if isinstance(argument, (nodes.List, nodes.Tuple)):
847-
return
827+
match argument:
828+
case util.UninferableBase():
829+
return
830+
case None:
831+
# Nothing was inferred.
832+
# Try to see if we have iter().
833+
if isinstance(node.args[0], nodes.Call):
834+
try:
835+
func = next(node.args[0].func.infer())
836+
except astroid.InferenceError:
837+
return
838+
if getattr(
839+
func, "name", None
840+
) == "iter" and utils.is_builtin_object(func):
841+
self.add_message("bad-reversed-sequence", node=node)
842+
return
848843

849-
# dicts are reversible, but only from Python 3.8 onward. Prior to
850-
# that, any class based on dict must explicitly provide a
851-
# __reversed__ method
852-
if not self._py38_plus and isinstance(argument, astroid.Instance):
853-
if any(
854-
ancestor.name == "dict" and utils.is_builtin_object(ancestor)
855-
for ancestor in itertools.chain(
856-
(argument._proxied,), argument._proxied.ancestors()
857-
)
858-
):
859-
try:
860-
argument.locals[REVERSED_PROTOCOL_METHOD]
861-
except KeyError:
862-
self.add_message("bad-reversed-sequence", node=node)
844+
case nodes.List() | nodes.Tuple():
863845
return
864846

847+
case astroid.Instance() if not self._py38_plus:
848+
# dicts are reversible, but only from Python 3.8 onward. Prior to
849+
# that, any class based on dict must explicitly provide a
850+
# __reversed__ method
851+
if any(
852+
ancestor.name == "dict" and utils.is_builtin_object(ancestor)
853+
for ancestor in itertools.chain(
854+
(argument._proxied,), argument._proxied.ancestors()
855+
)
856+
):
857+
try:
858+
argument.locals[REVERSED_PROTOCOL_METHOD]
859+
except KeyError:
860+
self.add_message("bad-reversed-sequence", node=node)
861+
return
862+
865863
if hasattr(argument, "getattr"):
866864
# everything else is not a proper sequence for reversed()
867865
for methods in REVERSED_METHODS:
@@ -910,15 +908,16 @@ def _check_self_assigning_variable(self, node: nodes.Assign) -> None:
910908
# Unpacking a variable into the same name.
911909
return
912910

913-
if isinstance(node.value, nodes.Name):
914-
if len(targets) != 1:
915-
return
916-
rhs_names = [node.value]
917-
elif isinstance(node.value, nodes.Tuple):
918-
rhs_count = len(node.value.elts)
919-
if len(targets) != rhs_count or rhs_count == 1:
920-
return
921-
rhs_names = node.value.elts
911+
match node.value:
912+
case nodes.Name():
913+
if len(targets) != 1:
914+
return
915+
rhs_names = [node.value]
916+
case nodes.Tuple():
917+
rhs_count = len(node.value.elts)
918+
if len(targets) != rhs_count or rhs_count == 1:
919+
return
920+
rhs_names = node.value.elts
922921

923922
for target, lhs_name in zip(targets, rhs_names):
924923
if not isinstance(lhs_name, nodes.Name):

pylint/checkers/base/basic_error_checker.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -218,26 +218,25 @@ def visit_classdef(self, node: nodes.ClassDef) -> None:
218218
def _too_many_starred_for_tuple(self, assign_tuple: nodes.Tuple) -> bool:
219219
starred_count = 0
220220
for elem in assign_tuple.itered():
221-
if isinstance(elem, nodes.Tuple):
222-
return self._too_many_starred_for_tuple(elem)
223-
if isinstance(elem, nodes.Starred):
224-
starred_count += 1
221+
match elem:
222+
case nodes.Tuple():
223+
return self._too_many_starred_for_tuple(elem)
224+
case nodes.Starred():
225+
starred_count += 1
225226
return starred_count > 1
226227

227228
@utils.only_required_for_messages(
228229
"too-many-star-expressions", "invalid-star-assignment-target"
229230
)
230231
def visit_assign(self, node: nodes.Assign) -> None:
231-
# Check *a, *b = ...
232-
assign_target = node.targets[0]
233-
# Check *a = b
234-
if isinstance(node.targets[0], nodes.Starred):
235-
self.add_message("invalid-star-assignment-target", node=node)
236-
237-
if not isinstance(assign_target, nodes.Tuple):
238-
return
239-
if self._too_many_starred_for_tuple(assign_target):
240-
self.add_message("too-many-star-expressions", node=node)
232+
match assign_target := node.targets[0]:
233+
case nodes.Starred():
234+
# Check *a = b
235+
self.add_message("invalid-star-assignment-target", node=node)
236+
case nodes.Tuple():
237+
# Check *a, *b = ...
238+
if self._too_many_starred_for_tuple(assign_target):
239+
self.add_message("too-many-star-expressions", node=node)
241240

242241
@utils.only_required_for_messages("star-needs-assignment-target")
243242
def visit_starred(self, node: nodes.Starred) -> None:

pylint/checkers/base/docstring_checker.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,13 @@ def _check_docstring(
191191
# Strings.
192192
if func.bound.name in {"str", "unicode", "bytes"}:
193193
return
194-
if node_type == "module":
195-
message = "missing-module-docstring"
196-
elif node_type == "class":
197-
message = "missing-class-docstring"
198-
else:
199-
message = "missing-function-docstring"
194+
match node_type:
195+
case "module":
196+
message = "missing-module-docstring"
197+
case "class":
198+
message = "missing-class-docstring"
199+
case _:
200+
message = "missing-function-docstring"
200201
self.add_message(message, node=node, confidence=confidence)
201202
elif not docstring.strip():
202203
if node_type == "class":

pylint/checkers/base/name_checker/checker.py

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -681,18 +681,19 @@ def _assigns_typealias(node: nodes.NodeNG | None) -> bool:
681681
def _check_typevar(self, name: str, node: nodes.AssignName) -> None:
682682
"""Check for TypeVar lint violations."""
683683
variance: TypeVarVariance = TypeVarVariance.invariant
684-
if isinstance(node.parent, nodes.Assign):
685-
keywords = node.assign_type().value.keywords
686-
args = node.assign_type().value.args
687-
elif isinstance(node.parent, nodes.Tuple):
688-
keywords = (
689-
node.assign_type().value.elts[node.parent.elts.index(node)].keywords
690-
)
691-
args = node.assign_type().value.elts[node.parent.elts.index(node)].args
692-
else: # PEP 695 generic type nodes
693-
keywords = ()
694-
args = ()
695-
variance = TypeVarVariance.inferred
684+
match node.parent:
685+
case nodes.Assign():
686+
keywords = node.assign_type().value.keywords
687+
args = node.assign_type().value.args
688+
case nodes.Tuple():
689+
keywords = (
690+
node.assign_type().value.elts[node.parent.elts.index(node)].keywords
691+
)
692+
args = node.assign_type().value.elts[node.parent.elts.index(node)].args
693+
case _: # PEP 695 generic type nodes
694+
keywords = ()
695+
args = ()
696+
variance = TypeVarVariance.inferred
696697

697698
name_arg = None
698699
for kw in keywords:
@@ -717,49 +718,48 @@ def _check_typevar(self, name: str, node: nodes.AssignName) -> None:
717718
if name_arg is None and args and isinstance(args[0], nodes.Const):
718719
name_arg = args[0].value
719720

720-
if variance == TypeVarVariance.inferred:
721-
# Ignore variance check for PEP 695 type parameters.
722-
# The variance is inferred by the type checker.
723-
# Adding _co or _contra suffix can help to reason about TypeVar.
724-
pass
725-
elif variance == TypeVarVariance.double_variant:
726-
self.add_message(
727-
"typevar-double-variance",
728-
node=node,
729-
confidence=interfaces.INFERENCE,
730-
)
731-
self.add_message(
732-
"typevar-name-incorrect-variance",
733-
node=node,
734-
args=("",),
735-
confidence=interfaces.INFERENCE,
736-
)
737-
elif variance == TypeVarVariance.covariant and not name.endswith("_co"):
738-
suggest_name = f"{re.sub('_contra$', '', name)}_co"
739-
self.add_message(
740-
"typevar-name-incorrect-variance",
741-
node=node,
742-
args=(f'. "{name}" is covariant, use "{suggest_name}" instead'),
743-
confidence=interfaces.INFERENCE,
744-
)
745-
elif variance == TypeVarVariance.contravariant and not name.endswith("_contra"):
746-
suggest_name = f"{re.sub('_co$', '', name)}_contra"
747-
self.add_message(
748-
"typevar-name-incorrect-variance",
749-
node=node,
750-
args=(f'. "{name}" is contravariant, use "{suggest_name}" instead'),
751-
confidence=interfaces.INFERENCE,
752-
)
753-
elif variance == TypeVarVariance.invariant and (
754-
name.endswith(("_co", "_contra"))
755-
):
756-
suggest_name = re.sub("_contra$|_co$", "", name)
757-
self.add_message(
758-
"typevar-name-incorrect-variance",
759-
node=node,
760-
args=(f'. "{name}" is invariant, use "{suggest_name}" instead'),
761-
confidence=interfaces.INFERENCE,
762-
)
721+
match variance:
722+
case TypeVarVariance.inferred:
723+
# Ignore variance check for PEP 695 type parameters.
724+
# The variance is inferred by the type checker.
725+
# Adding _co or _contra suffix can help to reason about TypeVar.
726+
pass
727+
case TypeVarVariance.double_variant:
728+
self.add_message(
729+
"typevar-double-variance",
730+
node=node,
731+
confidence=interfaces.INFERENCE,
732+
)
733+
self.add_message(
734+
"typevar-name-incorrect-variance",
735+
node=node,
736+
args=("",),
737+
confidence=interfaces.INFERENCE,
738+
)
739+
case TypeVarVariance.covariant if not name.endswith("_co"):
740+
suggest_name = f"{re.sub('_contra$', '', name)}_co"
741+
self.add_message(
742+
"typevar-name-incorrect-variance",
743+
node=node,
744+
args=(f'. "{name}" is covariant, use "{suggest_name}" instead'),
745+
confidence=interfaces.INFERENCE,
746+
)
747+
case TypeVarVariance.contravariant if not name.endswith("_contra"):
748+
suggest_name = f"{re.sub('_co$', '', name)}_contra"
749+
self.add_message(
750+
"typevar-name-incorrect-variance",
751+
node=node,
752+
args=(f'. "{name}" is contravariant, use "{suggest_name}" instead'),
753+
confidence=interfaces.INFERENCE,
754+
)
755+
case TypeVarVariance.invariant if name.endswith(("_co", "_contra")):
756+
suggest_name = re.sub("_contra$|_co$", "", name)
757+
self.add_message(
758+
"typevar-name-incorrect-variance",
759+
node=node,
760+
args=(f'. "{name}" is invariant, use "{suggest_name}" instead'),
761+
confidence=interfaces.INFERENCE,
762+
)
763763

764764
if name_arg is not None and name_arg != name:
765765
self.add_message(

0 commit comments

Comments
 (0)