With python-oracledb what does 'DPY-4027: no configuration directory to search for tnsnames.ora' mean

Question:

  1. With the python-oracledb driver the code:

    import oracledb
    
    cs = "MYDB"
    c = oracledb.connect(user='cj', password=mypw, dsn=cs)
    

    gives the error:

    oracledb.exceptions.DatabaseError: DPY-4027: no configuration directory to search for tnsnames.ora
    
  2. The same error also occurs in a second case:

    import oracledb
    
    cs = "MYDB = (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orclpdb1)))"
    c = oracledb.connect(user='cj', password=mypw, dsn=cs)
    

    and with this:

    import oracledb
    
    cs = "MYDB = (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orclpdb1)))"
    
    cp = oracledb.ConnectParams()
    cp.parse_connect_string(cs)
    

What does this mean?

Asked By: Christopher Jones

||

Answers:

This error means you used a connection string that python-oracledb took to be some kind of alias it needed to look up in a tnsnames.ora file, but it didn’t know where to find that file.

Database connection strings in python-oracledb can be one of:

  • An Oracle Easy Connect string like myhost:1521/orclpdb1
  • An Oracle Net Connect Descriptor string like (DESCRIPTION=(ADDRESS=(...))
  • A Net Service Name alias mapping to a connect descriptor. These connect descriptors are commonly stored in a ‘tnsnames.ora’ file on the machine where you are running Python. They may also be accessed from an LDAP server.

See the user documentation on connection strings

If the connection string is an alias, or is not recognized as an Easy Connect string or Connect Descriptor, then you must have a tnsnames.ora configuration file that maps the alias to a connect descriptor. The tnsnames.ora file might look like:

MYDB = (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orclpdb1)))

Case 1:

import oracledb

cs = "MYDB"
c = oracledb.connect(user='cj', password=mypw, dsn=cs)

To use this connection string, you need to tell python-oracledb where to find the tnsnames.ora file that contains the mapping from the alias MYDB to the connect descriptor that really tells Oracle where the DB is located. (See this answer for why).

If you have a file /opt/myconfigdir/tnsnames.ora, then in python-oracledb’s default ‘Thin’ mode you can do this:

import oracledb

cs = "MYDB"
c = oracledb.connect(user='cj', password=mypw, dsn=cs, config_dir='/opt/myconfigdir')

Note that even if ORACLE_HOME is set, Thin mode will not automatically read $ORACLE_HOME/network/admin/tnsnames.ora. You must explicitly tell python-oracledb (in Thin mode) where to read the file from.

In the Thick mode (which is the mode when the app calls init_oracle_client()), if the tnsnames.ora file is not put in a default location, then you can tell python-oracledb where to find it like:

import oracledb

oracledb.init_oracle_client(config_dir='/opt/myconfigdir')

cs = "MYDB"
c = oracledb.connect(user='cj', password=mypw, dsn=cs)

In both modes you can alternatively set the environment variable TNS_ADMIN to the directory containing the file, and then run Python. See the configuration file link above for more information.

Case 2:

import oracledb

cs = "MYDB = (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orclpdb1)))"
c = oracledb.connect(user='cj', password=mypw, dsn=cs)

This is a pure "typo". What was passed as the connection string is a string containing both a net service name alias and the connect descriptor, which is the syntax used in tnsnames.ora configuration files, not in applications themselves.

Python-oracledb didn’t understand this syntax and assumed you were trying to pass a net service name alias. It needed to look this up in a tnsnames.ora file, but failed to locate such a file.

A solution is to pass only the connect descriptor component without the MYDB = part. For example like:

import oracledb

cs = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orclpdb1)))"
c = oracledb.connect(user='cj', password=mypw, dsn=cs)

Or you can put the original, whole string in a tnsnames.ora file and then call:

import oracledb

cs = "MYDB"
c = oracledb.connect(user='cj', password=mypw, dsn=cs)

See the example above for where to locate the file.

Another alternative is to use the Easy Connect syntax:

import oracledb

cs = "localhost:1521/orclpdb1"
c = oracledb.connect(user='cj', password=mypw, dsn=cs)
Answered By: Christopher Jones