9393import sys
9494
9595
96+ class SteadyDBError (Exception ):
97+ """General SteadyDB error."""
98+
99+ class InvalidCursor (SteadyDBError ):
100+ """Database cursor is invalid."""
101+
102+
96103def connect (creator , maxusage = None , setsession = None , failures = None ,
97104 closeable = True , * args , ** kwargs ):
98105 """A tough version of the connection constructor of a DB-API 2 module.
@@ -123,22 +130,35 @@ class SteadyDBConnection:
123130
124131 version = __version__
125132
126- _closed = True
127-
128133 def __init__ (self , creator , maxusage = None , setsession = None , failures = None ,
129134 closeable = True , * args , ** kwargs ):
130135 """"Create a "tough" DB-API 2 connection."""
136+ # basic initialization to make finalizer work
137+ self ._con = None
138+ self ._closed = True
139+ # proper initialization of the connection
131140 try :
132141 self ._creator = creator .connect
133142 self ._dbapi = creator
134143 except AttributeError :
144+ # try finding the DB-API 2 module via the connection creator
135145 self ._creator = creator
136146 try :
137- self ._dbapi = sys .modules [creator .__module__ ]
138- if self ._dbapi .connect != creator :
139- raise AttributeError
140- except (AttributeError , KeyError ):
141- self ._dbapi = None
147+ self ._dbapi = creator .dbapi
148+ except AttributeError :
149+ try :
150+ self ._dbapi = sys .modules [creator .__module__ ]
151+ if self ._dbapi .connect != creator :
152+ raise AttributeError
153+ except (AttributeError , KeyError ):
154+ self ._dbapi = None
155+ try :
156+ self ._threadsafety = creator .threadsafety
157+ except AttributeError :
158+ try :
159+ self ._threadsafety = self ._dbapi .threadsafety
160+ except AttributeError :
161+ self ._threadsafety = None
142162 if not callable (self ._creator ):
143163 raise TypeError ("%r is not a connection provider." % (creator ,))
144164 if maxusage is not None and not isinstance (maxusage , (int , long )):
@@ -161,15 +181,74 @@ def _create(self):
161181 if self ._dbapi .connect != self ._creator :
162182 raise AttributeError
163183 except AttributeError :
184+ # try finding the DB-API 2 module via the connection itself
164185 try :
165- self ._dbapi = sys .modules [con .__module__ ]
166- if not callable (self ._dbapi .connect ):
167- raise AttributeError
168- except (AttributeError , KeyError ):
169- raise TypeError ("Cannot determine DB-API 2 module." )
186+ mod = con .__module__
187+ except AttributeError :
188+ mod = None
189+ while mod :
190+ try :
191+ self ._dbapi = sys .modules [mod ]
192+ if not callable (self ._dbapi .connect ):
193+ raise AttributeError
194+ except (AttributeError , KeyError ):
195+ pass
196+ else :
197+ break
198+ i = mod .rfind ('.' )
199+ if i < 0 :
200+ mod = None
201+ else :
202+ mod = mod [:i ]
203+ else :
204+ try :
205+ mod = con .OperationalError .__module__
206+ except AttributeError :
207+ mod = None
208+ while mod :
209+ try :
210+ self ._dbapi = sys .modules [mod ]
211+ if not callable (self ._dbapi .connect ):
212+ raise AttributeError
213+ except (AttributeError , KeyError ):
214+ pass
215+ else :
216+ break
217+ i = mod .rfind ('.' )
218+ if i < 0 :
219+ mod = None
220+ else :
221+ mod = mod [:i ]
222+ else :
223+ self ._dbapi = None
224+ if self ._threadsafety is None :
225+ try :
226+ self ._threadsafety = self ._dbapi .threadsafety
227+ except AttributeError :
228+ try :
229+ self ._threadsafety = con .threadsafety
230+ except AttributeError :
231+ pass
170232 if self ._failures is None :
171- self ._failures = (self ._dbapi .OperationalError ,
172- self ._dbapi .InternalError )
233+ try :
234+ self ._failures = (self ._dbapi .OperationalError ,
235+ self ._dbapi .InternalError )
236+ except AttributeError :
237+ try :
238+ self ._failures = (self ._creator .OperationalError ,
239+ self ._creator .InternalError )
240+ except AttributeError :
241+ try :
242+ self ._failures = (con .OperationalError ,
243+ con .InternalError )
244+ except AttributeError :
245+ raise AttributeError (
246+ "Could not determine failure exceptions"
247+ " (please set failures or creator.dbapi)." )
248+ if isinstance (self ._failures , tuple ):
249+ self ._failure = self ._failures [0 ]
250+ else :
251+ self ._failure = self ._failures
173252 self ._setsession (con )
174253 except Exception , error :
175254 # the database module could not be determined
@@ -213,14 +292,19 @@ def _close(self):
213292
214293 def dbapi (self ):
215294 """Return the underlying DB-API 2 module of the connection."""
295+ if self ._dbapi is None :
296+ raise AttributeError ("Could not determine DB-API 2 module"
297+ " (please set creator.dbapi)." )
216298 return self ._dbapi
217299
218300 def threadsafety (self ):
219301 """Return the thread safety level of the connection."""
220- try :
221- return self ._dbapi .threadsafety
222- except AttributeError :
302+ if self ._threadsafety is None :
303+ if self ._dbapi is None :
304+ raise AttributeError ("Could not determine threadsafety"
305+ " (please set creator.dbapi or creator.threadsafety)." )
223306 return 0
307+ return self ._threadsafety
224308
225309 def close (self ):
226310 """Close the tough connection.
@@ -290,10 +374,12 @@ def __del__(self):
290374class SteadyDBCursor :
291375 """A "tough" version of DB-API 2 cursors."""
292376
293- _closed = True
294-
295377 def __init__ (self , con , * args , ** kwargs ):
296378 """"Create a "tough" DB-API 2 cursor."""
379+ # basic initialization to make finalizer work
380+ self ._cursor = None
381+ self ._closed = True
382+ # proper initialization of the cursor
297383 self ._con = con
298384 self ._args , self ._kwargs = args , kwargs
299385 self ._clearsizes ()
@@ -424,12 +510,18 @@ def tough_method(*args, **kwargs):
424510
425511 def __getattr__ (self , name ):
426512 """Inherit methods and attributes of underlying cursor."""
427- if name .startswith ('execute' ) or name .startswith ('call' ):
428- # make execution methods "tough"
429- return self ._get_tough_method (name )
513+ if self ._cursor :
514+ if name .startswith ('execute' ) or name .startswith ('call' ):
515+ # make execution methods "tough"
516+ return self ._get_tough_method (name )
517+ else :
518+ return getattr (self ._cursor , name )
430519 else :
431- return getattr ( self . _cursor , name )
520+ raise InvalidCursor
432521
433522 def __del__ (self ):
434523 """Delete the steady cursor."""
435- self .close () # make sure the cursor is closed
524+ try :
525+ self .close () # make sure the cursor is closed
526+ except Exception :
527+ pass
0 commit comments