Instead of asking what standard practice is, as it is often unclear and subjective, you can try looking at the module itself for guidance. In general, using the with keyword as another suggested user is a great idea, but in this particular case it may not give you enough of the functionality that you expect.
Starting with version 1.2.5 of the MySQLdb.Connection module, MySQLdb.Connection implements the context manager protocol with the following code ( github ):
def __enter__(self): if self.get_autocommit(): self.query("BEGIN") return self.cursor() def __exit__(self, exc, value, tb): if exc: self.rollback() else: self.commit()
There are already several existing Q & A about with , or you can read Understanding Python with an instruction , but in essence, what happens is that __enter__ is executed at the beginning of the with block, and __exit__ is executed after exiting the with block. You can use the optional syntax with EXPR as VAR to bind the object returned by __enter__ to a name if you intend to reference this object later. So, given the above implementation, here is an easy way to query your database:
connection = MySQLdb.connect(...) with connection as cursor:
The question is, what are the connection and cursor states after exiting the with block? The above __exit__ method only calls self.rollback() or self.commit() , and none of these methods are used to call the close() method. The cursor itself does not have the __exit__ method, and it does not matter if it did this, because with controls only the connection. Therefore, both the connection and the cursor remain open after exiting the with block. This is easy to confirm by adding the following code to the example above:
try: cursor.execute('select 1;') print 'cursor is open;', except MySQLdb.ProgrammingError: print 'cursor is closed;', if connection.open: print 'connection is open' else: print 'connection is closed'
You should see that the “cursor is open, the connection is open” is printed in the standard format.
I believe that you need to close the cursor before making the connection.
Why? The MySQL C API , which is the basis for MySQLdb , does not implement any cursor object, as implied in the module documentation: "MySQL does not support cursors, but cursors are easily emulated." Indeed, the MySQLdb.cursors.BaseCursor class MySQLdb.cursors.BaseCursor inherited directly from object and does not impose such restrictions on cursors with respect to commit / rollback. The Oracle developer had this to say :
cnx.commit () before cur.close () sounds the most logical to me. Maybe you can follow the rule: "Close the cursor if you no longer need it." Therefore, commit () before closing the cursor. In the end, for Connector / Python, this is not a big deal, but another database.
I expect as close as you are going to go to "standard practice" on this.
Is there a significant advantage in finding sets of transactions that do not require intermediate commits so that you do not have to get new cursors for each transaction?
I doubt it very much, and trying to do this, you can introduce an additional human error. It is better to choose an agreement and stick to it.
Is there a lot of overhead for getting new cursors, or is it just not a big deal?
The overhead is negligible and does not apply to the database server at all; this is entirely within the scope of MySQLdb implementation. You can watch BaseCursor.__init__ on github if you are really interested in knowing what happens when you create a new cursor.
Returning to the previous one when we discussed with , perhaps now you can understand why the MySQLdb.Connection class __enter__ and __exit__ methods give you a new cursor object in each with t block to watch it or close it at the end of the block. It is quite lightweight and exists solely for your convenience.
If it is really important for you to micromechanize the cursor object, you can use contextlib.closing to make up for the fact that the cursor object does not have a specific __exit__ method. In this case, you can also use it to force the connection object to close when you exit the with block. This should output "my_curs closed, my_conn closed":
from contextlib import closing import MySQLdb with closing(MySQLdb.connect(...)) as my_conn: with closing(my_conn.cursor()) as my_curs: my_curs.execute('select 1;') result = my_curs.fetchall() try: my_curs.execute('select 1;') print 'my_curs is open;', except MySQLdb.ProgrammingError: print 'my_curs is closed;', if my_conn.open: print 'my_conn is open' else: print 'my_conn is closed'
Note that with closing(arg_obj) will not call the __enter__ and __exit__ argument methods; it will only call the method of the close object of the argument object at the end of the with block. (To see this in action, simply define the Foo class using the __enter__ , __exit__ and close methods, which contain simple print statements, and compare what happens when you do with Foo(): pass to what happens when you do with closing(Foo()): pass .) This has two significant meanings:
Firstly, if autocommit is enabled, MySQLdb will BEGIN an explicit transaction on the server when using with connection and committing the transaction at the end of the block. This is the default MySQLdb behavior, designed to protect you from the default MySQL behavior, which immediately makes any and all DML statements. MySQLdb assumes that when you use the context manager, you want to complete the transaction and use explicit BEGIN to bypass the autocommit parameter on the server. If you are used to using with connection , you might think that autocommit is turned off when it actually just bypasses. You may get an unpleasant surprise if you add closing to your code and lose transaction integrity; you cannot undo the changes, you can see concurrency errors, and this may not be immediately obvious.
Secondly, with closing(MySQLdb.connect(user, pass)) as VAR binds the connection object to the VAR , unlike with MySQLdb.connect(user, pass) as VAR , which binds the new cursor object to the VAR . In the latter case, you will not have direct access to the connection object! Instead, you will need to use the connection cursor attribute, which provides proxy access to the original connection. When the cursor is closed, the connection attribute is set to None . This results in an abandoned connection that will stick until one of the following events occurs:
- All cursor links are deleted.
- The cursor goes out of scope
- Connection time
- The connection is closed manually using the server administration tools.
You can verify this by checking open connections (in Workbench or using SHOW PROCESSLIST ) by doing the following lines one after another:
with MySQLdb.connect(...) as my_curs: pass my_curs.close() my_curs.connection