-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
False positive "not-context-manager" for psycopg.connect when used as context manager #5273
Comments
Possibly related: python/mypy#11004 (something to do with |
I also ran into this issue (with a different contextmanager that also uses overloads). What I discovered is that pylint appears to be checking that the overloads return a contextmanager, which produces an error because overloads return None. That is:
|
A workaround that worked for me is adding return statements to the overloads:
Of course, this is rather ugly and only works if you have control over the code containing the overloads. |
I also faced issues with this, using with connect(**connection_params) as conn:
with conn.cursor() as cursor:
# some code here
with cursor.copy(f"COPY {PG_DB_SCHEMA}.{PG_TABLE_NAME} ({columns}) FROM STDIN") as copy:
# some code here And when running PyLint I got the error: ************* Module path.to.my.module
path/to/my/module.py:95:8: E1129: Context manager 'NoneType' doesn't implement __enter__ and __exit__. (not-context-manager) |
I have come across this issue when using my own context manager with an overloaded factory function on a class. Strangely this doesn't happen when the factory function is free-standing. Here's some demo code: from enum import Enum, auto
from typing import overload, Literal
class Mode(Enum):
READ = auto()
WRITE = auto()
SIMULATE = auto()
class ConnectionBase:
def __enter__(self):
print(f"{type(self).__name__}: enter")
return self
def __exit__(self, exc_type, exc_value, traceback):
print(f"{type(self).__name__}: exit")
return exc_type is None
class ReadConnection(ConnectionBase):
pass
class WriteConnection(ConnectionBase):
pass
class SimConnection(ConnectionBase):
pass
def good_code():
@overload
def connect(mode: Literal[Mode.READ]) -> ReadConnection: ...
@overload
def connect(mode: Literal[Mode.WRITE]) -> WriteConnection: ...
@overload
def connect(mode: Literal[Mode.SIMULATE]) -> SimConnection: ...
def connect(mode: Mode):
if mode == Mode.READ:
return ReadConnection()
if mode == Mode.WRITE:
return WriteConnection()
if mode == Mode.SIMULATE:
return SimConnection()
raise ValueError(f"unknown connection mode: {mode}")
con = connect(mode=Mode.READ)
with con: # pylint knows that con is a contextmanager
...
def bad_code():
class Project:
@overload
def connect(self, mode: Literal[Mode.READ]) -> ReadConnection: ...
@overload
def connect(self, mode: Literal[Mode.WRITE]) -> WriteConnection: ...
@overload
def connect(self, mode: Literal[Mode.SIMULATE]) -> SimConnection: ...
def connect(self, mode: Mode):
if mode == Mode.READ:
return ReadConnection()
if mode == Mode.WRITE:
return WriteConnection()
if mode == Mode.SIMULATE:
return SimConnection()
raise ValueError(f"unknown connection mode: {mode}")
project = Project()
con = project.connect(mode=Mode.READ)
with con: # pylint reports E1129: 'NoneType' doesn't implement __enter__ and __exit__
... Pylint version:
|
I did a little investigating and wrote a (non-library-specific) test case which fails due to this false-positive. With |
Sounds great @adamtuft, would you mind adding this test case directly in pylint with a pull request ? |
Sure thing, I'll put one together 👍 |
Just realised that this is the same issue as #4660 for which you already have a regression test, fixed by pylint-dev/astroid#1173, so I guess there's no need for me to add another test? |
Thanks for checking and sorry for the delay. A regression test in pylint would still be useful (especially if we need to upgrade astroid, it's a way to close this pylint issue only when it's actually fixed in pylint because we upgraded astroid and the test that failed now pass). But of course it's less useful than if we had nothing in astroid, I'll let you judge if you still want to contribute :) |
@Pierre-Sassoulas I think @adamtuft is pointing out that we actually have this exact case in the test suite, just asserting the wrong result with a sad comment around it. When we upgrade to astroid 3.2 we can just change the result and remove the sad comment 👍. pylint/tests/functional/r/regression_02/regression_4660.py Lines 26 to 45 in a54d8a6
Thanks @adamtuft for investigating. Closing as fixed pending the upgrade to astroid 3.2. |
I still see this occuring even after picking up the fix
import psycopg
CONNECTION = psycopg.connect(
user="admin",
password="password",
host="host.com",
port="1234",
dbname="important",
keepalives=1,
)
with CONNECTION as conn:
with CONNECTION.cursor() as cursor:
cursor.execute("INSERT INTO tbl(name) VALUES(%s)", ("foobar",))
|
Bug description
Following:
...raises E1129 (not-context-manager) violation.
But the code works fine and sources look ok as psycopg.connect() returns Connection class (which indeed has
__enter__
and__exit__
).I'm running
psycopg==3.0.1
.Configuration
No response
Command used
Pylint output
Expected behavior
There shouldn't be error.
Pylint version
OS / Environment
Kubuntu 20.04 LTS
Additional dependencies
No response
The text was updated successfully, but these errors were encountered: