44# license that can be found in the LICENSE file or at
55# https://developers.google.com/open-source/licenses/bsd
66
7- import google .api_core .exceptions as grpc_exceptions
7+ """Database cursor API."""
8+
9+ from google .api_core .exceptions import (
10+ AlreadyExists ,
11+ FailedPrecondition ,
12+ InternalServerError ,
13+ InvalidArgument ,
14+ )
815from google .cloud .spanner_v1 import param_types
916
1017from .exceptions import (
4754
4855
4956class Cursor :
57+ """
58+ Database cursor to manage the context of a fetch operation.
59+
60+ :type connection: :class:`spanner_dbapi.connection.Connection`
61+ :param connection: Parent connection object for this Cursor.
62+ """
63+
5064 def __init__ (self , connection ):
5165 self ._itr = None
5266 self ._res = None
5367 self ._row_count = _UNSET_COUNT
5468 self ._connection = connection
55- self ._closed = False
69+ self ._is_closed = False
5670
57- # arraysize is a readable and writable property mandated
58- # by PEP-0249 https://www.python.org/dev/peps/pep-0249/#arraysize
59- # It determines the results of .fetchmany
71+ # the number of rows to fetch at a time with fetchmany()
6072 self .arraysize = 1
6173
6274 def execute (self , sql , args = None ):
@@ -69,7 +81,7 @@ def execute(self, sql, args=None):
6981 Returns:
7082 None
7183 """
72- self ._raise_if_already_closed ()
84+ self ._raise_if_closed ()
7385
7486 if not self ._connection :
7587 raise ProgrammingError ("Cursor is not connected to the database" )
@@ -93,14 +105,11 @@ def execute(self, sql, args=None):
93105 self .__handle_insert (sql , args or None )
94106 else :
95107 self .__handle_update (sql , args or None )
96- except (
97- grpc_exceptions .AlreadyExists ,
98- grpc_exceptions .FailedPrecondition ,
99- ) as e :
108+ except (AlreadyExists , FailedPrecondition ) as e :
100109 raise IntegrityError (e .details if hasattr (e , "details" ) else e )
101- except grpc_exceptions . InvalidArgument as e :
110+ except InvalidArgument as e :
102111 raise ProgrammingError (e .details if hasattr (e , "details" ) else e )
103- except grpc_exceptions . InternalServerError as e :
112+ except InternalServerError as e :
104113 raise OperationalError (e .details if hasattr (e , "details" ) else e )
105114
106115 def __handle_update (self , sql , params ):
@@ -228,16 +237,35 @@ def description(self):
228237 def rowcount (self ):
229238 return self ._row_count
230239
231- def _raise_if_already_closed (self ):
240+ @property
241+ def is_closed (self ):
242+ """The cursor close indicator.
243+
244+ :rtype: :class:`bool`
245+ :returns: True if this cursor or it's parent connection is closed, False
246+ otherwise.
232247 """
233- Raise an exception if attempting to use an already closed connection.
248+ return self ._is_closed or self ._connection .is_closed
249+
250+ def _raise_if_closed (self ):
251+ """Raise an exception if this cursor is closed.
252+
253+ Helper to check this cursor's state before running a
254+ SQL/DDL/DML query. If the parent connection is
255+ already closed it also raises an error.
256+
257+ :raises: :class:`InterfaceError` if this cursor is closed.
234258 """
235- if self ._closed :
236- raise InterfaceError ("cursor already closed" )
259+ if self .is_closed :
260+ raise InterfaceError ("cursor is already closed" )
237261
238262 def close (self ):
263+ """Close this cursor.
264+
265+ The cursor will be unusable from this point forward.
266+ """
239267 self .__clear ()
240- self ._closed = True
268+ self ._is_closed = True
241269
242270 def executemany (self , operation , seq_of_params ):
243271 if not self ._connection :
@@ -257,15 +285,15 @@ def __iter__(self):
257285 return self ._itr
258286
259287 def fetchone (self ):
260- self ._raise_if_already_closed ()
288+ self ._raise_if_closed ()
261289
262290 try :
263291 return next (self )
264292 except StopIteration :
265293 return None
266294
267295 def fetchall (self ):
268- self ._raise_if_already_closed ()
296+ self ._raise_if_closed ()
269297
270298 return list (self .__iter__ ())
271299
@@ -282,7 +310,7 @@ def fetchmany(self, size=None):
282310 Error if the previous call to .execute*() did not produce any result set
283311 or if no call was issued yet.
284312 """
285- self ._raise_if_already_closed ()
313+ self ._raise_if_closed ()
286314
287315 if size is None :
288316 size = self .arraysize
0 commit comments