-
Notifications
You must be signed in to change notification settings - Fork 20
FIX: Closed Attribute in cursor __del__
#183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR improves cursor resource management and error handling in the mssql_python cursor module. The changes make cursor operations more robust by implementing idempotent close behavior and better cleanup procedures.
- Enhanced cursor close operation to be idempotent and use more specific exception types
- Fixed destructor to properly check the
closed
attribute instead of_closed
- Improved logging during cleanup by reducing error noise to debug level
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
mssql_python/cursor.py
Outdated
|
||
Raises: | ||
Error: If any operation is attempted with the cursor after it is closed. | ||
""" | ||
if self.closed: | ||
raise Exception("Cursor is already closed.") | ||
raise ProgrammingError("Cursor is already closed.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The close() method documentation states it should be idempotent ('subsequent calls have no effect'), but the implementation still raises an exception when called on an already closed cursor. Consider removing this exception to make the method truly idempotent as documented.
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "idempotency" in this context means that calling close()
multiple times won't free resources twice or cause corruption - the first call frees resources and subsequent calls raise a predictable exception. This is actually the standard behavior in pyodbc as well. Added a comment here specifying the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deleted unwanted whl file which was committed earlier
mssql_python/msvcp140.dll
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleaned this up as well, this is already present inside vcredist
""" | ||
if "_closed" not in self.__dict__ or not self._closed: | ||
if "closed" not in self.__dict__ or not self.closed: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we define self.closed
in the constructor, so you can simply check if not
self.closed:`?
This may avoid reliance on dict, and guards against subtle bugs if the attribute is missing.
Let me know if I am missing anything wrt to dict if its used anywhere else.
@@ -457,7 +461,7 @@ def _check_closed(self): | |||
Error: If the cursor is closed. | |||
""" | |||
if self.closed: | |||
raise Exception("Operation cannot be performed: the cursor is closed.") | |||
raise ProgrammingError("Cursor is already closed.", "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes still keeps cursor.close() non‑idempotent while the comments claims it is idempotent.
close() now calls self._check_closed() at the start.
_check_closed() raises ProgrammingError when self.closed is True.
Therefore a second close() raises instead of being a no‑op. That is NOT idempotent.
First, the issue raised in 183 requires no-op\no error during second close of the cursor. Secondly, DB-API 2.0 convention defines calling .close() multiple times must be safe (no exception).
We can keep _check_closed() raising ProgrammingError only for operational methods (execute/fetch) after closure - NOT for close() itself.
Fix the tests to make sure double closing of cursor is silent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a couple of comments.
Work Item / Issue Reference
Summary
This pull request makes improvements to the cursor resource management and error handling in the
mssql_python/cursor.py
module. The main changes include making the cursor close operation idempotent, refining exception types, and improving logging during cleanup. These updates help ensure more robust and predictable behavior when working with cursors.Error handling improvements:
Exception
to a more specificProgrammingError
, providing clearer feedback to users.ProgrammingError
instead ofInterfaceError
, aligning with the new error handling approach.Resource cleanup and logging:
__del__
) to check for the correctclosed
attribute and made sure no exception is raised if the cursor is already closed, enhancing reliability during garbage collection.