Skip to content

Commit 46ab0f8

Browse files
authored
Simplify len() comparisons (#14907)
This PR removes certain `len()` comparisons when it would be faster/more succinct to do a truthy check instead, such as `len(x) == 0` vs `not x`. This fixes the [`FURB115`](https://github.com/dosisod/refurb/blob/master/docs/checks.md#furb115-no-len-compare) errors in Refurb (see #14887).
1 parent 1dd26a3 commit 46ab0f8

17 files changed

+33
-36
lines changed

mypy/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3301,7 +3301,7 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
33013301
manager.trace(f"Queuing {fresh_msg} SCC ({scc_str})")
33023302
fresh_scc_queue.append(scc)
33033303
else:
3304-
if len(fresh_scc_queue) > 0:
3304+
if fresh_scc_queue:
33053305
manager.log(f"Processing {len(fresh_scc_queue)} queued fresh SCCs")
33063306
# Defer processing fresh SCCs until we actually run into a stale SCC
33073307
# and need the earlier modules to be loaded.

mypy/checker.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5078,7 +5078,7 @@ def partition_by_callable(
50785078
callables, uncallables = self.partition_by_callable(
50795079
erase_to_union_or_bound(typ), unsound_partition
50805080
)
5081-
uncallables = [typ] if len(uncallables) else []
5081+
uncallables = [typ] if uncallables else []
50825082
return callables, uncallables
50835083

50845084
# A TupleType is callable if its fallback is, but needs special handling
@@ -5093,7 +5093,7 @@ def partition_by_callable(
50935093
callables, uncallables = self.partition_by_callable(
50945094
method.type, unsound_partition=False
50955095
)
5096-
if len(callables) and not len(uncallables):
5096+
if callables and not uncallables:
50975097
# Only consider the type callable if its __call__ method is
50985098
# definitely callable.
50995099
return [typ], []
@@ -5129,14 +5129,12 @@ def conditional_callable_type_map(
51295129

51305130
callables, uncallables = self.partition_by_callable(current_type, unsound_partition=False)
51315131

5132-
if len(callables) and len(uncallables):
5133-
callable_map = {expr: UnionType.make_union(callables)} if len(callables) else None
5134-
uncallable_map = (
5135-
{expr: UnionType.make_union(uncallables)} if len(uncallables) else None
5136-
)
5132+
if callables and uncallables:
5133+
callable_map = {expr: UnionType.make_union(callables)} if callables else None
5134+
uncallable_map = {expr: UnionType.make_union(uncallables)} if uncallables else None
51375135
return callable_map, uncallable_map
51385136

5139-
elif len(callables):
5137+
elif callables:
51405138
return {}, None
51415139

51425140
return None, {}
@@ -6511,7 +6509,7 @@ def conditional_types_with_intersection(
65116509
if intersection is None:
65126510
continue
65136511
out.append(intersection)
6514-
if len(out) == 0:
6512+
if not out:
65156513
# Only report errors if no element in the union worked.
65166514
if self.should_report_unreachable_issues():
65176515
for types, reason in errors:
@@ -7351,7 +7349,7 @@ def add_mapping(self, keys: set[TKey], values: set[TValue]) -> None:
73517349
73527350
Note that the given set of keys must be non-empty -- otherwise, nothing happens.
73537351
"""
7354-
if len(keys) == 0:
7352+
if not keys:
73557353
return
73567354

73577355
subtree_roots = [self._lookup_or_make_root_id(key) for key in keys]
@@ -7462,7 +7460,7 @@ def group_comparison_operands(
74627460
if current_indices and (operator != last_operator or operator not in operators_to_group):
74637461
# If some of the operands in the chain are assignable, defer adding it: we might
74647462
# end up needing to merge it with other chains that appear later.
7465-
if len(current_hashes) == 0:
7463+
if not current_hashes:
74667464
simplified_operator_list.append((last_operator, sorted(current_indices)))
74677465
else:
74687466
groups[last_operator].add_mapping(current_hashes, current_indices)
@@ -7485,7 +7483,7 @@ def group_comparison_operands(
74857483
current_hashes.add(right_hash)
74867484

74877485
if last_operator is not None:
7488-
if len(current_hashes) == 0:
7486+
if not current_hashes:
74897487
simplified_operator_list.append((last_operator, sorted(current_indices)))
74907488
else:
74917489
groups[last_operator].add_mapping(current_hashes, current_indices)

mypy/checkexpr.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ def check_typeddict_call(
686686
context: Context,
687687
orig_callee: Type | None,
688688
) -> Type:
689-
if len(args) >= 1 and all([ak == ARG_NAMED for ak in arg_kinds]):
689+
if args and all([ak == ARG_NAMED for ak in arg_kinds]):
690690
# ex: Point(x=42, y=1337)
691691
assert all(arg_name is not None for arg_name in arg_names)
692692
item_names = cast(List[str], arg_names)
@@ -708,7 +708,7 @@ def check_typeddict_call(
708708
callee, unique_arg.analyzed, context, orig_callee
709709
)
710710

711-
if len(args) == 0:
711+
if not args:
712712
# ex: EmptyDict()
713713
return self.check_typeddict_call_with_kwargs(callee, {}, context, orig_callee)
714714

@@ -2423,8 +2423,7 @@ def infer_overload_return_type(
24232423
inferred_types.append(infer_type)
24242424
type_maps.append(m)
24252425

2426-
if len(matches) == 0:
2427-
# No match was found
2426+
if not matches:
24282427
return None
24292428
elif any_causes_overload_ambiguity(matches, return_types, arg_types, arg_kinds, arg_names):
24302429
# An argument of type or containing the type 'Any' caused ambiguity.
@@ -3015,7 +3014,7 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
30153014
if not encountered_partial_type and not failed_out:
30163015
iterable_type = UnionType.make_union(iterable_types)
30173016
if not is_subtype(left_type, iterable_type):
3018-
if len(container_types) == 0:
3017+
if not container_types:
30193018
self.msg.unsupported_operand_types("in", left_type, right_type, e)
30203019
else:
30213020
container_type = UnionType.make_union(container_types)
@@ -5478,7 +5477,7 @@ def any_causes_overload_ambiguity(
54785477

54795478

54805479
def all_same_types(types: list[Type]) -> bool:
5481-
if len(types) == 0:
5480+
if not types:
54825481
return True
54835482
return all(is_same_type(t, types[0]) for t in types[1:])
54845483

mypy/checkpattern.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ def get_sequence_type(self, t: Type, context: Context) -> Type | None:
316316
if isinstance(t, UnionType):
317317
items = [self.get_sequence_type(item, context) for item in t.items]
318318
not_none_items = [item for item in items if item is not None]
319-
if len(not_none_items) > 0:
319+
if not_none_items:
320320
return make_simplified_union(not_none_items)
321321
else:
322322
return None

mypy/errors.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -637,14 +637,14 @@ def generate_unused_ignore_errors(self, file: str) -> None:
637637
used_ignored_codes = used_ignored_lines[line]
638638
unused_ignored_codes = set(ignored_codes) - set(used_ignored_codes)
639639
# `ignore` is used
640-
if len(ignored_codes) == 0 and len(used_ignored_codes) > 0:
640+
if not ignored_codes and used_ignored_codes:
641641
continue
642642
# All codes appearing in `ignore[...]` are used
643-
if len(ignored_codes) > 0 and len(unused_ignored_codes) == 0:
643+
if ignored_codes and not unused_ignored_codes:
644644
continue
645645
# Display detail only when `ignore[...]` specifies more than one error code
646646
unused_codes_message = ""
647-
if len(ignored_codes) > 1 and len(unused_ignored_codes) > 0:
647+
if len(ignored_codes) > 1 and unused_ignored_codes:
648648
unused_codes_message = f"[{', '.join(sorted(unused_ignored_codes))}]"
649649
message = f'Unused "type: ignore{unused_codes_message}" comment'
650650
for unused in unused_ignored_codes:

mypy/messages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2781,7 +2781,7 @@ def strip_quotes(s: str) -> str:
27812781

27822782

27832783
def format_string_list(lst: list[str]) -> str:
2784-
assert len(lst) > 0
2784+
assert lst
27852785
if len(lst) == 1:
27862786
return lst[0]
27872787
elif len(lst) <= 5:

mypy/nodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ def __init__(self, items: list[OverloadPart]) -> None:
556556
self.items = items
557557
self.unanalyzed_items = items.copy()
558558
self.impl = None
559-
if len(items) > 0:
559+
if items:
560560
# TODO: figure out how to reliably set end position (we don't know the impl here).
561561
self.set_line(items[0].line, items[0].column)
562562
self.is_final = False

mypy/partially_defined.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def done(self) -> BranchState:
153153
all_vars.update(b.must_be_defined)
154154
# For the rest of the things, we only care about branches that weren't skipped.
155155
non_skipped_branches = [b for b in self.branches if not b.skipped]
156-
if len(non_skipped_branches) > 0:
156+
if non_skipped_branches:
157157
must_be_defined = non_skipped_branches[0].must_be_defined
158158
for b in non_skipped_branches[1:]:
159159
must_be_defined.intersection_update(b.must_be_defined)
@@ -660,7 +660,7 @@ def visit_import(self, o: Import) -> None:
660660
else:
661661
# When you do `import x.y`, only `x` becomes defined.
662662
names = mod.split(".")
663-
if len(names) > 0:
663+
if names:
664664
# `names` should always be nonempty, but we don't want mypy
665665
# to crash on invalid code.
666666
self.tracker.record_definition(names[0])

mypy/semanal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5113,7 +5113,7 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None:
51135113
return None
51145114
types.append(analyzed)
51155115

5116-
if has_param_spec and num_args == 1 and len(types) > 0:
5116+
if has_param_spec and num_args == 1 and types:
51175117
first_arg = get_proper_type(types[0])
51185118
if not (
51195119
len(types) == 1

mypy/semanal_enum.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def parse_enum_call_args(
232232
% class_name,
233233
call,
234234
)
235-
if len(items) == 0:
235+
if not items:
236236
return self.fail_enum_call_arg(f"{class_name}() needs at least one item", call)
237237
if not values:
238238
values = [None] * len(items)

0 commit comments

Comments
 (0)