Unhelful error message when accessing connection attributes in subclass constructor
- What versions are you using?
database: 21 XE platform.platform: macOS-14.6.1-arm64-arm-64bit sys.maxsize > 2**32: True platform.python_version: 3.12.4 oracledb.version: 2.4.1
- Is it an error or a hang or a crash?
An exception happens when I access certain attributes in the constructor of a Connection subclass before calling the base class' __init__.
- What error(s) or behavior you are seeing?
The following Python code:
import oracledb
class C(oracledb.Connection):
def __init__(self, *args, **kwargs):
self.outputtypehandler = None
super().__init__(self, *args, **kwargs)
db = C("user/pwd@db")
results in the following output:
Traceback (most recent call last):
File "/Users/walter/checkouts/LivingLogic.Python.xist/oracledb_issue.py", line 9, in <module>
db = C("user/pwd@db")
^^^^^^^^^^^^^^^^
File "/Users/walter/checkouts/LivingLogic.Python.xist/oracledb_issue.py", line 5, in __init__
self.outputtypehandler = None
^^^^^^^^^^^^^^^^^^^^^^
File "/Users/walter/pyvenvs/default/lib/python3.12/site-packages/oracledb/connection.py", line 376, in outputtypehandler
self._verify_connected()
File "/Users/walter/pyvenvs/default/lib/python3.12/site-packages/oracledb/connection.py", line 85, in _verify_connected
if self._impl is None:
^^^^^^^^^^
AttributeError: 'C' object has no attribute '_impl'
Exception ignored in: <function Connection.__del__ at 0x106208cc0>
Traceback (most recent call last):
File "/Users/walter/pyvenvs/default/lib/python3.12/site-packages/oracledb/connection.py", line 568, in __del__
if self._impl is not None:
^^^^^^^^^^
AttributeError: 'C' object has no attribute '_impl'
I realize that this is probably a borderline use and a fix for the code is simple: Move setting the outputtypehandler attribute after the call to the base class' __init__. However the stacktrace (and the fact, that a second exception happens in __del__) suggests that oracledb's code seems to be unprepared for such cases, so it might be good to look into that problem nonetheless.
- Does your application call init_oracle_client()?
No.
- Include a runnable Python script that shows the problem.
See above.
I've pushed a patch that corrects this issue. As you noted it is a minor problem but correcting it was also quite straightforward so thanks for bringing it to my attention! If you are able to build from source you can verify that it works for you, too.
I tried compiling the current oracledb using the instructions at https://python-oracledb.readthedocs.io/en/latest/user_guide/installation.html#install-using-github, but got the following:
🐚 ~/checkouts ❯ git clone --recurse-submodules https://github.com/oracle/python-oracledb.git
Klone nach 'python-oracledb'...
remote: Enumerating objects: 8635, done.
remote: Counting objects: 100% (2703/2703), done.
remote: Compressing objects: 100% (700/700), done.
remote: Total 8635 (delta 2087), reused 2474 (delta 1879), pack-reused 5932 (from 1)
Empfange Objekte: 100% (8635/8635), 5.50 MiB | 20.05 MiB/s, fertig.
Löse Unterschiede auf: 100% (6696/6696), fertig.
Submodul 'src/oracledb/impl/thick/odpi' (https://github.com/oracle/odpi.git) für Pfad 'src/oracledb/impl/thick/odpi' in die Konfiguration eingetragen.
Klone nach '/Users/walter/checkouts/python-oracledb/src/oracledb/impl/thick/odpi'...
remote: Enumerating objects: 12125, done.
remote: Counting objects: 100% (3320/3320), done.
remote: Compressing objects: 100% (696/696), done.
remote: Total 12125 (delta 2628), reused 3177 (delta 2547), pack-reused 8805 (from 1)
Empfange Objekte: 100% (12125/12125), 4.93 MiB | 8.96 MiB/s, fertig.
Löse Unterschiede auf: 100% (10017/10017), fertig.
Submodul-Pfad 'src/oracledb/impl/thick/odpi': '582366f637203256f3a56faf894a4dc8c9e4cc77' ausgecheckt
🐚 ~/checkouts ❯ cd python-oracledb
🐚 ~/checkouts/python-oracledb ❯ python setup.py build
Traceback (most recent call last):
File "/Users/walter/checkouts/python-oracledb/setup.py", line 29, in <module>
from setuptools import setup, Extension
ModuleNotFoundError: No module named 'setuptools'
python -V says Python 3.12.4.
After using pip install setuptools, python setup.py build followed by python setup.py install worked, and running my test script finishes without any exceptions.
Most installations have setuptools already present, which probably explains why we didn't notice that. I can add another line in the instructions to ensure that setuptools is present when using that approach.
I found another situation, where a check if self._impl is not None fails (this time in the ConnectionPool class).
When you inherit from ConnectionPool and get the constructor signature wrong like this:
from oracledb import *
class ConnectionPool2(ConnectionPool):
def __init__(self, dsn, params=None, **kwargs):
super().__init__(dsn, params, **kwargs)
print(create_pool("...", pool_class=ConnectionPool2))
the following happens:
🐚 ~/x/python-oracledb ❯ python ~/x/oracle_db_inheritance.py
Traceback (most recent call last):
File "/Users/walter/x/oracledb_inheritance.py", line 9, in <module>
print(create_pool("...", pool_class=ConnectionPool2))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/walter/pyvenvs/langchain/lib/python3.12/site-packages/oracledb/pool.py", line 574, in create_pool
return pool_class(dsn, params=params, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/walter/x/oracledb_inheritance.py", line 5, in __init__
super().__init__(dsn, params, **kwargs)
TypeError: BaseConnectionPool.__init__() takes from 1 to 2 positional arguments but 3 were given
Exception ignored in: <function ConnectionPool.__del__ at 0x1042eab60>
Traceback (most recent call last):
File "/Users/walter/pyvenvs/langchain/lib/python3.12/site-packages/oracledb/pool.py", line 349, in __del__
if self._impl is not None:
^^^^^^^^^^
AttributeError: 'ConnectionPool2' object has no attribute '_impl'
The version of oracledb I was using for this test was build from the current source, so this seems be be a new (but somewhat related?) problem.
It was the same problem and had the same solution. I've pushed that patch as well.
This was included in python-oracledb 2.5.0 which was just released.