Skip to content

Commit

Permalink
Add new consider-using-generator and 'use-a-generator' checkers
Browse files Browse the repository at this point in the history
See issue #3165 (#3309)
See #3309 (comment)

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Co-authored-by: Daniel Hahler <github@thequod.de>
  • Loading branch information
3 people committed Feb 20, 2021
1 parent 570e655 commit 45c8245
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -446,4 +446,7 @@ contributors:

* David Gilman: contributor

* Ikraduya Edian: contributor
- Added new checks 'consider-using-generator' and 'use-a-generator'.

* Tiago Honorato: contributor
9 changes: 9 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,17 @@ What's New in Pylint 2.7.0?

Close #3555

* New check: ``consider-using-generator``

This check warns when a comprehension is used inside an `any` or `all` function,
since it is unnecessary and should be replaced by a generator instead.
Using a generator would be less code and way faster.

Close #3165

* Add Github Actions to replace Travis and AppVeyor in the future


What's New in Pylint 2.6.1?
===========================

Expand Down
2 changes: 2 additions & 0 deletions doc/whatsnew/2.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ New checkers

* Add `condition-evals-to-constant` check for conditionals using and/or that evaluate to a constant.

* Add `consider-using-generator` check for the use of list comprehension inside ``any`` or ``all`` function.

Other Changes
=============

Expand Down
61 changes: 61 additions & 0 deletions pylint/checkers/refactoring/refactoring_checker.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2016-2018 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
# Copyright (c) 2017 Łukasz Sznuk <ls@rdprojekt.pl>
# Copyright (c) 2017 Alex Hearn <alex.d.hearn@gmail.com>
# Copyright (c) 2017 Antonio Ossa <aaossa@uc.cl>
# Copyright (c) 2018 Konstantin Manna <Konstantin@Manna.uno>
# Copyright (c) 2018 Konstantin <Github@pheanex.de>
# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
# Copyright (c) 2018 Matej Marušák <marusak.matej@gmail.com>
# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
# Copyright (c) 2018 Mr. Senko <atodorov@mrsenko.com>
# Copyright (c) 2019 Ikraduya Edian <ikraduya@gmail.com>

# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
Expand Down Expand Up @@ -282,6 +300,18 @@ class RefactoringChecker(checkers.BaseTokenChecker):
"Emitted when calling the super() builtin with the current class "
"and instance. On Python 3 these arguments are the default and they can be omitted.",
),
"R1728": (
"Consider using a generator instead '%s(%s)'",
"consider-using-generator",
"If your container can be large using "
"a generator will bring better performance.",
),
"R1729": (
"Use a generator instead '%s(%s)'",
"use-a-generator",
"Comprehension inside of 'any' or 'all' is unnecessary. "
"A generator would be sufficient and faster.",
),
}
options = (
(
Expand Down Expand Up @@ -684,18 +714,49 @@ def _check_consider_using_comprehension_constructor(self, node):
message_name = "consider-using-set-comprehension"
self.add_message(message_name, node=node)

def _check_consider_using_generator(self, node):
# 'any' and 'all' definitely should use generator, while 'list' and 'tuple' need to be considered first
# See https://github.com/PyCQA/pylint/pull/3309#discussion_r576683109
checked_call = ["any", "all", "list", "tuple"]
if (
isinstance(node, astroid.Call)
and node.func
and isinstance(node.func, astroid.Name)
and node.func.name in checked_call
):
# functions in checked_calls take exactly one argument
# check whether the argument is list comprehension
if len(node.args) == 1 and isinstance(node.args[0], astroid.ListComp):
# remove square brackets '[]'
inside_comp = node.args[0].as_string()[1:-1]
call_name = node.func.name
if call_name in ["any", "all"]:
self.add_message(
"use-a-generator",
node=node,
args=(call_name, inside_comp),
)
else:
self.add_message(
"consider-using-generator",
node=node,
args=(call_name, inside_comp),
)

@utils.check_messages(
"stop-iteration-return",
"consider-using-dict-comprehension",
"consider-using-set-comprehension",
"consider-using-sys-exit",
"super-with-arguments",
"consider-using-generator",
)
def visit_call(self, node):
self._check_raising_stopiteration_in_generator_next_call(node)
self._check_consider_using_comprehension_constructor(node)
self._check_quit_exit_call(node)
self._check_super_with_arguments(node)
self._check_consider_using_generator(node)

@staticmethod
def _has_exit_in_scope(scope):
Expand Down
11 changes: 11 additions & 0 deletions tests/functional/c/consider_using_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# pylint: disable=missing-docstring, invalid-name
# https://github.com/PyCQA/pylint/issues/3165

list([])
tuple([])

list([0 for y in list(range(10))]) # [consider-using-generator]
tuple([0 for y in list(range(10))]) # [consider-using-generator]

list(0 for y in list(range(10)))
tuple(0 for y in list(range(10)))
2 changes: 2 additions & 0 deletions tests/functional/c/consider_using_generator.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
consider-using-generator:7:0::Consider using a generator instead 'list(0 for y in list(range(10)))'
consider-using-generator:8:0::Consider using a generator instead 'tuple(0 for y in list(range(10)))'
11 changes: 11 additions & 0 deletions tests/functional/u/use_a_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# pylint: disable=missing-docstring, invalid-name
# https://github.com/PyCQA/pylint/issues/3165

any([])
all([])

any([0 for x in list(range(10))]) # [use-a-generator]
all([0 for y in list(range(10))]) # [use-a-generator]

any(0 for x in list(range(10)))
all(0 for y in list(range(10)))
2 changes: 2 additions & 0 deletions tests/functional/u/use_a_generator.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
use-a-generator:7:0::Use a generator instead 'any(0 for x in list(range(10)))'
use-a-generator:8:0::Use a generator instead 'all(0 for y in list(range(10)))'

0 comments on commit 45c8245

Please sign in to comment.