Using with statement on cursor in cx_Oracle - python

cx_Oracle contains __enter__ and __exit__ on Connection objects, but not on Cursor objects. Thus, I use this everywhere to wrap cursors :
class CursorWrapper(object):
def __init__(self, connection):
self.connection = connection
self.cursor = None
def __enter__(self):
self.cursor = self.connection.cursor()
return self.cursor
def __exit__(self, exc_type, exc_value, traceback):
self.cursor.close()
then, when I want a cursor
with CursorWrapper(cnx) as cursor:
cursor.execute("whatever sql statement")
It suits my needs fairly well.
However, I was then wondering what could prevent __enter__ and __exit__ methods to be added directly in cx_Oracle ?
Or is there a better way to use cursors with context managements, which would explain why it is not defined in the module ?

Solution moved from #MathieuC.'s question post.
I can just use contextlib.closing.
import contextlib
with contextlib.closing(cnx.cursor()) as cursor:

Related

pymysql cursor doesn't close

In the PyMySQL library, in cursors.py the following functions are called:
def __enter__(self):
return self
def __exit__(self, *exc_info):
del exc_info
self.close()
That's mean that if I use the cursor class in the with statement, the cursor should close whenever I go out from the nested block. Why instead it remain setted?
db = pymysql.connect(config)
with pymysql.cursors.Cursor(db) as cursor:
print(cursor)
print(cursor)
also:
db = pymysql.connect(config)
with db.cursor() as cursor:
print(cursor)
print(cursor)
both forms return the cursor object printing two times (one time inside the with statement and one time out from the with statement?. Am I doing something wrong?
Closing a cursor doesn't null out the cursor, just detaches it from the database. Try printing cursor.connection instead.
Also, I think you're expecting the "with" keyword to delete the object in question, but it's really just syntactic sugar around the enter and exit functions.

Is there a "with conn.cursor() as..." way to work with Sqlite?

Instead of using:
import sqlite3
conn = sqlite3.connect(':memory:')
c = conn.cursor()
c.execute(...)
c.close()
would it be possible to use the Pythonic idiom:
with conn.cursor() as c:
c.execute(...)
It doesn't seem to work:
AttributeError: __exit__
Note: it's important to close a cursor because of this.
You can use contextlib.closing:
import sqlite3
from contextlib import closing
conn = sqlite3.connect(':memory:')
with closing(conn.cursor()) as cursor:
cursor.execute(...)
This works because closing(object) automatically calls the close() method of the passed in object after the with block.
A simpler alternative would be to use the connection object with the context manager, as specified in the docs.
with con:
con.execute(...)
If you insist on working with the cursor (because reasons), then why not make your own wrapper class?
class SafeCursor:
def __init__(self, connection):
self.con = connection
def __enter__(self):
self.cursor = self.con.cursor()
return self.cursor
def __exit__(self, typ, value, traceback):
self.cursor.close()
You'll then call your class like this:
with SafeCursor(conn) as c:
c.execute(...)
Adding to sudormrfbin's post. I've recently experienced an issue where an INSERT statement wasn't committing to the database. Turns out I was missing the with context manager for just the Connection object.
Also, it is a good practice to always close the Cursor object as well, as mentioned in this post.
Therefore, use two contextlib.closing() methods, each within a with context manager:
import contextlib
import sqlite3
# Auto-closes the Connection object
with contextlib.closing(sqlite3.connect("path_to_db_file")) as conn:
# Auto-commit to the database
with conn:
# Auto-close the Cursor object
with contextlib.closing(conn.cursor()) as cursor:
# Execute method(s)
cursor.execute(""" SQL statements here """)

Python Database Pooling with psycopg2

Below is a database pooling example. I don't understand the following.
Why the getcursor function use "yield"?
What is the context manager?
from psycopg2.pool import SimpleConnectionPool
from contextlib import contextmanager
dbConnection = "dbname='dbname' user='postgres' host='localhost' password='postgres'"
# pool define with 10 live connections
connectionpool = SimpleConnectionPool(1,10,dsn=dbConnection)
#contextmanager
def getcursor():
con = connectionpool.getconn()
try:
yield con.cursor()
finally:
connectionpool.putconn(con)
def main_work():
try:
# with here will take care of put connection when its done
with getcursor() as cur:
cur.execute("select * from \"TableName\"")
result_set = cur.fetchall()
except Exception as e:
print "error in executing with exception: ", e**
Both of your questions are related. In python context managers are what we're using whenever you see a with statement. Classically, they're written like this.
class getcursor(object):
def __enter__(self):
con = connectionpool.getconn()
return con
def __exit__(self, *args):
connectionpool.putconn(con)
Now when you use a context manager, it calls the __enter__ method on the with statement and the __exit__ method when it exits the context. Think of it like this.
cursor = getcursor()
with cursor as cur: # calls cursor.__enter__()
cur.execute("select * from \"TableName\"")
result_set = cur.fetchall()
# We're now exiting the context so it calls `cursor.__exit__()`
# with some exception info if relevant
x = 1
The #contextmanager decorator is some sugar to make creating a context manager easier. Basically, it uses the yield statement to give the execution back to the caller. Everything up to and including the yield statement is the __enter__ method and everything after that is effectively the __exit__ statement.

Building a class to use `with` to establish exclusive sql connections

I am working on a simple convenience class to use with the with operator so that I can establish exclusive access to a sqlite3 database for a extended writing session in a concurrent system.
Here is the class:
import sqlite3 as sql
class ExclusiveSqlConnection(object):
"""meant to be used with a with statement to ensure proper closure"""
def __enter__(self, path):
self.con = sql.connect(path, isolation_level = "EXCLUSIVE")
self.con.execute("BEGIN EXCLUSIVE")
self.cur = self.con.cursor()
return self
def __exit__(self):
self.con.commit()
self.con.close()
However, when I run this I get the error:
with sql_lib.ExclusiveSqlConnection(self.output) as c:
TypeError: object.__new__() takes no parameters
The constructor (__init__ method) for your ExclusiveSqlConnection needs to take a path parameter.
On the other hand, __enter__ takes no parameter other than self.
import sqlite3 as sql
class ExclusiveSqlConnection(object):
"""meant to be used with a with statement to ensure proper closure"""
def __init__(self, path):
self.path = path
def __enter__(self):
self.con = sql.connect(self.path, isolation_level = "EXCLUSIVE")
self.con.execute("BEGIN EXCLUSIVE")
self.cur = self.con.cursor()
return self
def __exit__(self, exception_type, exception_val, trace):
self.con.commit()
self.con.close()

The open/close function of SQLite implementation

I'm trying to come up with SQLiteDB object, and following is the open/close code for it.
Does this work without problem? Am I missing something important?
For close(), I use con.close() and cursor.close(), but I'm wondering if cursor.close() is necessary.
class SQLiteDB(object):
def __init__(self, dbFile, connect = True):
self.dbFile = dbFile
self.con = None
self.cursor = None
if connect:
self.open()
def __del__(self):
if self.con:
self.close()
def open(self):
self.con = sqlite3.connect(self.dbFile)
self.cursor = self.connector.cursor()
return self.con, self.cursor
def close(self):
self.con.close()
self.cursor.close()
self.cursor = None
self.con = None
What happens on Cursor.close() depends on the underlying database implementation. For SQLite it might currently work without closing, but for other implementations or a future SQLite version it might not, so I would recommend to close the Cursor object. You can find further information on Cursor.close() in PEP 249.
Also, there seems to be a typo in your code:
self.connector = sqlite3.connect(self.dbFile)
should probably be
self.con = sqlite3.connect(self.dbFile)
Otherwise your code looks fine to me. Happy coding :) .

Categories