summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2018-12-06 08:58:46 +0100
committerClaudiu Popa <pcmanticore@gmail.com>2018-12-06 08:58:46 +0100
commitc320b0e0d77f34972631a82a7088b368d9002dd7 (patch)
tree3675a1ab1f51562b15da6b6492f329fc799109b4
parent95bc362f59476a41538a8d17e3232ad893e004ff (diff)
downloadpylint-git-c320b0e0d77f34972631a82a7088b368d9002dd7.tar.gz
Fix false positive with `not-async-context-manager` caused by not understanding `contextlib.asynccontextmanager`
Close #2440
-rw-r--r--ChangeLog4
-rw-r--r--pylint/checkers/async.py26
-rw-r--r--pylint/test/functional/not_async_context_manager_py37.py12
-rw-r--r--pylint/test/functional/not_async_context_manager_py37.rc2
-rw-r--r--pylint/test/functional/not_async_context_manager_py37.txt0
5 files changed, 35 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 6d90cdf52..ce24efe3d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,10 @@ What's New in Pylint 2.3.0?
Release date: TBA
+* Fix false positive with `not-async-context-manager` caused by not understanding `contextlib.asynccontextmanager`
+
+ Close #2440
+
* Refactor ``bad-reversed-sequence`` to account for more objects that can define ``__reversed__``
One such object would be an enum class, for which ``__reversed__`` yields each individual enum.
diff --git a/pylint/checkers/async.py b/pylint/checkers/async.py
index 881b29daf..21feba035 100644
--- a/pylint/checkers/async.py
+++ b/pylint/checkers/async.py
@@ -9,12 +9,14 @@
import sys
import astroid
+from astroid import bases
from astroid import exceptions
from pylint import checkers
from pylint.checkers import utils as checker_utils
from pylint import interfaces
from pylint import utils
+from pylint.checkers.utils import decorated_with
class AsyncChecker(checkers.BaseChecker):
@@ -41,6 +43,7 @@ class AsyncChecker(checkers.BaseChecker):
self._ignore_mixin_members = utils.get_global_option(
self, "ignore-mixin-members"
)
+ self._async_generators = ["contextlib.asynccontextmanager"]
@checker_utils.check_messages("yield-inside-async-function")
def visit_asyncfunctiondef(self, node):
@@ -53,29 +56,34 @@ class AsyncChecker(checkers.BaseChecker):
@checker_utils.check_messages("not-async-context-manager")
def visit_asyncwith(self, node):
for ctx_mgr, _ in node.items:
- infered = checker_utils.safe_infer(ctx_mgr)
- if infered is None or infered is astroid.Uninferable:
+ inferred = checker_utils.safe_infer(ctx_mgr)
+ if inferred is None or inferred is astroid.Uninferable:
continue
- if isinstance(infered, astroid.Instance):
+ if isinstance(inferred, bases.AsyncGenerator):
+ # Check if we are dealing with a function decorated
+ # with contextlib.asynccontextmanager.
+ if decorated_with(inferred.parent, self._async_generators):
+ continue
+ else:
try:
- infered.getattr("__aenter__")
- infered.getattr("__aexit__")
+ inferred.getattr("__aenter__")
+ inferred.getattr("__aexit__")
except exceptions.NotFoundError:
- if isinstance(infered, astroid.Instance):
+ if isinstance(inferred, astroid.Instance):
# If we do not know the bases of this class,
# just skip it.
- if not checker_utils.has_known_bases(infered):
+ if not checker_utils.has_known_bases(inferred):
continue
# Just ignore mixin classes.
if self._ignore_mixin_members:
- if infered.name[-5:].lower() == "mixin":
+ if inferred.name[-5:].lower() == "mixin":
continue
else:
continue
self.add_message(
- "not-async-context-manager", node=node, args=(infered.name,)
+ "not-async-context-manager", node=node, args=(inferred.name,)
)
diff --git a/pylint/test/functional/not_async_context_manager_py37.py b/pylint/test/functional/not_async_context_manager_py37.py
new file mode 100644
index 000000000..705e5afc9
--- /dev/null
+++ b/pylint/test/functional/not_async_context_manager_py37.py
@@ -0,0 +1,12 @@
+# pylint: disable=missing-docstring
+
+from contextlib import asynccontextmanager
+
+
+@asynccontextmanager
+async def context_manager(value):
+ yield value
+
+
+async with context_manager(42) as ans:
+ assert ans == 42
diff --git a/pylint/test/functional/not_async_context_manager_py37.rc b/pylint/test/functional/not_async_context_manager_py37.rc
new file mode 100644
index 000000000..a17bb22da
--- /dev/null
+++ b/pylint/test/functional/not_async_context_manager_py37.rc
@@ -0,0 +1,2 @@
+[testoptions]
+min_pyver=3.7
diff --git a/pylint/test/functional/not_async_context_manager_py37.txt b/pylint/test/functional/not_async_context_manager_py37.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/pylint/test/functional/not_async_context_manager_py37.txt