Reputation:
Is there any way I can convert a weak variable to strong one in Python?
# utils.py
def connect_db():
cnx = mysql.connector.connect(user="root", database="test_db")
cursor = cnx.cursor()
return cursor
# main.py
from utils import connect_db
def main():
cursor = connect_db()
cursor.execute("some commands")
I got this error
ReferenceError: weakly-referenced object no longer exists
Upvotes: 0
Views: 262
Reputation: 114330
First, let's see why the error is happening. You open a connection and bind it to a function-local name cnx
. Then you create a cursor, which has a weak reference to the connection. As soon as you return the cursor, the connection no longer has strong references, and it up for garbage collection. By the time you attempt to execute a query, the connection has been garbage collected.
As you noted, making the reference from cursor to connection a strong one would solve your immediate problem. At the same time, there is a reason that the API was designed this way. You don't want to have too many connections lingering around, all because some cursors did not get garbage collected.
Instead, the right answer is to handle the connection explicitly, instead of burying it in a function that returns a cursor. Among other things, it should be done in an enclosing with
block to make the shut-down explicit in case of error. Since the existing implementation does not appear to support context management, you will have to write out the try-catch explicitly.
For example, you could return both the connection and the cursor:
def connect_db():
cnx = mysql.connector.connect(user="root", database="test_db")
cursor = cnx.cursor()
return cnx, cursor
def main():
cnx = None
try:
cnx, cursor = connect_db()
cursor.execute("some commands")
finally:
if cnx is not None:
cnx.close()
A more elegant solution might be to make your own context manager for the database instead of returning two separate objects (similar to https://stackoverflow.com/a/67645694/2988730, but more encapsulated):
class connect_db:
def __enter__(self):
self.cnx = mysql.connector.connect(user="root", database="test_db")
self.cursor = self.cnx.cursor()
return cursor
def __exit__(self, *args):
self.cursor.close()
self.cnx.close()
def main():
with connect_db() as cursor:
cursor.execute("some commands")
Upvotes: 2